From c8555234a084d06b2bc829bd99bf3a54fac983a1 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:41:22 +0530 Subject: [PATCH 01/23] feat(rust): add the Rust port workspace preview Track the nine-crate Rust workspace (rover-core, rover-tasks, rover-store, rover-access, rover-agents, rover-source, rover-execution, rover-tui and rover-cli) with its lockfile, the pinned 1.88.0 MSRV toolchain file and the .cargo build config. The workspace forbids unsafe code and warns on clippy pedantic lints. Build output lives under the ignored /target/ directory. The Go binary remains the Rover product; this workspace is an unreleased port that is gated by docs/design/RUST_PARITY_PLAN.md. Co-Authored-By: Claude Opus 5.5 --- .cargo/config.toml | 2 + .gitignore | 1 + Cargo.lock | 1641 +++ Cargo.toml | 25 + crates/rover-access/Cargo.toml | 21 + crates/rover-access/src/lib.rs | 426 + crates/rover-agents/Cargo.toml | 20 + crates/rover-agents/src/detect.rs | 653 ++ crates/rover-agents/src/herdr_manifest.rs | 1187 +++ .../rover-agents/src/herdr_manifests/amp.toml | 65 + .../src/herdr_manifests/antigravity.toml | 33 + .../src/herdr_manifests/claude.toml | 230 + .../src/herdr_manifests/cline.toml | 63 + .../src/herdr_manifests/codex.toml | 94 + .../src/herdr_manifests/cursor.toml | 57 + .../src/herdr_manifests/devin.toml | 86 + .../src/herdr_manifests/droid.toml | 48 + .../src/herdr_manifests/gemini.toml | 25 + .../src/herdr_manifests/github-copilot.toml | 45 + .../src/herdr_manifests/grok.toml | 179 + .../src/herdr_manifests/hermes.toml | 102 + .../src/herdr_manifests/kilo.toml | 24 + .../src/herdr_manifests/kimi.toml | 77 + .../src/herdr_manifests/kiro.toml | 97 + .../src/herdr_manifests/letta.toml | 95 + .../src/herdr_manifests/maki.toml | 68 + .../src/herdr_manifests/muse.toml | 113 + .../src/herdr_manifests/opencode.toml | 37 + .../rover-agents/src/herdr_manifests/pi.toml | 21 + .../src/herdr_manifests/qodercli.toml | 38 + .../src/herdr_manifests/qwen.toml | 119 + crates/rover-agents/src/lib.rs | 422 + crates/rover-agents/src/lifecycle.rs | 269 + crates/rover-agents/src/process_identity.rs | 636 ++ crates/rover-agents/src/rollup.rs | 332 + crates/rover-agents/src/sessions.rs | 695 ++ crates/rover-agents/src/transcript.rs | 389 + crates/rover-cli/Cargo.toml | 26 + crates/rover-cli/src/main.rs | 2711 +++++ crates/rover-cli/tests/session_daemon.rs | 96 + crates/rover-core/Cargo.toml | 16 + crates/rover-core/src/lib.rs | 516 + crates/rover-core/src/repository.rs | 234 + crates/rover-core/src/task.rs | 400 + crates/rover-execution/Cargo.toml | 26 + crates/rover-execution/src/layout.rs | 1121 ++ crates/rover-execution/src/lib.rs | 1056 ++ crates/rover-execution/src/pty.rs | 834 ++ crates/rover-execution/src/sessions.rs | 1239 +++ crates/rover-execution/src/supervisor.rs | 1507 +++ crates/rover-execution/src/terminal.rs | 537 + crates/rover-source/Cargo.toml | 17 + crates/rover-source/src/file_browser.rs | 593 ++ crates/rover-source/src/lib.rs | 2953 ++++++ crates/rover-store/Cargo.toml | 21 + crates/rover-store/src/budget.rs | 460 + crates/rover-store/src/files.rs | 1739 ++++ crates/rover-store/src/legacy.rs | 802 ++ crates/rover-store/src/legacy/import.rs | 1348 +++ crates/rover-store/src/lib.rs | 1891 ++++ crates/rover-store/src/resources.rs | 409 + crates/rover-tasks/Cargo.toml | 18 + crates/rover-tasks/src/lib.rs | 834 ++ crates/rover-tui/Cargo.toml | 18 + crates/rover-tui/src/lib.rs | 9168 +++++++++++++++++ rust-toolchain.toml | 4 + 66 files changed, 39029 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 crates/rover-access/Cargo.toml create mode 100644 crates/rover-access/src/lib.rs create mode 100644 crates/rover-agents/Cargo.toml create mode 100644 crates/rover-agents/src/detect.rs create mode 100644 crates/rover-agents/src/herdr_manifest.rs create mode 100644 crates/rover-agents/src/herdr_manifests/amp.toml create mode 100644 crates/rover-agents/src/herdr_manifests/antigravity.toml create mode 100644 crates/rover-agents/src/herdr_manifests/claude.toml create mode 100644 crates/rover-agents/src/herdr_manifests/cline.toml create mode 100644 crates/rover-agents/src/herdr_manifests/codex.toml create mode 100644 crates/rover-agents/src/herdr_manifests/cursor.toml create mode 100644 crates/rover-agents/src/herdr_manifests/devin.toml create mode 100644 crates/rover-agents/src/herdr_manifests/droid.toml create mode 100644 crates/rover-agents/src/herdr_manifests/gemini.toml create mode 100644 crates/rover-agents/src/herdr_manifests/github-copilot.toml create mode 100644 crates/rover-agents/src/herdr_manifests/grok.toml create mode 100644 crates/rover-agents/src/herdr_manifests/hermes.toml create mode 100644 crates/rover-agents/src/herdr_manifests/kilo.toml create mode 100644 crates/rover-agents/src/herdr_manifests/kimi.toml create mode 100644 crates/rover-agents/src/herdr_manifests/kiro.toml create mode 100644 crates/rover-agents/src/herdr_manifests/letta.toml create mode 100644 crates/rover-agents/src/herdr_manifests/maki.toml create mode 100644 crates/rover-agents/src/herdr_manifests/muse.toml create mode 100644 crates/rover-agents/src/herdr_manifests/opencode.toml create mode 100644 crates/rover-agents/src/herdr_manifests/pi.toml create mode 100644 crates/rover-agents/src/herdr_manifests/qodercli.toml create mode 100644 crates/rover-agents/src/herdr_manifests/qwen.toml create mode 100644 crates/rover-agents/src/lib.rs create mode 100644 crates/rover-agents/src/lifecycle.rs create mode 100644 crates/rover-agents/src/process_identity.rs create mode 100644 crates/rover-agents/src/rollup.rs create mode 100644 crates/rover-agents/src/sessions.rs create mode 100644 crates/rover-agents/src/transcript.rs create mode 100644 crates/rover-cli/Cargo.toml create mode 100644 crates/rover-cli/src/main.rs create mode 100644 crates/rover-cli/tests/session_daemon.rs create mode 100644 crates/rover-core/Cargo.toml create mode 100644 crates/rover-core/src/lib.rs create mode 100644 crates/rover-core/src/repository.rs create mode 100644 crates/rover-core/src/task.rs create mode 100644 crates/rover-execution/Cargo.toml create mode 100644 crates/rover-execution/src/layout.rs create mode 100644 crates/rover-execution/src/lib.rs create mode 100644 crates/rover-execution/src/pty.rs create mode 100644 crates/rover-execution/src/sessions.rs create mode 100644 crates/rover-execution/src/supervisor.rs create mode 100644 crates/rover-execution/src/terminal.rs create mode 100644 crates/rover-source/Cargo.toml create mode 100644 crates/rover-source/src/file_browser.rs create mode 100644 crates/rover-source/src/lib.rs create mode 100644 crates/rover-store/Cargo.toml create mode 100644 crates/rover-store/src/budget.rs create mode 100644 crates/rover-store/src/files.rs create mode 100644 crates/rover-store/src/legacy.rs create mode 100644 crates/rover-store/src/legacy/import.rs create mode 100644 crates/rover-store/src/lib.rs create mode 100644 crates/rover-store/src/resources.rs create mode 100644 crates/rover-tasks/Cargo.toml create mode 100644 crates/rover-tasks/src/lib.rs create mode 100644 crates/rover-tui/Cargo.toml create mode 100644 crates/rover-tui/src/lib.rs create mode 100644 rust-toolchain.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..eaa0194 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target-dir = "target/rust-1.88.0" diff --git a/.gitignore b/.gitignore index 33f5be1..f636383 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Binaries & build /bin/ +/target/ coverage.out coverage.html *.test diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..8e9ea49 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1641 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + +[[package]] +name = "alacritty_terminal" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda177466b9524d59f1b12f0dd30b68696788e9992a7e959021c4a0ed96fcf59" +dependencies = [ + "base64", + "bitflags 2.13.2", + "home", + "libc", + "log", + "miow", + "parking_lot", + "piper", + "polling", + "regex-automata", + "rustix", + "rustix-openpty", + "signal-hook 0.4.4", + "unicode-width", + "vte", + "windows-sys 0.59.0", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ar_archive_writer" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73cd58deff2140a0a8eae87e417bd01db68a33e148aa93d1e8cd837e55e312b6" +dependencies = [ + "object", +] + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" + +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "by_address" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cc" +version = "1.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54413ede23c2daf518f35156dfde027feb2374004d63bd497f983c8db9c0e313" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "compact_str" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dfdd1c2274d9aa354115b09dc9a901d6c5576818cdf70d14cae2bdb47df00ab" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" + +[[package]] +name = "crossterm" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +dependencies = [ + "bitflags 2.13.2", + "crossterm_winapi", + "derive_more", + "document-features", + "mio", + "parking_lot", + "rustix", + "signal-hook 0.3.18", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "cursor-icon" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" + +[[package]] +name = "darling" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 3.0.6", +] + +[[package]] +name = "darling_macro" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" +dependencies = [ + "darling_core", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.119", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "either" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fast-srgb8" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "filedescriptor" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d" +dependencies = [ + "libc", + "thiserror 1.0.69", + "winapi", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25905e51abafe4dcea6c15fec58c57b601cdbd0ee53d22ea1d3016c587d39b" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "futures-io" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284" + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "hybrid-array" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" +dependencies = [ + "typenum", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "instability" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf84e73fa6f27f299dec58e13223cf70db80da872eb921d4f6138342a0eabc8" +dependencies = [ + "darling", + "indoc", + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "kasuari" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde5057d6143cc94e861d90f591b9303d6716c6b9602309150bd068853c10899" +dependencies = [ + "hashbrown 0.16.1", + "portable-atomic", + "thiserror 2.0.20", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libsqlite3-sys" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1d20bef17f513b9b3004532233187769cd072d790971f4e4da0e346eb6401e8" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "line-clipping" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8" +dependencies = [ + "bitflags 2.13.2", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "lru" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a860605968fce16869fd239cf4237a82f3ac470723415db603b0e8b6c8d4fb9" +dependencies = [ + "hashbrown 0.17.1", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "mio" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "miow" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags 2.13.2", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "object" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b" +dependencies = [ + "memchr", +] + +[[package]] +name = "palette" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" +dependencies = [ + "approx", + "fast-srgb8", + "libm", + "palette_derive", +] + +[[package]] +name = "palette_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" +dependencies = [ + "by_address", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-pty" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a596a2b3d2752d94f51fac2d4a96737b8705dddd311a32b9af47211f08671e" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "downcast-rs", + "filedescriptor", + "lazy_static", + "libc", + "log", + "nix", + "serial2", + "shared_library", + "shell-words", + "winapi", + "winreg", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd034599e63b970727f70d79e02d62390a4a84f7c6b827c27c46d5ac3fa622" +dependencies = [ + "ar_archive_writer", + "cc", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "ratatui" +version = "0.30.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3274ba0a2c5e1bcad2a2005d20f4dc59dad26b2eb0940fb094500dba4099d57d" +dependencies = [ + "instability", + "ratatui-core", + "ratatui-crossterm", + "ratatui-widgets", + "serde", +] + +[[package]] +name = "ratatui-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbb175c433c8e28a809d1f5773a2ae96e68c0ce40db865cbab1020bf33ae479c" +dependencies = [ + "bitflags 2.13.2", + "compact_str", + "hashbrown 0.17.1", + "itertools", + "kasuari", + "lru", + "palette", + "serde", + "strum", + "thiserror 2.0.20", + "unicode-segmentation", + "unicode-truncate", + "unicode-width", +] + +[[package]] +name = "ratatui-crossterm" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567584a3b0e6a8203c23de40b4861497266725eb5363dbfd18a1edd603cca9f0" +dependencies = [ + "cfg-if", + "crossterm", + "instability", + "ratatui-core", +] + +[[package]] +name = "ratatui-widgets" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66e3d19bcc9130ca376277d93b60767ff121ace3be06f5f95f81dd68956407d1" +dependencies = [ + "bitflags 2.13.2", + "hashbrown 0.17.1", + "indoc", + "instability", + "itertools", + "line-clipping", + "ratatui-core", + "serde", + "strum", + "time", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.13.2", +] + +[[package]] +name = "regex" +version = "1.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "rover-access" +version = "0.1.0" +dependencies = [ + "getrandom", + "rover-core", + "rover-store", + "rusqlite", + "serde", + "serde_json", + "time", +] + +[[package]] +name = "rover-agents" +version = "0.1.0" +dependencies = [ + "regex", + "rover-core", + "rover-store", + "rusqlite", + "serde", + "serde_json", + "toml", +] + +[[package]] +name = "rover-cli" +version = "0.1.0" +dependencies = [ + "rover-agents", + "rover-core", + "rover-execution", + "rover-source", + "rover-store", + "rover-tasks", + "rover-tui", + "rusqlite", + "serde_json", +] + +[[package]] +name = "rover-core" +version = "0.1.0" +dependencies = [ + "getrandom", + "serde", + "sha2", + "time", +] + +[[package]] +name = "rover-execution" +version = "0.1.0" +dependencies = [ + "alacritty_terminal", + "base64", + "portable-pty", + "rover-core", + "rover-store", + "rusqlite", + "rustix", + "serde", + "serde_json", + "time", +] + +[[package]] +name = "rover-source" +version = "0.1.0" +dependencies = [ + "rover-core", + "rover-store", + "serde", + "serde_json", +] + +[[package]] +name = "rover-store" +version = "0.1.0" +dependencies = [ + "rover-core", + "rusqlite", + "rustix", + "serde", + "serde_json", + "serde_stacker", + "sha2", + "stacker", +] + +[[package]] +name = "rover-tasks" +version = "0.1.0" +dependencies = [ + "rover-core", + "rover-store", + "rusqlite", + "serde", + "serde_json", +] + +[[package]] +name = "rover-tui" +version = "0.1.0" +dependencies = [ + "crossterm", + "ratatui", + "rover-agents", + "rover-core", + "rover-execution", + "rover-source", + "rover-store", +] + +[[package]] +name = "rusqlite" +version = "0.40.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f2a97da3e3873c73cb2a2e71b35c40ff95e0b1eefa8d72d8499a6928c3b5b3" +dependencies = [ + "bitflags 2.13.2", + "fallible-iterator", + "fallible-streaming-iterator", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.13.2", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustix-openpty" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de16c7c59892b870a6336f185dc10943517f1327447096bbb7bb32cd85e2393" +dependencies = [ + "errno", + "libc", + "rustix", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_stacker" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4936375d50c4be7eff22293a9344f8e46f323ed2b3c243e52f89138d9bb0f4a" +dependencies = [ + "serde", + "serde_core", + "stacker", +] + +[[package]] +name = "serial2" +version = "0.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16809bc35793b19ce4e0c53924bc0dce3937f15487997cfdaed936004180730" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shared_library" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" +dependencies = [ + "lazy_static", + "libc", +] + +[[package]] +name = "shell-words" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77" + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a0c28ca5908dbdbcd52e6fdaa00358ab88637f8ab33e1f188dd510eb44b53d" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook 0.3.18", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "smallvec" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba467056f1b547ed52077911161fc86985becbc60e8e1857c8a144dab0def891" + +[[package]] +name = "stacker" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707f49d46706bacf8a2b00d51dace3f9de527c13eec3778f570c411f89e69967" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.61.2", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +dependencies = [ + "thiserror-impl 2.0.20", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "time" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +dependencies = [ + "deranged", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-truncate" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5" +dependencies = [ + "itertools", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "vte" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5924018406ce0063cd67f8e008104968b74b563ee1b85dde3ed1f7cb87d3dbd" +dependencies = [ + "arrayvec", + "bitflags 2.13.2", + "cursor-icon", + "log", + "memchr", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..bccf834 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,25 @@ +[workspace] +resolver = "2" +members = [ + "crates/rover-core", + "crates/rover-tasks", + "crates/rover-store", + "crates/rover-access", + "crates/rover-agents", + "crates/rover-source", + "crates/rover-execution", + "crates/rover-tui", + "crates/rover-cli", +] + +[workspace.package] +edition = "2021" +license = "MIT" +rust-version = "1.88" + +[workspace.lints.rust] +unsafe_code = "forbid" + +[workspace.lints.clippy] +all = "warn" +pedantic = "warn" diff --git a/crates/rover-access/Cargo.toml b/crates/rover-access/Cargo.toml new file mode 100644 index 0000000..786853b --- /dev/null +++ b/crates/rover-access/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "rover-access" +version = "0.1.0" +edition.workspace = true +license.workspace = true +rust-version.workspace = true +description = "Project-scoped local grant authorization for Rover" + +[dependencies] +rover-core = { path = "../rover-core" } +rover-store = { path = "../rover-store" } +getrandom = "=0.4.3" +serde = { version = "=1.0.229", default-features = false, features = ["derive", "std"] } +serde_json = { version = "=1.0.151", default-features = false, features = ["std"] } +time = { version = "=0.3.55", default-features = false, features = ["formatting", "parsing", "std"] } + +[dev-dependencies] +rusqlite = { version = "=0.40.2", default-features = false, features = ["bundled"] } + +[lints] +workspace = true diff --git a/crates/rover-access/src/lib.rs b/crates/rover-access/src/lib.rs new file mode 100644 index 0000000..f297f00 --- /dev/null +++ b/crates/rover-access/src/lib.rs @@ -0,0 +1,426 @@ +//! Locally issued bearer grants bound to an exact project and state root. + +use std::fmt; +use std::path::Path; +use std::time::Duration; + +use getrandom::fill; +use rover_core::{control_audience, digest, now, Id, RepositoryIdentity, SCHEMA}; +use rover_store::{Store, StoreError}; +use serde::{Deserialize, Serialize}; +use time::{format_description::well_known::Rfc3339, OffsetDateTime}; + +const TOKEN_PREFIX: &str = "rvr_"; +const MIN_TTL: Duration = Duration::from_secs(60); +const MAX_TTL: Duration = Duration::from_secs(30 * 24 * 60 * 60); +const MAX_TOOLS: usize = 32; +const MAX_NOTE_BYTES: usize = 4096; +const TOKEN_ENTROPY_BYTES: usize = 32; +const TOKEN_LEN: usize = 47; +const GRANT_KIND: &str = "grant"; + +const ALLOWED_TOOLS: &[&str] = &[ + "rover_agent_capabilities", + "rover_inspect", + "rover_status", + "rover_report", + "rover_diff", + "rover_context_search", + "rover_verify", + "rover_task_run", + "rover_task_cancel", +]; + +/// A persisted grant. The raw bearer token is never stored in the record. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct Grant { + /// Rover schema marker. + pub schema: String, + /// SHA-256 of the raw bearer token. + pub id: String, + /// Audience binds the state root and repository. + pub audience: String, + /// SHA-256 project identity for the canonical repository path. + pub project: String, + /// Tool names allowed by this grant. + pub tools: Vec, + /// Human supplied reason for issuance. + pub note: String, + /// RFC3339 creation time. + pub created_at: String, + /// RFC3339 expiry time. + pub expires_at: String, + /// Whether the grant was revoked. + pub revoked: bool, +} + +impl Grant { + /// Check whether this grant names a tool. + #[must_use] + pub fn allows(&self, tool: &str) -> bool { + self.tools.iter().any(|allowed| allowed == tool) + } +} + +/// Failure while issuing, authenticating, or revoking a local grant. +#[derive(Debug)] +pub enum GrantError { + /// Request parameters violate the local grant contract. + InvalidRequest, + /// Repository path could not be made absolute. + RepositoryPath(std::io::Error), + /// The operating system's cryptographic random source failed. + Random(getrandom::Error), + /// The grant store failed. + Store(StoreError), + /// The grant could not be represented with a valid expiration timestamp. + Timestamp, + /// Authentication failed. Deliberately does not disclose which check failed. + Denied, +} + +impl fmt::Display for GrantError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidRequest => formatter.write_str("invalid grant request"), + Self::RepositoryPath(error) => write!(formatter, "repository identity failed: {error}"), + Self::Random(error) => write!(formatter, "grant token generation failed: {error}"), + Self::Store(error) => write!(formatter, "grant store failed: {error}"), + Self::Timestamp => { + formatter.write_str("grant timestamp is outside the supported range") + } + Self::Denied => formatter.write_str("unauthorized or expired grant"), + } + } +} + +impl std::error::Error for GrantError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::RepositoryPath(error) => Some(error), + Self::Random(error) => Some(error), + Self::Store(error) => Some(error), + Self::InvalidRequest | Self::Timestamp | Self::Denied => None, + } + } +} + +/// Grant operations over one Rover record store. +pub struct GrantService<'store> { + store: &'store Store, +} + +impl<'store> GrantService<'store> { + /// Bind grant operations to a Rover state store. + #[must_use] + pub const fn new(store: &'store Store) -> Self { + Self { store } + } + + /// Issue and persist a project-scoped bearer grant. + /// + /// # Errors + /// + /// Returns [`GrantError::InvalidRequest`] for an empty/duplicate/unknown + /// tool list, invalid note, or TTL outside 1 minute through 30 days. + /// Returns a store, path, entropy, or timestamp error if issuance fails. + pub fn issue( + &self, + state_root: &Path, + repository: &str, + tools: &[&str], + note: &str, + ttl: Duration, + ) -> Result<(Grant, String), GrantError> { + if tools.is_empty() + || tools.len() > MAX_TOOLS + || !(MIN_TTL..=MAX_TTL).contains(&ttl) + || note.trim().is_empty() + || note.len() > MAX_NOTE_BYTES + { + return Err(GrantError::InvalidRequest); + } + let mut seen = Vec::with_capacity(tools.len()); + for tool in tools { + Id::parse(*tool).map_err(|_| GrantError::InvalidRequest)?; + if !ALLOWED_TOOLS.contains(tool) || seen.contains(tool) { + return Err(GrantError::InvalidRequest); + } + seen.push(tool); + } + + let identity = + RepositoryIdentity::resolve(repository).map_err(GrantError::RepositoryPath)?; + let mut entropy = [0_u8; TOKEN_ENTROPY_BYTES]; + fill(&mut entropy).map_err(GrantError::Random)?; + let token = format!("{TOKEN_PREFIX}{}", base64url_no_pad(&entropy)); + let token_digest = digest(token.as_bytes()); + let created_at = now().to_rfc3339().map_err(|_| GrantError::Timestamp)?; + let expiry_duration = time::Duration::try_from(ttl).map_err(|_| GrantError::Timestamp)?; + let expires_at = OffsetDateTime::now_utc() + .checked_add(expiry_duration) + .ok_or(GrantError::Timestamp)? + .format(&Rfc3339) + .map_err(|_| GrantError::Timestamp)?; + let grant = Grant { + schema: SCHEMA.to_owned(), + id: token_digest.clone(), + audience: control_audience(state_root, &identity), + project: identity.project_id().to_hex(), + tools: tools.iter().map(|tool| (*tool).to_owned()).collect(), + note: note.to_owned(), + created_at, + expires_at, + revoked: false, + }; + self.store + .put(GRANT_KIND, &grant.id, &grant, "grant.created") + .map_err(GrantError::Store)?; + Ok((grant, token)) + } + + /// Authenticate a bearer token against the exact state root and project. + /// + /// All malformed, missing, expired, revoked, and cross-scope tokens return + /// the same [`GrantError::Denied`] value. + /// + /// # Errors + /// + /// Returns [`GrantError::Denied`] for any invalid, missing, expired, + /// revoked, or differently scoped token. + pub fn authenticate( + &self, + state_root: &Path, + repository: &str, + token: &str, + ) -> Result { + if token.len() != TOKEN_LEN || !token.starts_with(TOKEN_PREFIX) { + return Err(GrantError::Denied); + } + let id = digest(token.as_bytes()); + let grant: Grant = self + .store + .get_typed(GRANT_KIND, &id) + .map_err(|_| GrantError::Denied)?; + let identity = RepositoryIdentity::resolve(repository).map_err(|_| GrantError::Denied)?; + let expiration = + OffsetDateTime::parse(&grant.expires_at, &Rfc3339).map_err(|_| GrantError::Denied)?; + if grant.revoked + || OffsetDateTime::now_utc() >= expiration + || grant.audience != control_audience(state_root, &identity) + || grant.project != identity.project_id().to_hex() + { + return Err(GrantError::Denied); + } + Ok(grant) + } + + /// Revoke a grant and append its event in one store transaction. + /// + /// # Errors + /// + /// Returns [`GrantError::InvalidRequest`] for an invalid record ID or + /// [`GrantError::Store`] if the grant is missing or the mutation fails. + pub fn revoke(&self, id: &str) -> Result<(), GrantError> { + Id::parse(id).map_err(|_| GrantError::InvalidRequest)?; + self.store + .mutate(GRANT_KIND, id, "grant.revoked", |value| { + let mut grant: Grant = + serde_json::from_value(value.clone()).map_err(StoreError::Json)?; + grant.revoked = true; + serde_json::to_value(grant).map_err(StoreError::Json) + }) + .map_err(GrantError::Store) + } +} + +fn base64url_no_pad(bytes: &[u8]) -> String { + const ALPHABET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + let mut encoded = String::with_capacity(bytes.len().div_ceil(3) * 4); + for chunk in bytes.chunks(3) { + let first = chunk[0]; + let second = chunk.get(1).copied().unwrap_or(0); + let third = chunk.get(2).copied().unwrap_or(0); + encoded.push(char::from(ALPHABET[usize::from(first >> 2)])); + encoded.push(char::from( + ALPHABET[usize::from(((first & 0x03) << 4) | (second >> 4))], + )); + if chunk.len() >= 2 { + encoded.push(char::from( + ALPHABET[usize::from(((second & 0x0f) << 2) | (third >> 6))], + )); + } + if chunk.len() == 3 { + encoded.push(char::from(ALPHABET[usize::from(third & 0x3f)])); + } + } + encoded +} + +#[cfg(test)] +mod tests { + use std::path::Path; + use std::time::Duration; + + use rover_store::Store; + use rusqlite::Connection; + use time::{format_description::well_known::Rfc3339, OffsetDateTime}; + + use super::{base64url_no_pad, Grant, GrantError, GrantService, MAX_TTL}; + + fn memory_store() -> Store { + Store::from_connection(Connection::open_in_memory().expect("open memory database")) + .expect("migrate memory database") + } + + #[test] + fn base64url_encoder_matches_rfc4648_unpadded_vectors() { + assert_eq!(base64url_no_pad(b"f"), "Zg"); + assert_eq!(base64url_no_pad(b"fo"), "Zm8"); + assert_eq!(base64url_no_pad(b"foo"), "Zm9v"); + assert_eq!(base64url_no_pad(b"foobar"), "Zm9vYmFy"); + } + + #[test] + fn grant_authentication_binds_state_and_project_then_revoke_denies() { + let store = memory_store(); + let service = GrantService::new(&store); + let (grant, token) = service + .issue( + Path::new("/state/one"), + "/project/a", + &["rover_status", "rover_diff"], + "owned client", + Duration::from_secs(60), + ) + .expect("issue grant"); + assert_eq!(token.len(), 47); + assert!(token.starts_with("rvr_")); + assert_eq!(grant.id, rover_core::digest(token.as_bytes())); + let authenticated = service + .authenticate(Path::new("/state/one"), "/project/a", &token) + .expect("authenticate grant"); + assert!(authenticated.allows("rover_status")); + assert!(!authenticated.allows("rover_verify")); + assert!(matches!( + service.authenticate(Path::new("/state/one"), "/project/b", &token), + Err(GrantError::Denied) + )); + assert!(matches!( + service.authenticate(Path::new("/state/two"), "/project/a", &token), + Err(GrantError::Denied) + )); + service.revoke(&grant.id).expect("revoke grant"); + assert!(matches!( + service.authenticate(Path::new("/state/one"), "/project/a", &token), + Err(GrantError::Denied) + )); + } + + #[test] + fn grant_request_validation_matches_tool_note_and_ttl_bounds() { + let store = memory_store(); + let service = GrantService::new(&store); + assert!(matches!( + service.issue( + Path::new("/state"), + "/repo", + &[], + "reason", + Duration::from_secs(60) + ), + Err(GrantError::InvalidRequest) + )); + for (tools, note, ttl) in [ + (vec!["not_a_tool"], "reason", Duration::from_secs(60)), + ( + vec!["rover_status", "rover_status"], + "reason", + Duration::from_secs(60), + ), + (vec!["rover_status"], " ", Duration::from_secs(60)), + (vec!["rover_status"], "reason", Duration::from_secs(59)), + ( + vec!["rover_status"], + "reason", + MAX_TTL + Duration::from_nanos(1), + ), + ] { + assert!(matches!( + service.issue(Path::new("/state"), "/repo", &tools, note, ttl), + Err(GrantError::InvalidRequest) + )); + } + assert!(service + .issue( + Path::new("/state"), + "/repo", + &["rover_status"], + &"n".repeat(4096), + MAX_TTL + ) + .is_ok()); + assert!(matches!( + service.issue( + Path::new("/state"), + "/repo", + &["rover_status"], + &"n".repeat(4097), + Duration::from_secs(60) + ), + Err(GrantError::InvalidRequest) + )); + } + + #[test] + fn malformed_expiry_and_token_are_denied_without_leaking_reason() { + let store = memory_store(); + let service = GrantService::new(&store); + let (mut grant, token) = service + .issue( + Path::new("/state"), + "/repo", + &["rover_status"], + "reason", + Duration::from_secs(60), + ) + .expect("issue grant"); + assert!(matches!( + service.authenticate(Path::new("/state"), "/repo", "wrong"), + Err(GrantError::Denied) + )); + grant.expires_at = (OffsetDateTime::now_utc() - time::Duration::seconds(1)) + .format(&Rfc3339) + .expect("format expired timestamp"); + store + .put("grant", &grant.id, &grant, "") + .expect("set expired timestamp"); + assert!(matches!( + service.authenticate(Path::new("/state"), "/repo", &token), + Err(GrantError::Denied) + )); + } + + #[test] + fn grant_record_round_trips_with_go_field_names() { + let store = memory_store(); + let service = GrantService::new(&store); + let (grant, _) = service + .issue( + Path::new("/state"), + "/repo", + &["rover_status"], + "reason", + Duration::from_secs(60), + ) + .expect("issue grant"); + let saved: Grant = store + .get_typed("grant", &grant.id) + .expect("read grant record"); + assert_eq!(saved, grant); + let json = serde_json::to_value(saved).expect("serialize grant"); + assert_eq!(json["created_at"], grant.created_at); + assert_eq!(json["expires_at"], grant.expires_at); + assert_eq!(json["revoked"], false); + } +} diff --git a/crates/rover-agents/Cargo.toml b/crates/rover-agents/Cargo.toml new file mode 100644 index 0000000..bf441ef --- /dev/null +++ b/crates/rover-agents/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "rover-agents" +version = "0.1.0" +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[dependencies] +rover-core = { path = "../rover-core" } +rover-store = { path = "../rover-store" } +serde = { version = "=1.0.229", default-features = false, features = ["std", "derive"] } +serde_json = { version = "=1.0.151", default-features = false, features = ["std"] } +regex = "=1.12.4" +toml = { version = "=0.8.23", default-features = false, features = ["parse"] } + +[dev-dependencies] +rusqlite = { version = "=0.40.2", default-features = false, features = ["bundled"] } + +[lints] +workspace = true diff --git a/crates/rover-agents/src/detect.rs b/crates/rover-agents/src/detect.rs new file mode 100644 index 0000000..187a104 --- /dev/null +++ b/crates/rover-agents/src/detect.rs @@ -0,0 +1,653 @@ +//! Evidence-based process and bottom-buffer agent detection. +//! +//! The caller supplies the foreground executable and the live bottom-buffer +//! screen snapshot. A scrolled viewport or historical text is not a valid +//! substitute for that snapshot. + +use std::collections::BTreeSet; +use std::io; + +use crate::profiles; + +const MAX_MANIFESTS: usize = 128; +const MAX_PROCESS_NAMES: usize = 32; +const MAX_RULES: usize = 256; +const MAX_SCREEN_BYTES: usize = 64 * 1024; +const MAX_MARKERS_PER_RULE: usize = 16; +const MAX_MARKER_BYTES: usize = 256; + +/// State derived from process and screen evidence. +#[derive( + Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize, +)] +#[serde(rename_all = "snake_case")] +pub enum AgentState { + /// A known foreground agent has not been classified from available input. + Unknown, + /// The screen did not match an active/blocked rule for a known agent. + Idle, + /// A screen rule indicates ongoing work. + Working, + /// A screen rule indicates a visible request for human input. + Blocked, +} + +/// Confidence for the state conclusion, separate from whether identity exists. +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum Confidence { + /// No state conclusion is supported. + None, + /// A documented fallback was used without a matching state rule. + Medium, + /// One unambiguous status conclusion has explicit evidence. + High, +} + +/// A group of literal markers that can classify a live screen snapshot. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ScreenRule { + /// Stable manifest-local rule identifier. + pub id: String, + /// State assigned when the rule matches. + pub state: AgentState, + /// Higher-priority rules take precedence; conflicting top-priority matches + /// produce unknown rather than an arbitrary state. + pub priority: u16, + /// Any one marker may match. Values are literal substrings, not regex. + pub any_of: Vec, +} + +/// Local, declarative process/screen manifest for an agent kind. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ScreenManifest { + /// Stable agent kind or user-defined agent label. + pub agent: String, + /// Manifest origin shown in explain evidence (for example `local`). + pub source: String, + /// Human-readable manifest version. + pub version: String, + /// Exact executable basenames that identify this agent. + pub process_names: Vec, + /// Rules for classifying the live bottom-buffer snapshot. + pub rules: Vec, +} + +impl ScreenManifest { + /// Validate a manifest before it participates in detection. + /// + /// # Errors + /// + /// Returns `InvalidInput` for empty, oversized, duplicate, or terminal- + /// control-bearing metadata and rules. + pub fn validate(&self) -> io::Result<()> { + if !valid_display_text(&self.agent, 128) + || !valid_display_text(&self.source, 256) + || !valid_display_text(&self.version, 64) + { + return Err(invalid_input("invalid agent detection manifest metadata")); + } + if self.process_names.is_empty() || self.process_names.len() > MAX_PROCESS_NAMES { + return Err(invalid_input("invalid agent detection process names")); + } + let mut process_names = BTreeSet::new(); + for name in &self.process_names { + if !valid_process_name(name) || !process_names.insert(normalize_process_name(name)) { + return Err(invalid_input("invalid or duplicate agent process name")); + } + } + if self.rules.len() > MAX_RULES { + return Err(invalid_input("agent detection manifest has too many rules")); + } + let mut rule_ids = BTreeSet::new(); + for rule in &self.rules { + if !valid_rule_id(&rule.id) || !rule_ids.insert(rule.id.as_str()) { + return Err(invalid_input( + "invalid or duplicate agent detection rule id", + )); + } + if rule.state == AgentState::Unknown + || rule.any_of.is_empty() + || rule.any_of.len() > MAX_MARKERS_PER_RULE + || rule.any_of.iter().any(|marker| { + !valid_display_text(marker, MAX_MARKER_BYTES) || marker.trim().is_empty() + }) + { + return Err(invalid_input("invalid agent detection rule")); + } + } + Ok(()) + } +} + +/// Foreground process evidence supplied by a platform adapter. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ProcessSample { + /// Executable path or basename for the current foreground process. + pub executable: String, +} + +/// Inputs for one detection evaluation. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct DetectionRequest { + /// Foreground process sample. Child-process guesses are not accepted here. + pub foreground: Option, + /// Current live bottom-buffer snapshot, not scrollback or a historical view. + pub screen: Option, + /// Validated local or bundled manifests available to this evaluation. + pub manifests: Vec, +} + +/// Explainable result of one detection evaluation. +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] +pub struct DetectionResult { + /// Recognized agent kind, if foreground-process evidence resolved one. + pub agent: Option, + /// Classified state, or `unknown` when evidence is insufficient/conflicted. + pub state: AgentState, + /// Confidence in the state conclusion (identity may still be known). + pub confidence: Confidence, + /// Stable reason code for diagnostics and tests. + pub reason: &'static str, + /// Bounded evidence details; raw screen contents are never copied here. + pub evidence: Vec, + /// Manifest metadata indicating visible idle prompt chrome. + pub visibility: DetectionVisibility, + /// A transcript/history viewer should not update the authoritative state. + pub skip_state_update: bool, +} + +/// Screen chrome flags from the matched manifest rule. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize)] +pub struct DetectionVisibility { + /// Manifest metadata indicating a visible idle prompt. + pub idle: bool, + /// Manifest metadata indicating a visible human-input blocker. + pub blocker: bool, + /// Manifest metadata indicating visible working UI chrome. + pub working: bool, +} + +/// One safe-to-display detection observation. +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] +pub struct DetectionEvidence { + /// Signal class (`foreground_process` or `screen_manifest`). + pub source: &'static str, + /// Manifest origin, when this observation came from a screen rule. + #[serde(skip_serializing_if = "Option::is_none")] + pub manifest_source: Option, + /// Manifest version, when this observation came from a screen rule. + #[serde(skip_serializing_if = "Option::is_none")] + pub manifest_version: Option, + /// Rule identifier, when this observation came from a screen rule. + #[serde(skip_serializing_if = "Option::is_none")] + pub rule_id: Option, + /// Safe process basename or literal rule marker, never the full screen. + pub value: String, +} + +/// Detect an agent using exact foreground executable matches and an optional +/// live screen manifest. A known process with no matching rule falls back to +/// idle only when a valid live screen snapshot and manifest were supplied. +#[must_use] +pub fn detect_agent(request: &DetectionRequest) -> DetectionResult { + let Some(foreground) = request.foreground.as_ref() else { + return result( + None, + AgentState::Unknown, + Confidence::None, + "foreground_process_unavailable", + Vec::new(), + ); + }; + let executable = basename(&foreground.executable); + if !valid_process_name(executable) { + return result( + None, + AgentState::Unknown, + Confidence::None, + "invalid_foreground_process", + Vec::new(), + ); + } + let process_evidence = || DetectionEvidence { + source: "foreground_process", + manifest_source: None, + manifest_version: None, + rule_id: None, + value: executable.to_owned(), + }; + let agent = match identify_foreground(request, executable) { + Ok(agent) => agent, + Err(reason) => { + return result( + None, + AgentState::Unknown, + Confidence::None, + reason, + vec![process_evidence()], + ); + } + }; + let evidence = vec![process_evidence()]; + let Some(screen) = request.screen.as_deref() else { + return result( + Some(agent), + AgentState::Unknown, + Confidence::None, + "live_screen_unavailable", + evidence, + ); + }; + if screen.len() > MAX_SCREEN_BYTES { + return result( + Some(agent), + AgentState::Unknown, + Confidence::None, + "live_screen_exceeds_limit", + evidence, + ); + } + if !valid_screen_text(screen) { + return result( + Some(agent), + AgentState::Unknown, + Confidence::None, + "live_screen_contains_unsafe_controls", + evidence, + ); + } + evaluate_screen(&agent, screen, &request.manifests, evidence) +} + +fn identify_foreground( + request: &DetectionRequest, + executable: &str, +) -> Result { + if request.manifests.len() > MAX_MANIFESTS { + return Err("too_many_agent_manifests"); + } + let normalized = normalize_process_name(executable); + let mut candidates = BTreeSet::::new(); + for descriptor in profiles() { + if descriptor.executable != "user-defined" + && normalize_process_name(descriptor.executable) == normalized + { + candidates.insert(descriptor.name.to_owned()); + } + } + for manifest in request + .manifests + .iter() + .filter(|manifest| manifest.validate().is_ok()) + { + if manifest + .process_names + .iter() + .any(|name| normalize_process_name(name) == normalized) + { + candidates.insert(manifest.agent.clone()); + } + } + match candidates.len() { + 0 => Err("foreground_process_unrecognized"), + 1 => Ok(candidates.into_iter().next().unwrap_or_default()), + _ => Err("foreground_process_ambiguous"), + } +} + +fn evaluate_screen( + agent: &str, + screen: &str, + manifests: &[ScreenManifest], + evidence: Vec, +) -> DetectionResult { + let matching: Vec<_> = manifests + .iter() + .filter(|manifest| manifest.agent == agent && manifest.validate().is_ok()) + .collect(); + let Some(manifest) = matching.first().copied() else { + return result( + Some(agent.to_owned()), + AgentState::Unknown, + Confidence::None, + "agent_manifest_unavailable", + evidence, + ); + }; + if matching.len() > 1 { + return result( + Some(agent.to_owned()), + AgentState::Unknown, + Confidence::None, + "agent_manifest_ambiguous", + evidence, + ); + } + classify_screen(agent, screen, manifest, evidence) +} + +fn classify_screen( + agent: &str, + screen: &str, + manifest: &ScreenManifest, + mut evidence: Vec, +) -> DetectionResult { + let screen_lower = screen.to_lowercase(); + let mut matches = Vec::new(); + for rule in &manifest.rules { + let matched_marker = rule + .any_of + .iter() + .find(|marker| screen_lower.contains(&marker.to_lowercase())); + if matched_marker.is_some() { + matches.push((rule.priority, rule.state, rule.id.as_str())); + } + } + let Some(priority) = matches.iter().map(|entry| entry.0).max() else { + return result( + Some(agent.to_owned()), + AgentState::Idle, + Confidence::Medium, + "known_agent_no_screen_rule_defaults_idle", + evidence, + ); + }; + matches.retain(|entry| entry.0 == priority); + matches.sort_by(|left, right| left.2.cmp(right.2)); + let states = matches.iter().map(|entry| entry.1).collect::>(); + for (_, _, rule_id) in &matches { + evidence.push(DetectionEvidence { + source: "screen_manifest", + manifest_source: Some(manifest.source.clone()), + manifest_version: Some(manifest.version.clone()), + rule_id: Some((*rule_id).to_owned()), + value: "literal marker matched".to_owned(), + }); + } + if states.len() != 1 { + return result( + Some(agent.to_owned()), + AgentState::Unknown, + Confidence::None, + "conflicting_screen_rules", + evidence, + ); + } + result( + Some(agent.to_owned()), + states.iter().next().copied().unwrap_or(AgentState::Unknown), + Confidence::High, + "screen_rule_matched", + evidence, + ) +} + +fn result( + agent: Option, + state: AgentState, + confidence: Confidence, + reason: &'static str, + evidence: Vec, +) -> DetectionResult { + DetectionResult { + agent, + state, + confidence, + reason, + evidence, + visibility: DetectionVisibility::default(), + skip_state_update: false, + } +} + +fn basename(path: &str) -> &str { + path.rsplit(['/', '\\']).next().unwrap_or_default() +} + +fn normalize_process_name(name: &str) -> String { + let name = name.to_ascii_lowercase(); + name.strip_suffix(".exe").unwrap_or(&name).to_owned() +} + +fn valid_process_name(name: &str) -> bool { + !name.is_empty() + && name.len() <= 128 + && name + .bytes() + .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'-' | b'.' | b'+')) +} + +fn valid_rule_id(value: &str) -> bool { + !value.is_empty() + && value.len() <= 64 + && value + .bytes() + .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'-' | b'.')) +} + +fn valid_display_text(value: &str, limit: usize) -> bool { + !value.is_empty() && value.len() <= limit && !value.chars().any(is_unsafe_text_character) +} + +fn valid_screen_text(value: &str) -> bool { + value.len() <= MAX_SCREEN_BYTES + && !value.chars().any(|character| { + character != '\n' && character != '\t' && is_unsafe_text_character(character) + }) +} + +fn is_unsafe_text_character(character: char) -> bool { + character.is_control() + || matches!( + character, + '\u{061c}' + | '\u{200e}' + | '\u{200f}' + | '\u{202a}'..='\u{202e}' + | '\u{2066}'..='\u{2069}' + | '\u{feff}' + ) +} + +fn invalid_input(message: &str) -> io::Error { + io::Error::new(io::ErrorKind::InvalidInput, message) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn manifest(agent: &str, process: &str, rules: Vec) -> ScreenManifest { + ScreenManifest { + agent: agent.to_owned(), + source: "local-test".to_owned(), + version: "1".to_owned(), + process_names: vec![process.to_owned()], + rules, + } + } + + fn rule(id: &str, state: AgentState, priority: u16, marker: &str) -> ScreenRule { + ScreenRule { + id: id.to_owned(), + state, + priority, + any_of: vec![marker.to_owned()], + } + } + + #[test] + fn exact_foreground_process_and_screen_rule_supply_explainable_evidence() { + let manifest = manifest( + "review-bot", + "review-bot", + vec![rule( + "approval", + AgentState::Blocked, + 10, + "Allow this action?", + )], + ); + let result = detect_agent(&DetectionRequest { + foreground: Some(ProcessSample { + executable: "/usr/local/bin/review-bot".to_owned(), + }), + screen: Some("Tests done.\nAllow this action?".to_owned()), + manifests: vec![manifest], + }); + assert_eq!(result.agent.as_deref(), Some("review-bot")); + assert_eq!(result.state, AgentState::Blocked); + assert_eq!(result.confidence, Confidence::High); + assert_eq!(result.reason, "screen_rule_matched"); + assert_eq!(result.evidence.len(), 2); + assert_eq!(result.evidence[1].rule_id.as_deref(), Some("approval")); + assert!(!serde_json::to_string(&result) + .unwrap() + .contains("Tests done")); + } + + #[test] + fn built_in_process_identity_never_implies_a_status() { + let result = detect_agent(&DetectionRequest { + foreground: Some(ProcessSample { + executable: "C:\\tools\\CODEX.EXE".to_owned(), + }), + screen: None, + manifests: Vec::new(), + }); + assert_eq!(result.agent.as_deref(), Some("codex-exec")); + assert_eq!(result.state, AgentState::Unknown); + assert_eq!(result.confidence, Confidence::None); + assert_eq!(result.reason, "live_screen_unavailable"); + } + + #[test] + fn idle_fallback_requires_valid_manifest_and_live_snapshot() { + let result = detect_agent(&DetectionRequest { + foreground: Some(ProcessSample { + executable: "claude".to_owned(), + }), + screen: Some("ordinary agent screen".to_owned()), + manifests: Vec::new(), + }); + assert_eq!(result.state, AgentState::Unknown); + assert_eq!(result.confidence, Confidence::None); + assert_eq!(result.reason, "agent_manifest_unavailable"); + + let manifest = manifest("claude-print", "claude", Vec::new()); + let result = detect_agent(&DetectionRequest { + foreground: Some(ProcessSample { + executable: "claude".to_owned(), + }), + screen: Some("ordinary agent screen".to_owned()), + manifests: vec![manifest], + }); + assert_eq!(result.state, AgentState::Idle); + assert_eq!(result.confidence, Confidence::Medium); + assert_eq!(result.reason, "known_agent_no_screen_rule_defaults_idle"); + } + + #[test] + fn process_names_are_exact_and_manifest_collisions_are_unknown() { + let unrecognized = detect_agent(&DetectionRequest { + foreground: Some(ProcessSample { + executable: "codex-wrapper".to_owned(), + }), + screen: Some("working".to_owned()), + manifests: Vec::new(), + }); + assert_eq!(unrecognized.agent, None); + assert_eq!(unrecognized.reason, "foreground_process_unrecognized"); + + let result = detect_agent(&DetectionRequest { + foreground: Some(ProcessSample { + executable: "worker".to_owned(), + }), + screen: Some("working".to_owned()), + manifests: vec![ + manifest("one", "worker", Vec::new()), + manifest("two", "worker", Vec::new()), + ], + }); + assert_eq!(result.agent, None); + assert_eq!(result.reason, "foreground_process_ambiguous"); + } + + #[test] + fn priority_resolves_rules_but_conflicting_ties_remain_unknown() { + let detect = |manifest| { + detect_agent(&DetectionRequest { + foreground: Some(ProcessSample { + executable: "worker".to_owned(), + }), + screen: Some("running; approve".to_owned()), + manifests: vec![manifest], + }) + }; + let higher_priority = manifest( + "worker", + "worker", + vec![ + rule("working", AgentState::Working, 1, "running"), + rule("blocked", AgentState::Blocked, 2, "approve"), + ], + ); + assert_eq!(detect(higher_priority).state, AgentState::Blocked); + let tied = manifest( + "worker", + "worker", + vec![ + rule("working", AgentState::Working, 2, "running"), + rule("blocked", AgentState::Blocked, 2, "approve"), + ], + ); + let result = detect(tied); + assert_eq!(result.state, AgentState::Unknown); + assert_eq!(result.confidence, Confidence::None); + assert_eq!(result.reason, "conflicting_screen_rules"); + } + + #[test] + fn invalid_manifests_and_hostile_screen_fail_closed() { + let mut invalid = manifest( + "worker", + "worker", + vec![rule("bad\u{202e}rule", AgentState::Blocked, 1, "approve")], + ); + assert!(invalid.validate().is_err()); + invalid.rules[0].id = "approval".to_owned(); + assert!(invalid.validate().is_ok()); + assert!(manifest( + "worker", + "worker", + vec![ + rule("same", AgentState::Idle, 1, "a"), + rule("same", AgentState::Working, 1, "b") + ] + ) + .validate() + .is_err()); + + let result = detect_agent(&DetectionRequest { + foreground: Some(ProcessSample { + executable: "worker".to_owned(), + }), + screen: Some("unsafe\u{202e}screen".to_owned()), + manifests: vec![invalid], + }); + assert_eq!(result.state, AgentState::Unknown); + assert_eq!(result.reason, "live_screen_contains_unsafe_controls"); + + let request = DetectionRequest { + foreground: Some(ProcessSample { + executable: "worker".to_owned(), + }), + screen: Some("idle".to_owned()), + manifests: (0..=MAX_MANIFESTS) + .map(|index| manifest(&format!("worker-{index}"), "worker", Vec::new())) + .collect(), + }; + assert_eq!(detect_agent(&request).reason, "too_many_agent_manifests"); + } +} diff --git a/crates/rover-agents/src/herdr_manifest.rs b/crates/rover-agents/src/herdr_manifest.rs new file mode 100644 index 0000000..282dda4 --- /dev/null +++ b/crates/rover-agents/src/herdr_manifest.rs @@ -0,0 +1,1187 @@ +//! Bounded evaluator for the audited Herdr screen-manifest format. +//! +//! This module ports the format and decision rules independently. The bundled +//! TOML data is tracked with per-file provenance in `UPSTREAM_SOURCE_AUDIT.md`. + +use std::io; +use std::sync::OnceLock; + +use regex::{Regex, RegexBuilder}; +use serde::Deserialize; + +use crate::detect::{ + AgentState, Confidence, DetectionEvidence, DetectionResult, DetectionVisibility, +}; + +const MAX_MANIFEST_BYTES: usize = 64 * 1024; +const MAX_RULES: usize = 128; +const MAX_GATE_DEPTH: usize = 8; +const MAX_TOTAL_GATES: usize = 512; +const MAX_MATCHERS_PER_GATE: usize = 32; +const MAX_TOTAL_MATCHERS: usize = 1024; +const MAX_MATCHER_CHARS: usize = 512; +const MAX_TOML_NESTING: usize = 32; +const MAX_REGEX_SIZE: usize = 1024 * 1024; +const ENGINE_VERSION: u32 = 3; + +const BUNDLED: &[(&str, &str)] = &[ + ("amp", include_str!("herdr_manifests/amp.toml")), + ("agy", include_str!("herdr_manifests/antigravity.toml")), + ("claude", include_str!("herdr_manifests/claude.toml")), + ("cline", include_str!("herdr_manifests/cline.toml")), + ("codex", include_str!("herdr_manifests/codex.toml")), + ("cursor", include_str!("herdr_manifests/cursor.toml")), + ("devin", include_str!("herdr_manifests/devin.toml")), + ("droid", include_str!("herdr_manifests/droid.toml")), + ("gemini", include_str!("herdr_manifests/gemini.toml")), + ( + "copilot", + include_str!("herdr_manifests/github-copilot.toml"), + ), + ("grok", include_str!("herdr_manifests/grok.toml")), + ("hermes", include_str!("herdr_manifests/hermes.toml")), + ("kilo", include_str!("herdr_manifests/kilo.toml")), + ("kimi", include_str!("herdr_manifests/kimi.toml")), + ("kiro", include_str!("herdr_manifests/kiro.toml")), + ("letta", include_str!("herdr_manifests/letta.toml")), + ("maki", include_str!("herdr_manifests/maki.toml")), + ("muse", include_str!("herdr_manifests/muse.toml")), + ("opencode", include_str!("herdr_manifests/opencode.toml")), + ("pi", include_str!("herdr_manifests/pi.toml")), + ("qodercli", include_str!("herdr_manifests/qodercli.toml")), + ("qwen", include_str!("herdr_manifests/qwen.toml")), +]; + +/// A validated, compiled Herdr-format detection manifest. +#[derive(Clone, Debug)] +pub struct HerdrManifest { + /// Canonical manifest identity. + pub id: String, + /// Upstream manifest version when present. + pub version: Option, + /// Declared aliases, retained for agent resolution. + pub aliases: Vec, + rules: Vec, +} + +#[derive(Clone, Debug)] +// These flags intentionally mirror the pinned upstream rule schema. +#[allow(clippy::struct_excessive_bools)] +struct CompiledRule { + id: String, + state: Option, + priority: i32, + region: String, + visible_idle: bool, + visible_blocker: bool, + visible_working: bool, + skip_state_update: bool, + gate: CompiledGate, +} + +#[derive(Clone, Debug)] +struct CompiledGate { + all: Vec, + any: Vec, + not: Vec, + contains: Vec, + regex: Vec, + line_regex: Vec, +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct ManifestDoc { + id: String, + #[serde(default)] + version: Option, + #[serde(default)] + min_engine_version: Option, + #[serde(default)] + updated_at: Option, + #[serde(default)] + aliases: Vec, + rules: Vec, +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +// These booleans are distinct, documented fields in the pinned TOML schema. +#[allow(clippy::struct_excessive_bools)] +struct RuleDoc { + id: String, + #[serde(default)] + state: Option, + #[serde(default)] + priority: i32, + #[serde(default = "default_region")] + region: String, + #[serde(default)] + visible_idle: bool, + #[serde(default)] + visible_blocker: bool, + #[serde(default)] + visible_working: bool, + #[serde(default)] + skip_state_update: bool, + #[serde(default)] + all: Vec, + #[serde(default)] + any: Vec, + #[serde(default, rename = "not")] + not: Vec, + #[serde(default)] + contains: Vec, + #[serde(default)] + regex: Vec, + #[serde(default)] + line_regex: Vec, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields)] +struct GateDoc { + #[serde(default)] + all: Vec, + #[serde(default)] + any: Vec, + #[serde(default, rename = "not")] + not: Vec, + #[serde(default)] + contains: Vec, + #[serde(default)] + regex: Vec, + #[serde(default)] + line_regex: Vec, +} + +#[derive(Default)] +struct Complexity { + gates: usize, + matchers: usize, +} + +#[derive(Clone, Copy, Debug, Default)] +struct RuleOutcomeFlags { + visibility: DetectionVisibility, + skip_state_update: bool, +} + +fn default_region() -> String { + "whole_recent".to_owned() +} + +/// Parse and compile one Herdr-format manifest under explicit input and +/// complexity limits. +/// +/// # Errors +/// +/// Returns `InvalidInput` for malformed TOML, unsupported engine versions, +/// unknown fields, invalid regions/regex, or exceeded bounds. +pub fn parse_herdr_manifest(source: &str) -> io::Result { + if source.len() > MAX_MANIFEST_BYTES || !toml_nesting_is_bounded(source) { + return Err(invalid_input("agent manifest exceeds parser bounds")); + } + let doc: ManifestDoc = toml::from_str(source) + .map_err(|error| invalid_input_owned(format!("invalid agent manifest TOML: {error}")))?; + validate_doc(&doc)?; + let mut rules = Vec::with_capacity(doc.rules.len()); + for rule in doc.rules { + let gate = GateDoc { + all: rule.all, + any: rule.any, + not: rule.not, + contains: rule.contains, + regex: rule.regex, + line_regex: rule.line_regex, + }; + let gate = compile_gate(&gate)?; + rules.push(CompiledRule { + id: rule.id, + state: rule.state, + priority: rule.priority, + region: rule.region, + visible_idle: rule.visible_idle, + visible_blocker: rule.visible_blocker, + visible_working: rule.visible_working, + skip_state_update: rule.skip_state_update, + gate, + }); + } + Ok(HerdrManifest { + id: doc.id, + version: doc.version, + aliases: doc.aliases, + rules, + }) +} + +/// Return all 22 immutable bundled manifests after validating the complete +/// set on first use. +/// +/// # Errors +/// +/// Returns an error if any compiled-in manifest violates the parser contract. +pub fn bundled_herdr_manifests() -> io::Result<&'static [HerdrManifest]> { + static MANIFESTS: OnceLock, String>> = OnceLock::new(); + match MANIFESTS.get_or_init(|| { + BUNDLED + .iter() + .map(|(_, source)| parse_herdr_manifest(source).map_err(|error| error.to_string())) + .collect() + }) { + Ok(manifests) => Ok(manifests), + Err(error) => Err(invalid_input_owned(format!( + "bundled agent manifest set is invalid: {error}" + ))), + } +} + +/// Resolve an audited direct interactive-agent executable to its bundled +/// screen manifest. Wrapper and child-process inference are not performed. +/// +/// # Errors +/// +/// Returns an error if the compiled-in manifest set fails validation. +pub fn bundled_manifest_for_executable( + executable: &str, +) -> io::Result> { + let basename = executable.rsplit(['/', '\\']).next().unwrap_or_default(); + if basename.is_empty() + || basename.len() > 128 + || basename + .chars() + .any(|character| character.is_control() || character.is_whitespace()) + { + return Ok(None); + } + let normalized = basename.to_ascii_lowercase(); + let normalized = normalized.strip_suffix(".exe").unwrap_or(&normalized); + let normalized = normalized.strip_suffix(".cmd").unwrap_or(normalized); + let Some(id) = manifest_id_for_executable(normalized) else { + return Ok(None); + }; + Ok(bundled_herdr_manifests()? + .iter() + .find(|manifest| manifest.id == id)) +} + +fn manifest_id_for_executable(executable: &str) -> Option<&'static str> { + Some(match executable { + "pi" => "pi", + "claude" => "claude", + "codex" => "codex", + "gemini" => "gemini", + "cursor-agent" => "cursor", + "devin" => "devin", + "agy" => "agy", + "cline" => "cline", + "opencode" => "opencode", + "copilot" => "copilot", + "kimi" => "kimi", + "kiro-cli" => "kiro", + "droid" => "droid", + "amp" => "amp", + "grok" => "grok", + "hermes" => "hermes", + "kilo" => "kilo", + "qodercli" => "qodercli", + "qwen" => "qwen", + "letta" => "letta", + "maki" => "maki", + "muse" => "muse", + _ => return None, + }) +} + +/// Evaluate one validated manifest against a live screen and explicit OSC +/// title/progress evidence. +#[must_use] +pub fn evaluate_herdr_manifest( + manifest: &HerdrManifest, + screen: &str, + osc_title: &str, + osc_progress: &str, +) -> DetectionResult { + if screen.len() > 64 * 1024 { + return unknown_result(&manifest.id, "live_screen_unavailable", None); + } + if osc_title.len() > 4096 || osc_progress.len() > 4096 { + return unknown_result(&manifest.id, "osc_evidence_exceeds_limit", None); + } + if !screen_text_is_safe(screen) + || !screen_text_is_safe(osc_title) + || !screen_text_is_safe(osc_progress) + { + return unknown_result(&manifest.id, "unsafe_terminal_evidence", None); + } + let input = MatchInput { + screen, + osc_title, + osc_progress, + }; + let mut selected: Option<(&CompiledRule, String)> = None; + for rule in &manifest.rules { + let region = select_region(input, &rule.region); + if !gate_matches(&rule.gate, region) { + continue; + } + match selected { + Some((previous, _)) if previous.priority >= rule.priority => {} + _ => selected = Some((rule, region.to_owned())), + } + } + let Some((rule, _matched_region)) = selected else { + let state = if manifest.id == "codex" { + AgentState::Unknown + } else { + AgentState::Idle + }; + let reason = if manifest.id == "codex" { + "codex_state_ambiguous" + } else { + "default_known_agent_idle_fallback" + }; + return result_with_rule(manifest, state, reason, None, RuleOutcomeFlags::default()); + }; + let state = rule.state.unwrap_or(AgentState::Unknown); + result_with_rule( + manifest, + state, + "manifest_rule_matched", + Some(&rule.id), + RuleOutcomeFlags { + visibility: DetectionVisibility { + idle: rule.visible_idle && state == AgentState::Idle, + blocker: rule.visible_blocker && state == AgentState::Blocked, + working: rule.visible_working && state == AgentState::Working, + }, + skip_state_update: rule.skip_state_update, + }, + ) +} + +fn result_with_rule( + manifest: &HerdrManifest, + state: AgentState, + reason: &'static str, + rule_id: Option<&str>, + flags: RuleOutcomeFlags, +) -> DetectionResult { + let mut evidence = Vec::new(); + if let Some(rule_id) = rule_id { + evidence.push(DetectionEvidence { + source: "screen_manifest", + manifest_source: Some("herdr-bundled".to_owned()), + manifest_version: manifest.version.clone(), + rule_id: Some(rule_id.to_owned()), + value: "manifest rule matched".to_owned(), + }); + } + DetectionResult { + agent: Some(manifest.id.clone()), + state, + confidence: if rule_id.is_some() { + Confidence::High + } else if state == AgentState::Idle { + Confidence::Medium + } else { + Confidence::None + }, + reason, + evidence, + visibility: flags.visibility, + skip_state_update: flags.skip_state_update, + } +} + +fn unknown_result( + manifest_id: &str, + reason: &'static str, + rule_id: Option<&str>, +) -> DetectionResult { + DetectionResult { + agent: Some(manifest_id.to_owned()), + state: AgentState::Unknown, + confidence: Confidence::None, + reason, + evidence: rule_id + .map(|id| DetectionEvidence { + source: "screen_manifest", + manifest_source: Some("herdr-bundled".to_owned()), + manifest_version: None, + rule_id: Some(id.to_owned()), + value: "manifest rule matched".to_owned(), + }) + .into_iter() + .collect(), + visibility: DetectionVisibility::default(), + skip_state_update: false, + } +} + +fn validate_doc(doc: &ManifestDoc) -> io::Result<()> { + if !valid_metadata(&doc.id, 64) + || doc + .version + .as_ref() + .is_some_and(|value| !valid_metadata(value, 64)) + || doc + .updated_at + .as_ref() + .is_some_and(|value| !valid_metadata(value, 64)) + || doc.aliases.len() > 32 + || doc.aliases.iter().any(|value| !valid_metadata(value, 64)) + { + return Err(invalid_input("invalid agent manifest metadata")); + } + if doc + .min_engine_version + .is_some_and(|required| required > ENGINE_VERSION) + { + return Err(invalid_input("agent manifest requires a newer engine")); + } + if doc.rules.is_empty() || doc.rules.len() > MAX_RULES { + return Err(invalid_input("invalid agent manifest rule count")); + } + let mut ids = std::collections::BTreeSet::new(); + let mut complexity = Complexity::default(); + for rule in &doc.rules { + if !valid_metadata(&rule.id, 128) || !ids.insert(rule.id.as_str()) { + return Err(invalid_input("invalid or duplicate agent manifest rule id")); + } + if rule.skip_state_update + && (rule.state != Some(AgentState::Unknown) + || rule.visible_idle + || rule.visible_blocker + || rule.visible_working) + { + return Err(invalid_input("invalid skip_state_update rule")); + } + validate_region(&rule.region, doc.min_engine_version)?; + let gate = GateDoc { + all: rule.all.clone(), + any: rule.any.clone(), + not: rule.not.clone(), + contains: rule.contains.clone(), + regex: rule.regex.clone(), + line_regex: rule.line_regex.clone(), + }; + validate_gate(&gate, 0, true, &mut complexity)?; + } + Ok(()) +} + +fn validate_gate( + gate: &GateDoc, + depth: usize, + require_positive: bool, + complexity: &mut Complexity, +) -> io::Result<()> { + if depth > MAX_GATE_DEPTH { + return Err(invalid_input("agent manifest gate nesting exceeds limit")); + } + complexity.gates += 1; + if complexity.gates > MAX_TOTAL_GATES { + return Err(invalid_input("agent manifest gate count exceeds limit")); + } + let count = gate.contains.len() + gate.regex.len() + gate.line_regex.len(); + if count > MAX_MATCHERS_PER_GATE { + return Err(invalid_input("agent manifest matcher count exceeds limit")); + } + complexity.matchers += count; + if complexity.matchers > MAX_TOTAL_MATCHERS { + return Err(invalid_input( + "agent manifest total matcher count exceeds limit", + )); + } + if gate + .contains + .iter() + .chain(&gate.regex) + .chain(&gate.line_regex) + .any(|value| value.chars().count() > MAX_MATCHER_CHARS) + { + return Err(invalid_input("agent manifest matcher length exceeds limit")); + } + let positive = count > 0 || !gate.all.is_empty() || !gate.any.is_empty(); + let has_any = positive || !gate.not.is_empty(); + if (require_positive && !positive) || (!require_positive && !has_any) { + return Err(invalid_input("agent manifest gate has no positive matcher")); + } + for pattern in gate.regex.iter().chain(&gate.line_regex) { + compile_regex(pattern)?; + } + for nested in &gate.all { + validate_gate(nested, depth + 1, true, complexity)?; + } + for nested in &gate.any { + validate_gate(nested, depth + 1, true, complexity)?; + } + for nested in &gate.not { + validate_gate(nested, depth + 1, false, complexity)?; + } + Ok(()) +} + +fn compile_gate(gate: &GateDoc) -> io::Result { + Ok(CompiledGate { + all: gate + .all + .iter() + .map(compile_gate) + .collect::>()?, + any: gate + .any + .iter() + .map(compile_gate) + .collect::>()?, + not: gate + .not + .iter() + .map(compile_gate) + .collect::>()?, + contains: gate + .contains + .iter() + .map(|text| text.to_lowercase()) + .collect(), + regex: gate + .regex + .iter() + .map(|pattern| compile_regex(pattern)) + .collect::>()?, + line_regex: gate + .line_regex + .iter() + .map(|pattern| compile_regex(pattern)) + .collect::>()?, + }) +} + +fn compile_regex(pattern: &str) -> io::Result { + RegexBuilder::new(pattern) + .size_limit(MAX_REGEX_SIZE) + .dfa_size_limit(MAX_REGEX_SIZE) + .build() + .map_err(|_| invalid_input("invalid or oversized agent manifest regex")) +} + +fn gate_matches(gate: &CompiledGate, text: &str) -> bool { + let lower = text.to_lowercase(); + gate.contains.iter().all(|needle| lower.contains(needle)) + && gate.regex.iter().all(|regex| regex.is_match(text)) + && gate + .line_regex + .iter() + .all(|regex| text.lines().any(|line| regex.is_match(line))) + && gate.all.iter().all(|nested| gate_matches(nested, text)) + && (gate.any.is_empty() || gate.any.iter().any(|nested| gate_matches(nested, text))) + && !gate.not.iter().any(|nested| gate_matches(nested, text)) +} + +#[derive(Clone, Copy)] +struct MatchInput<'a> { + screen: &'a str, + osc_title: &'a str, + osc_progress: &'a str, +} + +fn select_region<'a>(input: MatchInput<'a>, name: &str) -> &'a str { + let region = name.trim(); + match region { + "whole_recent" => input.screen, + "after_last_prompt_marker" => after_last_prompt_marker(input.screen), + "before_current_prompt_marker" => before_current_prompt_marker(input.screen), + "whole_recent_without_current_prompt_marker" => { + whole_recent_without_current_prompt_marker(input.screen) + } + "current_prompt_block_marker" => current_prompt_block_marker(input.screen).unwrap_or(""), + "after_current_prompt_block_marker" => { + after_current_prompt_block_marker(input.screen).unwrap_or("") + } + "prompt_box_body" => prompt_box_body(input.screen).unwrap_or(""), + "above_prompt_box" => above_prompt_box(input.screen), + "last_non_empty_above_prompt_box" => last_non_empty_line(above_prompt_box(input.screen)), + "after_last_horizontal_rule" => after_last_horizontal_rule(input.screen), + "osc_title" => input.osc_title, + "osc_progress" => input.osc_progress, + _ => match parse_count(region, "bottom_lines") { + Some(count) => bottom_lines(input.screen, count), + None => match parse_count(region, "bottom_non_empty_lines") { + Some(count) => bottom_non_empty_lines(input.screen, count), + None => parse_top_count(region) + .map_or("", |count| top_non_empty_lines(input.screen, count)), + }, + }, + } +} + +fn validate_region(region: &str, min_engine: Option) -> io::Result<()> { + let valid = matches!( + region.trim(), + "whole_recent" + | "after_last_prompt_marker" + | "before_current_prompt_marker" + | "whole_recent_without_current_prompt_marker" + | "current_prompt_block_marker" + | "after_current_prompt_block_marker" + | "prompt_box_body" + | "above_prompt_box" + | "last_non_empty_above_prompt_box" + | "after_last_horizontal_rule" + | "osc_title" + | "osc_progress" + ) || parse_count(region.trim(), "bottom_lines").is_some() + || parse_count(region.trim(), "bottom_non_empty_lines").is_some() + || parse_top_count(region.trim()).is_some(); + if !valid + || (region.trim().starts_with("top_non_empty_lines(") + && min_engine.is_some_and(|version| version < 3)) + { + return Err(invalid_input( + "invalid or unsupported agent manifest region", + )); + } + Ok(()) +} + +fn parse_count(spec: &str, prefix: &str) -> Option { + let value = spec + .strip_prefix(prefix)? + .strip_prefix('(')? + .strip_suffix(')')?; + value.parse().ok() +} + +fn parse_top_count(spec: &str) -> Option { + let value = spec + .strip_prefix("top_non_empty_lines(")? + .strip_suffix(')')?; + if value.starts_with('0') || !value.bytes().all(|byte| byte.is_ascii_digit()) { + return None; + } + value + .parse::() + .ok() + .filter(|count| u16::try_from(*count).is_ok()) +} + +fn lines(text: &str) -> Vec<&str> { + text.lines().collect() +} + +fn slice_from<'a>(text: &'a str, rows: &[&str], index: usize) -> &'a str { + let offset = rows[..index.min(rows.len())] + .iter() + .map(|line| line.len() + 1) + .sum::() + .min(text.len()); + &text[offset..] +} + +fn bottom_lines(text: &str, count: usize) -> &str { + let rows = lines(text); + slice_from(text, &rows, rows.len().saturating_sub(count)) +} + +fn bottom_non_empty_lines(text: &str, count: usize) -> &str { + let rows = lines(text); + let Some(start) = rows + .iter() + .enumerate() + .rev() + .filter(|(_, line)| !line.trim().is_empty()) + .take(count) + .last() + .map(|(index, _)| index) + else { + return ""; + }; + slice_from(text, &rows, start) +} + +fn top_non_empty_lines(text: &str, count: usize) -> &str { + let rows = lines(text); + let Some(end) = rows + .iter() + .enumerate() + .filter(|(_, line)| !line.trim().is_empty()) + .take(count) + .last() + .map(|(index, _)| index + 1) + else { + return ""; + }; + let offset = rows[..end] + .iter() + .map(|line| line.len() + 1) + .sum::() + .min(text.len()); + &text[..offset] +} + +fn after_last_prompt_marker(text: &str) -> &str { + let rows = lines(text); + rows.iter() + .rposition(|line| codex_prompt_line(line)) + .map_or(text, |index| slice_from(text, &rows, index + 1)) +} + +fn before_current_prompt_marker(text: &str) -> &str { + let rows = lines(text); + let Some(index) = current_codex_prompt_index(&rows) else { + return text; + }; + let offset = rows[..index] + .iter() + .map(|line| line.len() + 1) + .sum::() + .min(text.len()); + &text[..offset] +} + +fn whole_recent_without_current_prompt_marker(text: &str) -> &str { + if current_codex_prompt_index(&lines(text)).is_some() { + "" + } else { + text + } +} + +fn current_prompt_block_marker(text: &str) -> Option<&str> { + let rows = lines(text); + let prompt = current_codex_prompt_index(&rows)?; + rows[..prompt] + .iter() + .rev() + .find(|line| codex_block_marker_line(line)) + .copied() +} + +fn after_current_prompt_block_marker(text: &str) -> Option<&str> { + let rows = lines(text); + let prompt = current_codex_prompt_index(&rows)?; + let block = rows[..prompt] + .iter() + .rposition(|line| codex_block_marker_line(line))?; + Some(slice_from(text, &rows, block)) +} + +fn current_codex_prompt_index(rows: &[&str]) -> Option { + let prompt = rows.iter().rposition(|line| codex_prompt_line(line))?; + (!rows[prompt + 1..] + .iter() + .any(|line| codex_block_marker_line(line))) + .then_some(prompt) +} + +fn codex_prompt_line(line: &str) -> bool { + line == "›" || line.starts_with("› ") +} + +fn codex_block_marker_line(line: &str) -> bool { + line.starts_with(['•', '■', '✗', '✓']) +} + +fn prompt_box_body(text: &str) -> Option<&str> { + let rows = lines(text); + let top = prompt_box_top_border_index(&rows)?; + let start = rows[..=top] + .iter() + .map(|line| line.len() + 1) + .sum::(); + let end_index = rows[top + 1..] + .iter() + .position(|line| is_horizontal_rule(line)) + .map_or(rows.len(), |relative| top + 1 + relative); + let end = rows[..end_index] + .iter() + .map(|line| line.len() + 1) + .sum::() + .min(text.len()); + Some(&text[start.min(text.len())..end]) +} + +fn above_prompt_box(text: &str) -> &str { + let rows = lines(text); + let Some(top) = prompt_box_top_border_index(&rows) else { + return text; + }; + let end = rows[..top] + .iter() + .map(|line| line.len() + 1) + .sum::() + .min(text.len()); + &text[..end] +} + +fn after_last_horizontal_rule(text: &str) -> &str { + let mut offset = 0usize; + let mut end = 0usize; + for line in text.lines() { + offset = (offset + line.len() + 1).min(text.len()); + if is_horizontal_rule(line) { + end = offset; + } + } + &text[end..] +} + +fn last_non_empty_line(text: &str) -> &str { + text.lines() + .rev() + .find(|line| !line.trim().is_empty()) + .unwrap_or("") +} + +fn prompt_box_top_border_index(rows: &[&str]) -> Option { + let mut count = 0; + for index in (0..rows.len()).rev() { + if is_horizontal_rule(rows[index]) { + count += 1; + if count == 2 { + return Some(index); + } + } + } + None +} + +fn is_horizontal_rule(line: &str) -> bool { + let trimmed = line.trim(); + let count = trimmed + .chars() + .take_while(|character| *character == '─') + .count(); + if count == 0 { + return false; + } + let suffix = trimmed.chars().skip(count).collect::(); + suffix.trim().is_empty() || count >= 3 +} + +fn valid_metadata(value: &str, max: usize) -> bool { + !value.is_empty() + && value.len() <= max + && !value + .chars() + .any(|character| character.is_control() || is_bidi_control(character)) +} + +fn screen_text_is_safe(value: &str) -> bool { + !value + .chars() + .any(|character| character.is_control() && character != '\n' && character != '\t') + && !value.chars().any(is_bidi_control) +} + +fn is_bidi_control(character: char) -> bool { + matches!( + character, + '\u{061c}' + | '\u{200e}' + | '\u{200f}' + | '\u{202a}'..='\u{202e}' + | '\u{2066}'..='\u{2069}' + | '\u{feff}' + ) +} + +fn toml_nesting_is_bounded(source: &str) -> bool { + let bytes = source.as_bytes(); + let mut index = 0; + let mut depth = 0usize; + let mut quote = None; + let mut multiline = false; + let mut escaped = false; + let mut comment = false; + while index < bytes.len() { + let byte = bytes[index]; + if comment { + if byte == b'\n' { + comment = false; + } + index += 1; + continue; + } + if let Some(delimiter) = quote { + if delimiter == b'"' && !multiline && escaped { + escaped = false; + index += 1; + continue; + } + if delimiter == b'"' && !multiline && byte == b'\\' { + escaped = true; + index += 1; + continue; + } + if byte == delimiter { + if multiline { + if bytes.get(index..index + 3) == Some(&[delimiter; 3]) { + quote = None; + multiline = false; + index += 3; + continue; + } + } else { + quote = None; + } + } + index += 1; + continue; + } + if byte == b'#' { + comment = true; + index += 1; + continue; + } + if matches!(byte, b'"' | b'\'') { + quote = Some(byte); + multiline = bytes.get(index..index + 3) == Some(&[byte; 3]); + index += if multiline { 3 } else { 1 }; + continue; + } + if matches!(byte, b'[' | b'{') { + depth += 1; + if depth > MAX_TOML_NESTING { + return false; + } + } else if matches!(byte, b']' | b'}') { + depth = depth.saturating_sub(1); + } + index += 1; + } + true +} + +fn invalid_input(message: &str) -> io::Error { + io::Error::new(io::ErrorKind::InvalidInput, message) +} + +fn invalid_input_owned(message: String) -> io::Error { + io::Error::new(io::ErrorKind::InvalidInput, message) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn manifest(rules: &str, id: &str) -> String { + format!("id = \"{id}\"\nversion = \"1\"\n{rules}") + } + + #[test] + fn every_pinned_bundled_manifest_parses_and_compiles() { + let manifests = bundled_herdr_manifests().expect("all audited manifests parse"); + assert_eq!(manifests.len(), 22); + let ids = manifests + .iter() + .map(|value| value.id.as_str()) + .collect::>(); + assert!(ids.contains(&"codex")); + assert!(ids.contains(&"claude")); + assert!(ids.contains(&"copilot")); + } + + #[test] + fn gate_operators_and_priority_match_the_pinned_manifest_contract() { + let manifest = parse_herdr_manifest(&manifest( + r#" +[[rules]] +id = "lower" +state = "working" +priority = 10 +contains = ["active"] + +[[rules]] +id = "upper" +state = "blocked" +priority = 20 +all = [{ contains = ["active"], any = [{ regex = ["approve\\s+now"] }, { line_regex = ["^allow$"] }] }] +not = [{ contains = ["cancelled"] }] +"#, + "sample", + )) + .unwrap(); + let result = evaluate_herdr_manifest(&manifest, "active\napprove now", "", ""); + assert_eq!(result.state, AgentState::Blocked); + assert_eq!(result.evidence[0].rule_id.as_deref(), Some("upper")); + + let excluded = evaluate_herdr_manifest(&manifest, "active\napprove now\ncancelled", "", ""); + assert_eq!(excluded.state, AgentState::Working); + assert_eq!(excluded.evidence[0].rule_id.as_deref(), Some("lower")); + } + + #[test] + fn ties_keep_the_first_rule_and_regions_select_only_the_requested_text() { + let manifest = parse_herdr_manifest(&manifest( + r#" +[[rules]] +id = "first" +state = "working" +priority = 1 +region = "bottom_non_empty_lines(1)" +contains = ["active"] + +[[rules]] +id = "tie" +state = "blocked" +priority = 1 +region = "bottom_non_empty_lines(1)" +contains = ["active"] +"#, + "sample", + )) + .unwrap(); + let result = evaluate_herdr_manifest(&manifest, "active\n\nidle", "", ""); + assert_eq!(result.state, AgentState::Idle); + let result = evaluate_herdr_manifest(&manifest, "old\n\nactive", "", ""); + assert_eq!(result.state, AgentState::Working); + assert_eq!(result.evidence[0].rule_id.as_deref(), Some("first")); + } + + #[test] + fn osc_regions_skip_state_flags_and_codex_fallback_are_preserved() { + let codex = parse_herdr_manifest(include_str!("herdr_manifests/codex.toml")).unwrap(); + let fallback = evaluate_herdr_manifest(&codex, "ordinary", "", ""); + assert_eq!(fallback.state, AgentState::Unknown); + assert_eq!(fallback.reason, "codex_state_ambiguous"); + + let amp = parse_herdr_manifest(include_str!("herdr_manifests/amp.toml")).unwrap(); + let title = evaluate_herdr_manifest(&, "", "Amp: Plugin confirmation needed", ""); + assert_eq!(title.state, AgentState::Blocked); + assert!(title.visibility.blocker); + + let viewer = evaluate_herdr_manifest( + &codex, + "↑/↓ to scroll\npgup/pgdn to move\nhome/end to jump\nq to quit\nesc to edit prev", + "", + "", + ); + assert!(viewer.skip_state_update); + } + + #[test] + fn malformed_unknown_or_excessively_nested_manifests_fail_closed() { + let bad_regex = manifest( + "[[rules]]\nid = \"bad\"\nstate = \"working\"\nregex = [\"(\"]\n", + "bad", + ); + assert!(parse_herdr_manifest(&bad_regex).is_err()); + let unknown = manifest( + "[[rules]]\nid = \"bad\"\nstate = \"working\"\nextra = 1\ncontains = [\"x\"]\n", + "bad", + ); + assert!(parse_herdr_manifest(&unknown).is_err()); + let nested = format!( + "id = \"bad\"\nrules = {}{}{}", + "[".repeat(40), + "0", + "]".repeat(40) + ); + assert!(parse_herdr_manifest(&nested).is_err()); + } + + #[test] + fn top_non_empty_region_without_minimum_engine_version_matches_upstream() { + let source = manifest( + "[[rules]]\nid = \"top\"\nstate = \"working\"\nregion = \"top_non_empty_lines(1)\"\ncontains = [\"active\"]\n", + "sample", + ); + let parsed = parse_herdr_manifest(&source).expect("minimum engine version is optional"); + let result = evaluate_herdr_manifest(&parsed, "active\ninactive", "", ""); + assert_eq!(result.state, AgentState::Working); + + let old_engine = + source.replace("id = \"sample\"", "id = \"sample\"\nmin_engine_version = 2"); + assert!(parse_herdr_manifest(&old_engine).is_err()); + } + + #[test] + fn every_upstream_region_selector_is_accepted_and_slices_screen_as_expected() { + let screen = "top\n• current block\n────────\nbody\n────────\nlast\n› run\n"; + let input = MatchInput { + screen, + osc_title: "title evidence", + osc_progress: "progress evidence", + }; + let cases = [ + ("whole_recent", screen), + ("after_last_prompt_marker", ""), + ( + "before_current_prompt_marker", + "top\n• current block\n────────\nbody\n────────\nlast\n", + ), + ("whole_recent_without_current_prompt_marker", ""), + ("current_prompt_block_marker", "• current block"), + ( + "after_current_prompt_block_marker", + "• current block\n────────\nbody\n────────\nlast\n› run\n", + ), + ("prompt_box_body", "body\n"), + ("above_prompt_box", "top\n• current block\n"), + ("last_non_empty_above_prompt_box", "• current block"), + ("after_last_horizontal_rule", "last\n› run\n"), + ("osc_title", "title evidence"), + ("osc_progress", "progress evidence"), + ("bottom_lines(1)", "› run\n"), + ("bottom_non_empty_lines(1)", "› run\n"), + ("top_non_empty_lines(1)", "top\n"), + ]; + for (selector, expected) in cases { + validate_region(selector, None).unwrap_or_else(|error| panic!("{selector}: {error}")); + assert_eq!(select_region(input, selector), expected, "{selector}"); + } + } + + #[test] + fn unsafe_osc_terminal_controls_fail_closed() { + let manifest = parse_herdr_manifest(include_str!("herdr_manifests/amp.toml")).unwrap(); + let result = evaluate_herdr_manifest(&manifest, "", "title\u{001b}", ""); + assert_eq!(result.state, AgentState::Unknown); + assert_eq!(result.reason, "unsafe_terminal_evidence"); + } + + #[test] + fn audited_direct_interactive_executables_resolve_to_bundled_manifests() { + for (executable, id) in [ + ("pi", "pi"), + ("claude", "claude"), + ("codex", "codex"), + ("gemini", "gemini"), + ("cursor-agent", "cursor"), + ("devin", "devin"), + ("agy", "agy"), + ("cline", "cline"), + ("opencode", "opencode"), + ("copilot", "copilot"), + ("kimi", "kimi"), + ("kiro-cli", "kiro"), + ("droid", "droid"), + ("amp", "amp"), + ("grok", "grok"), + ("hermes", "hermes"), + ("kilo", "kilo"), + ("qodercli", "qodercli"), + ("qwen", "qwen"), + ("letta", "letta"), + ("maki", "maki"), + ("muse", "muse"), + ] { + assert_eq!( + bundled_manifest_for_executable(executable) + .unwrap() + .map(|manifest| manifest.id.as_str()), + Some(id), + "{executable}" + ); + } + assert!(bundled_manifest_for_executable("custom-wrapper") + .unwrap() + .is_none()); + assert!(bundled_manifest_for_executable("codex\nmalicious") + .unwrap() + .is_none()); + } +} diff --git a/crates/rover-agents/src/herdr_manifests/amp.toml b/crates/rover-agents/src/herdr_manifests/amp.toml new file mode 100644 index 0000000..1fc5dfa --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/amp.toml @@ -0,0 +1,65 @@ +id = "amp" +version = "2026.07.09.1" +min_engine_version = 2 +updated_at = "2026-07-09T00:00:00Z" +aliases = ["amp-local"] + +[[rules]] +id = "osc_title_plugin_confirmation_blocked" +state = "blocked" +priority = 1100 +region = "osc_title" +visible_blocker = true +contains = ["Plugin confirmation needed"] + +[[rules]] +id = "osc_title_working" +state = "working" +priority = 1050 +region = "osc_title" +visible_working = true +regex = ['^[\x{2800}-\x{28FF}] '] + +[[rules]] +id = "approval_footer" +state = "blocked" +priority = 300 +region = "whole_recent" +visible_blocker = true +any = [ + { contains = ["waiting for approval"] }, + { contains = ["invoke tool"] }, + { contains = ["run this command?"] }, + { contains = ["allow editing file:"] }, + { contains = ["allow creating file:"] }, + { contains = ["confirm tool call"] }, + { contains = ["approve"], any = [{ contains = ["allow all for this session"] }, { contains = ["allow all for every session"] }, { contains = ["allow file for every session"] }, { contains = ["deny with feedback"] }] }, +] + +[[rules]] +id = "status_footer_working" +state = "working" +priority = 200 +region = "bottom_non_empty_lines(5)" +visible_working = true +line_regex = ['(?i)^\s*╰\s+\S+\s+(thinking|streaming|running tools|waiting)\s+─'] + +[[rules]] +id = "esc_cancel_working" +state = "working" +priority = 100 +region = "whole_recent" +visible_working = true +contains = ["esc to cancel"] + +[[rules]] +id = "osc_title_idle" +state = "idle" +priority = 50 +region = "osc_title" +visible_idle = true +contains = [" - amp - "] +not = [ + { regex = ['^[\x{2800}-\x{28FF}] '] }, + { contains = ["Plugin confirmation needed"] }, +] diff --git a/crates/rover-agents/src/herdr_manifests/antigravity.toml b/crates/rover-agents/src/herdr_manifests/antigravity.toml new file mode 100644 index 0000000..8b9bcfc --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/antigravity.toml @@ -0,0 +1,33 @@ +id = "agy" +version = "2026.06.24.1" +min_engine_version = 1 +updated_at = "2026-06-24T00:00:00Z" +aliases = ["antigravity", "antigravity-cli"] + +[[rules]] +id = "permission_prompt" +state = "blocked" +priority = 300 +region = "whole_recent" +visible_blocker = true +contains = ["requesting permission for:"] +any = [ + { contains = ["do you want to proceed?"] }, + { contains = ["tab amend", "edit command"] }, +] + +[[rules]] +id = "spinner_working" +state = "working" +priority = 100 +region = "whole_recent" +visible_working = true +line_regex = ['^\s*[\u2800-\u28FF]+\s+\p{Alphabetic}+\w*ing\b'] + +[[rules]] +id = "background_tasks_working" +state = "working" +priority = 90 +region = "bottom_non_empty_lines(5)" +visible_working = true +line_regex = ['(?i)·\s*[1-9][0-9]*\s+task'] diff --git a/crates/rover-agents/src/herdr_manifests/claude.toml b/crates/rover-agents/src/herdr_manifests/claude.toml new file mode 100644 index 0000000..0c7bdcf --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/claude.toml @@ -0,0 +1,230 @@ +id = "claude" +version = "2026.09.11.1" +min_engine_version = 2 +updated_at = "2026-09-11T00:00:00Z" +aliases = ["claude-code"] + +[[rules]] +id = "osc_title_working" +state = "working" +priority = 1100 +region = "osc_title" +visible_working = true +# Braille covers <= 2.1.227; half-circles are the 2.1.228 busy spinner. +regex = ['^[\x{2800}-\x{28FF}\x{25D0}-\x{25D3}] '] + +[[rules]] +id = "live_turn_working" +state = "working" +priority = 970 +region = "bottom_non_empty_lines(12)" +visible_working = true +any = [ + { line_regex = ['^\s*[⏸⏵].*esc to interrupt(?:\s|·|$)'] }, + { line_regex = ['^\s*[\x{002A}\x{00B7}\x{2722}\x{2733}\x{2736}\x{273B}\x{273D}]\s+\S.*…(?:\s+\(\d+[smh](?:\s|·)|\s*$)'] }, +] + +[[rules]] +id = "background_agents_working" +state = "working" +priority = 965 +region = "last_non_empty_above_prompt_box" +visible_working = true +line_regex = ['^\s*[\x{002A}\x{00B7}\x{2722}\x{2736}\x{273B}\x{273D}]\s+Waiting for [1-9]\d* background agents? to finish\s*$'] + +[[rules]] +id = "background_mcp_task_working" +state = "working" +priority = 965 +region = "bottom_non_empty_lines(12)" +visible_working = true +# Claude renders activity summaries at column zero; wrapped continuations are indented. +# Keeping that shape prevents user prompt text from impersonating this signal. +regex = ['(?m)^[\x{002A}\x{00B7}\x{2722}\x{2736}\x{273B}\x{273D}][ \t]+\S[^\n]*?(?:\n[ \t]+[^\n]*?){0,3}·(?:[ \t]+|\n[ \t]*)[1-9]\d*(?:[ \t]+|\n[ \t]*)MCP(?:[ \t]+|\n[ \t]*)tasks?(?:[ \t]+|\n[ \t]*)still(?:[ \t]+|\n[ \t]*)running[ \t]*$'] +not = [ + { contains = ["do you want to proceed?"] }, + { contains = ["esc to cancel"] }, + { contains = ["waiting for permission"] }, + { contains = ["do you want to allow this connection?"] }, + { contains = ["tab to amend"] }, + { contains = ["ctrl+e to explain"] }, +] + +[[rules]] +id = "btw_overlay_working" +state = "working" +priority = 975 +region = "bottom_non_empty_lines(5)" +visible_working = true +line_regex = [ + '^\s*/btw(?:\s|$)', + '(?i)esc to close\s*$', +] + +[[rules]] +id = "transcript_viewer" +state = "unknown" +priority = 1000 +region = "bottom_non_empty_lines(3)" +skip_state_update = true +contains = ["showing detailed transcript"] +any = [ + { contains = ["ctrl+o", "to toggle"] }, + { contains = ["ctrl+e", "show all"] }, + { contains = ["ctrl+e", "collapse"] }, + { contains = ["↑↓ scroll"] }, + { contains = ["? for shortcuts"] }, +] + +[[rules]] +id = "live_blocked_form" +state = "blocked" +priority = 980 +region = "after_last_horizontal_rule" +visible_blocker = true +contains = ["esc to cancel"] +any = [ + { contains = ["enter to confirm"] }, + { contains = ["enter to select"], any = [ + { contains = ["tab/arrow keys to navigate"] }, + { contains = ["arrow keys to navigate"] }, + { contains = ["arrows to navigate"] }, + { contains = ["↑/↓ to navigate"] }, + { contains = ["↑↓ to navigate"] }, + ] }, +] + +[[rules]] +id = "dynamic_workflow_prompt" +state = "blocked" +priority = 980 +region = "whole_recent" +visible_blocker = true +contains = ["run a dynamic workflow?", "esc to cancel"] + +[[rules]] +id = "mcp_elicitation_prompt" +state = "blocked" +priority = 980 +region = "whole_recent" +visible_blocker = true +# MCP elicitation dialogs (elicitation/create) show Accept/Decline controls +# with an "Esc to cancel" footer but no Enter hint, so live_blocked_form +# cannot see them (issue #3283). Gate on the invariant header line, the +# Accept/Decline control line, and the cancel footer. +contains = ["esc to cancel"] +line_regex = ['(?i)^\s*MCP server ["\x{201C}].+["\x{201D}] requests your input\s*$'] +all = [ + { any = [ + { line_regex = ['^\s*\x{276F}?\s*Accept\b'] }, + { line_regex = ['^\s*\x{276F}?\s*Decline\b'] }, + ] }, +] + +[[rules]] +id = "live_prompt_box" +state = "idle" +priority = 950 +region = "prompt_box_body" +visible_idle = true +line_regex = ['^\s*❯'] +not = [ + { contains = ["enter to select"] }, + { contains = ["esc to cancel"] }, + { contains = ["tab/arrow keys"] }, + { contains = ["arrow keys to navigate"] }, + { contains = ["↑/↓ to navigate"] }, +] + +[[rules]] +id = "model_picker_menu" +state = "unknown" +priority = 900 +region = "whole_recent" +skip_state_update = true +contains = ["select model", "enter to set as default", "esc to cancel"] +not = [ + { contains = ["do you want to proceed?"] }, + { contains = ["enter to select"] }, +] + +[[rules]] +id = "bash_permission_prompt" +state = "blocked" +priority = 850 +region = "whole_recent" +visible_blocker = true +contains = ["do you want to proceed?"] +any = [ + { contains = ["bash command"] }, + { contains = ["bash("] }, + { contains = ["contains expansion"] }, + { contains = ["tab to amend"] }, + { contains = ["ctrl+e to explain"] }, +] +# Claude marks the selected option with "❯", so every option branch has to allow +# that prefix. Numbered branches that omit it only match options the cursor has +# moved away from, which left the resting layout below unmatched here (#2650): +# ❯ 1. Yes / 2. Yes, and don't ask again for: / 3. No +# Cover both the two-option and "don't ask again" three-option shapes so this +# rule, not the narrower generic_permission_prompt, claims Bash approvals. +all = [ + { any = [ + { line_regex = ['(?i)^\s*❯?\s*yes\b'] }, + { line_regex = ['(?i)^\s*❯?\s*1\.\s*yes\b'] }, + { line_regex = ['(?i)^\s*❯?\s*2\.\s*yes\b'] }, + { line_regex = ['(?i)^\s*❯?\s*2\.\s*no\b'] }, + { line_regex = ['(?i)^\s*❯?\s*3\.\s*no\b'] }, + ] }, +] + +[[rules]] +id = "generic_permission_prompt" +state = "blocked" +priority = 840 +region = "after_last_horizontal_rule" +visible_blocker = true +contains = ["do you want to proceed?", "esc to cancel"] +all = [ + { any = [ + { line_regex = ['(?i)^\s*❯?\s*1\.\s*yes\b'] }, + { line_regex = ['(?i)^\s*2\.\s*yes\b'] }, + { line_regex = ['(?i)^\s*2\.\s*no\b'] }, + { line_regex = ['(?i)^\s*3\.\s*no\b'] }, + ] }, +] + +[[rules]] +id = "legacy_no_prompt_blocker" +state = "blocked" +priority = 300 +region = "whole_recent" +any = [ + { contains = ["do you want to"], any = [{ contains = ["yes"] }, { contains = ["❯"] }] }, + { contains = ["would you like to"], any = [{ contains = ["yes"] }, { contains = ["❯"] }] }, + { contains = ["waiting for permission"] }, + { contains = ["do you want to allow this connection?"] }, + { contains = ["tab to amend"] }, + { contains = ["ctrl+e to explain"] }, + { contains = ["do you want to proceed?", "esc to cancel"] }, + { contains = ["review your answers"] }, + { contains = ["skip interview and plan immediately"] }, +] +not = [ + { regex = ['(?m)^\s*❯\s*$'] }, +] + +[[rules]] +id = "osc_title_idle" +state = "idle" +priority = 250 +region = "osc_title" +visible_idle = true +regex = ['^\x{2733} '] + +[[rules]] +id = "osc_progress_idle" +state = "idle" +priority = 250 +region = "osc_progress" +regex = ['^4;0'] diff --git a/crates/rover-agents/src/herdr_manifests/cline.toml b/crates/rover-agents/src/herdr_manifests/cline.toml new file mode 100644 index 0000000..2d31408 --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/cline.toml @@ -0,0 +1,63 @@ +id = "cline" +version = "2026.09.11.1" +min_engine_version = 1 +updated_at = "2026-09-11T00:00:00Z" + +[[rules]] +id = "tool_permission" +state = "blocked" +priority = 300 +region = "whole_recent" +visible_blocker = true +any = [ + { contains = ["let cline use this tool"] }, + { contains = ["[act mode]", "execute command?", "yes"] }, + { contains = ["[act mode]", "use this tool?", "yes"] }, + { contains = ["[plan mode]", "execute command?", "yes"] }, + { contains = ["[plan mode]", "use this tool?", "yes"] }, +] + +[[rules]] +id = "inline_tool_permission" +state = "blocked" +priority = 300 +region = "bottom_non_empty_lines(16)" +visible_blocker = true +contains = ["Cline needs permission", "Approve tool call?", "[y] Approve", "[n] Deny"] + +[[rules]] +id = "inline_question" +state = "blocked" +priority = 300 +region = "bottom_non_empty_lines(24)" +visible_blocker = true +line_regex = ['^\s*Cline is asking a question\s*$', '^\s*>\s+\S'] +contains = ["(Tab)", "Shift+Tab"] + +[[rules]] +id = "active_turn" +state = "working" +priority = 200 +region = "bottom_non_empty_lines(20)" +visible_working = true +any = [ + { line_regex = ['^\s*[\x{2801}-\x{28FF}]\s+\S'] }, + { contains = ["Thinking... (esc to cancel)"] }, +] + +[[rules]] +id = "composer_idle" +state = "idle" +priority = 100 +region = "bottom_non_empty_lines(12)" +visible_idle = true +line_regex = ['^\s*❯(?:\s.*)?$'] +contains = ["─", "(Tab)", "Shift+Tab"] + +[[rules]] +id = "default_cline_working" +state = "working" +priority = -10 +region = "whole_recent" +visible_working = true +regex = ['(?s).+'] diff --git a/crates/rover-agents/src/herdr_manifests/codex.toml b/crates/rover-agents/src/herdr_manifests/codex.toml new file mode 100644 index 0000000..dd27575 --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/codex.toml @@ -0,0 +1,94 @@ +id = "codex" +version = "2026.09.23.1" +min_engine_version = 3 +updated_at = "2026-09-23T22:12:00Z" + +[[rules]] +id = "osc_title_blocked" +state = "blocked" +priority = 1100 +region = "osc_title" +visible_blocker = true +contains = ["Action Required"] + +[[rules]] +id = "osc_title_working" +state = "working" +priority = 1050 +region = "osc_title" +visible_working = true +regex = ['(?:^| )[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏](?: |$)'] + +[[rules]] +id = "transcript_viewer" +state = "unknown" +priority = 1000 +region = "after_last_prompt_marker" +skip_state_update = true +contains = ["↑/↓ to scroll", "pgup/pgdn to", "home/end to jump", "q to quit"] +any = [ + { contains = ["esc to edit prev"] }, + { contains = ["esc/← to edit prev"] }, +] + +[[rules]] +id = "trust_directory" +state = "blocked" +priority = 950 +region = "top_non_empty_lines(20)" +visible_blocker = true +all = [ + { regex = ['\A> You are in [^\r\n]+(?:\r?\n|$)'] }, + { regex = ['(?s)Do\s+you\s+trust\s+the\s+contents\s+of\s+this\s+directory\?'] }, +] + +[[rules]] +id = "startup_update" +state = "blocked" +priority = 950 +region = "bottom_non_empty_lines(20)" +visible_blocker = true +contains = ["Update available!", "Update now"] +regex = ['Skip\s+until\s+next\s+version', 'Press enter to continue\s*\z'] + +[[rules]] +id = "live_strong_blocker" +state = "blocked" +priority = 900 +region = "after_last_prompt_marker" +visible_blocker = true +any = [ + { contains = ["press enter to confirm or esc to cancel"] }, + { contains = ["enter to submit answer"] }, + { contains = ["enter to submit all"] }, + { contains = ["allow command?"] }, + { contains = ["All Results", "Filesystem Only", "Plugins"] }, +] + +[[rules]] +id = "weak_blocker" +state = "blocked" +priority = 600 +region = "whole_recent_without_current_prompt_marker" +# Sparkles can replace the space after ›. A later response marker makes that prompt stale. +not = [{ regex = ['(?m)^›[⠁⠂⠄⠈⠐⠠⡀⢀][^\n]*(?:\n(?:[^•■✗✓\n][^\n]*)?)*\z'] }] +any = [ + { contains = ["[y/n]"] }, + { contains = ["yes (y)"] }, + { contains = ["do you want to"], any = [{ contains = ["yes"] }, { contains = ["❯"] }] }, + { contains = ["would you like to"], any = [{ contains = ["yes"] }, { contains = ["❯"] }] }, +] + +[[rules]] +id = "screen_working_fallback" +state = "working" +priority = 500 +region = "before_current_prompt_marker" +visible_working = true +# Support animated and reduced-motion status, including dynamic activity labels. +# The interrupt hint can be remapped, unbound, or hidden, and queued inputs can +# sit below the status. Require the live timer suffix with no later response. +any = [{ contains = [" to interrupt)"] }, { contains = ["s)"] }] +regex = ['(?m)^(?:[•◦][ \t]+)?[^\s›•◦■✗✓─][^\r\n]* \((?:[0-9]+[hm] )*[0-9]+s(?: • [^\r\n]+? to interrupt)?\)(?: · [^\r\n]*)?(?:\r?\n(?:[^•◦›■✗✓─\r\n][^\r\n]*|•[ \t]+(?:Queued\s+follow-up\s+inputs|Messages\s+to\s+be\s+submitted\s+after\s+next\s+tool\s+call(?:\s+\(press\s+[^\r\n]+?\s+to\s+interrupt\s+and\s+send\s+immediately\))?|Messages\s+to\s+be\s+submitted\s+at\s+end\s+of\s+turn)|›[⠁⠂⠄⠈⠐⠠⡀⢀][^\r\n]*)?)*\s*\z'] +# A failed reconnect keeps its final elapsed timer but is no longer working. +not = [{ line_regex = ['^(?:[•◦][ \t]+)?Reconnect failed — check the endpoint, then relaunch \([0-9hms ]+\)$'] }] diff --git a/crates/rover-agents/src/herdr_manifests/cursor.toml b/crates/rover-agents/src/herdr_manifests/cursor.toml new file mode 100644 index 0000000..ee03e6d --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/cursor.toml @@ -0,0 +1,57 @@ +id = "cursor" +version = "2026.08.03.1" +min_engine_version = 1 +updated_at = "2026-08-03T01:08:04Z" +aliases = ["cursor-agent"] + +[[rules]] +id = "write_file_approval" +state = "blocked" +priority = 320 +region = "bottom_non_empty_lines(8)" +visible_blocker = true +contains = ["write to this file?", "proceed (y)"] +any = [ + { contains = ["reject & propose changes"] }, + { contains = ["esc or n or p"] }, + { contains = ["add write("] }, +] + +[[rules]] +id = "approval_prompt" +state = "blocked" +priority = 300 +region = "whole_recent" +visible_blocker = true +any = [ + { contains = ["waiting for approval", "run this command?"], any = [{ contains = ["run (once) (y)"] }, { contains = ["skip (esc or n)"] }] }, + { contains = ["(y) (enter)"] }, + { line_regex = ['(?i)^\s*allow .*\(y\)'] }, + { contains = ["keep (n)"] }, + { contains = ["skip (esc or n)"] }, + { line_regex = ['(?i)^\s*(?:→\s*)?run .*\(y\)'] }, +] + +[[rules]] +id = "stop_hint_working" +state = "working" +priority = 100 +region = "bottom_non_empty_lines(6)" +visible_working = true +contains = ["ctrl+c to stop"] + +[[rules]] +id = "background_task_status_working" +state = "working" +priority = 95 +region = "bottom_non_empty_lines(5)" +visible_working = true +line_regex = ['(?i)\b[1-9][0-9]*\s+background\s+tasks?\b'] + +[[rules]] +id = "spinner_working" +state = "working" +priority = 90 +region = "bottom_non_empty_lines(8)" +visible_working = true +line_regex = ['^\s*(⬡|⬢|[\u2800-\u28FF]+)\s+\p{Alphabetic}+\w*ing\b'] diff --git a/crates/rover-agents/src/herdr_manifests/devin.toml b/crates/rover-agents/src/herdr_manifests/devin.toml new file mode 100644 index 0000000..c9564f7 --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/devin.toml @@ -0,0 +1,86 @@ +id = "devin" +version = "2026.06.15.1" +min_engine_version = 1 +updated_at = "2026-06-15T00:00:00Z" +aliases = ["devin-cli", "devin cli"] + +[[rules]] +id = "workspace_trust_prompt" +state = "blocked" +priority = 300 +region = "bottom_non_empty_lines(8)" +visible_blocker = true +contains = [ + "do you trust the authors of this directory?", + "with untrusted content.", + "yes, trust ", +] + +[[rules]] +id = "permission_prompt" +state = "blocked" +priority = 290 +region = "bottom_non_empty_lines(8)" +visible_blocker = true +contains = ["approve once", "select", "confirm", "esc cancel"] + +[[rules]] +id = "running_tools_footer" +state = "working" +priority = 200 +region = "bottom_non_empty_lines(8)" +visible_working = true +contains = ["running tools", "esc to interrupt"] +not = [ + { contains = ["approve once", "esc cancel"] }, +] + +[[rules]] +id = "guide_while_working" +state = "working" +priority = 190 +region = "bottom_non_empty_lines(6)" +visible_working = true +contains = ["guide devin while it works"] +not = [ + { contains = ["approve once", "esc cancel"] }, +] + +[[rules]] +id = "tool_reading_timeout" +state = "working" +priority = 180 +region = "bottom_non_empty_lines(8)" +visible_working = true +contains = ["reading shell ", "timeout:"] +not = [ + { contains = ["approve once", "esc cancel"] }, +] + +[[rules]] +id = "welcome_prompt_footer" +state = "idle" +priority = 120 +region = "bottom_non_empty_lines(8)" +visible_idle = true +contains = ["ask devin to build", "features, fix bugs", "your code"] +line_regex = ['^\s*❭ Ask Devin to build'] +not = [ + { contains = ["approve once", "esc cancel"] }, + { contains = ["running tools", "esc to interrupt"] }, + { contains = ["guide devin while it works"] }, +] + +[[rules]] +id = "live_prompt_footer" +state = "idle" +priority = 100 +region = "bottom_non_empty_lines(6)" +visible_idle = true +contains = ["context:"] +line_regex = ['^\s*❭'] +not = [ + { contains = ["approve once", "esc cancel"] }, + { contains = ["running tools", "esc to interrupt"] }, + { contains = ["guide devin while it works"] }, +] diff --git a/crates/rover-agents/src/herdr_manifests/droid.toml b/crates/rover-agents/src/herdr_manifests/droid.toml new file mode 100644 index 0000000..c41d71b --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/droid.toml @@ -0,0 +1,48 @@ +id = "droid" +version = "2026.06.10.1" +min_engine_version = 1 +updated_at = "2026-06-10T00:00:00Z" + +[[rules]] +id = "execute_selection_blocker" +state = "blocked" +priority = 300 +region = "whole_recent" +visible_blocker = true +contains = ["enter to select", "esc to cancel"] +any = [ + { contains = ["↑↓ to navigate"] }, + { contains = ["use ↑↓ to navigate"] }, +] +all = [ + { any = [{ contains = ["> yes, allow"] }, { contains = ["> no, cancel"] }] }, +] + +[[rules]] +id = "selection_menu_blocker" +state = "blocked" +priority = 290 +region = "bottom_non_empty_lines(8)" +visible_blocker = true +contains = ["enter select", "esc cancel"] +any = [ + { contains = ["↑/↓ navigate"] }, + { contains = ["↑↓ navigate"] }, +] + +[[rules]] +id = "spinner_stop_working" +state = "working" +priority = 110 +region = "whole_recent" +visible_working = true +contains = ["esc to stop"] +line_regex = ['^\s*[\u2800-\u28FF]'] + +[[rules]] +id = "stop_hint_working" +state = "working" +priority = 100 +region = "whole_recent" +visible_working = true +contains = ["esc to stop"] diff --git a/crates/rover-agents/src/herdr_manifests/gemini.toml b/crates/rover-agents/src/herdr_manifests/gemini.toml new file mode 100644 index 0000000..9d7a28e --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/gemini.toml @@ -0,0 +1,25 @@ +id = "gemini" +version = "2026.06.10.1" +min_engine_version = 1 +updated_at = "2026-06-10T00:00:00Z" + +[[rules]] +id = "apply_or_allow_change" +state = "blocked" +priority = 300 +region = "whole_recent" +visible_blocker = true +any = [ + { contains = ["│ Apply this change"] }, + { contains = ["│ Allow execution"] }, + { all = [{ contains = ["yes"] }, { any = [{ contains = ["waiting for user confirmation"] }, { contains = ["│ Do you want to proceed"] }, { contains = ["do you want to proceed?"] }] }] }, + { line_regex = ['(?i)^\s*❯.*(yes|allow)'] }, +] + +[[rules]] +id = "esc_cancel_working" +state = "working" +priority = 100 +region = "whole_recent" +visible_working = true +contains = ["esc to cancel"] diff --git a/crates/rover-agents/src/herdr_manifests/github-copilot.toml b/crates/rover-agents/src/herdr_manifests/github-copilot.toml new file mode 100644 index 0000000..57fafdd --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/github-copilot.toml @@ -0,0 +1,45 @@ +id = "copilot" +version = "2026.08.29.1" +min_engine_version = 1 +updated_at = "2026-08-29T00:00:00Z" +aliases = ["github-copilot", "ghcs"] + +[[rules]] +id = "selection_blocker" +state = "blocked" +priority = 300 +region = "whole_recent" +visible_blocker = true +all = [ + { any = [ + { contains = ["esc to cancel"] }, + { contains = ["esc cancel"] }, + ] }, + { any = [ + { contains = ["enter to select"] }, + { contains = ["enter to confirm"] }, + { contains = ["enter to submit"] }, + { contains = ["enter accept"] }, + ] }, +] + +[[rules]] +id = "background_agents_working" +state = "working" +priority = 110 +region = "bottom_non_empty_lines(6)" +visible_working = true +line_regex = ['^\s*◎\s+Waiting for background agents(?:\s|·|$)'] + +[[rules]] +id = "working_cancel_hint" +state = "working" +priority = 100 +region = "whole_recent" +visible_working = true +any = [ + { contains = ["esc to cancel"] }, + { contains = ["esc cancel"] }, + { contains = ["esc again to cancel"] }, + { contains = ["esc interrupt"] } +] diff --git a/crates/rover-agents/src/herdr_manifests/grok.toml b/crates/rover-agents/src/herdr_manifests/grok.toml new file mode 100644 index 0000000..67ba4eb --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/grok.toml @@ -0,0 +1,179 @@ +id = "grok" +version = "2026.09.18.2" +min_engine_version = 3 +updated_at = "2026-09-18T00:00:00Z" +aliases = ["grok-build"] + +# Evidence: Grok Build 0.2.101 source and 1.0.34 live pane reads. +# +# Grok emits OSC 0 titles by default. Idle is "grok" or +# " - grok". During a turn, the configured title gains a braille +# spinner and activity text. Permission prompts add "⚠ Action Required"; +# that prefix blinks while the terminal is unfocused, so visible blocker +# rules outrank the title spinner rule. +# +# Grok also emits OSC 9;4 progress on supported terminals. Herdr retains the +# payload after "9;": "4;1;-1" while busy and "4;0;0" when idle. +# +# Working turns render one live status line directly above the prompt box: +# "⠧ Waiting on subagent… 2.8s 13s ⇣29.7k [stop]" +# "⠴ Explore /tmp/… + 1 more… 5.6s 19s ⇣29.7k [stop]" +# with a braille spinner and a trailing [stop] chip, plus an Esc:cancel +# footer hint. Permission prompts and ask-user-question dialogs replace the +# spinner with "◆" and draw a "┃"-guttered option list: +# "┃ 2 (○) Yes, proceed" +# "┃ z (○) Type your answer here" +# with footer hints "1/3:select │ Ctrl+o:yolo │ Ctrl+c:cancel" (permission) +# or "Esc:unselect │ Tab:scrollback │ Shift+x:dismiss" (question dialog). +# Idle footers end with "Ctrl+.:shortcuts" and never contain "Esc:cancel". +# The startup splash draws its logo with braille characters, so working +# rules must anchor on the [stop] chip, not on a bare spinner glyph. + +[[rules]] +id = "osc_title_blocked" +state = "blocked" +priority = 1300 +region = "osc_title" +visible_blocker = true +contains = ["Action Required"] + +[[rules]] +id = "option_dialog_blocked" +state = "blocked" +priority = 1200 +region = "whole_recent" +visible_blocker = true +line_regex = ['^\s*┃\s+[0-9a-z]+\s+\([●○]\)\s'] + +[[rules]] +id = "permission_hints_blocked" +state = "blocked" +priority = 1190 +region = "bottom_non_empty_lines(2)" +visible_blocker = true +contains = [":select", "ctrl+o:yolo", "ctrl+c:cancel"] + +[[rules]] +id = "question_dialog_hints_blocked" +state = "blocked" +priority = 1185 +region = "bottom_non_empty_lines(2)" +visible_blocker = true +contains = ["tab:scrollback", "shift+x:dismiss"] + +# Pre-0.2.x permission UI kept for older Grok Build releases. +[[rules]] +id = "permission_scope_selector" +state = "blocked" +priority = 1180 +region = "whole_recent" +visible_blocker = true +contains = ["yes, proceed", "no, reject"] +any = [ + { contains = ["use ← → to choose permission whitelist scope"] }, + { contains = ["←/→:scope"] }, +] + +# Grok clears its OSC busy signals while background work runs. The first +# non-empty row is pinned application chrome, where this animated chip shows +# the number of running background tasks and disappears when the count is zero. +[[rules]] +id = "background_work_chip_working" +state = "working" +priority = 1170 +region = "top_non_empty_lines(1)" +visible_working = true +line_regex = ['[⋅:⸬⁙.·]\s+[1-9][0-9]*\s+│'] + +# Grok 1.0.34 moves background counts above the composer and clears OSC +# progress between turns even while these commands are still running. +[[rules]] +id = "background_status_working" +state = "working" +priority = 1165 +region = "bottom_non_empty_lines(12)" +visible_working = true +line_regex = ['^\s*[○◎◉]\s+[1-9][0-9]*\s+(?:commands?|monitors?|loops?|subagents?)(?:\s+·\s+[1-9][0-9]*\s+(?:commands?|monitors?|loops?|subagents?))*\s+still running(?:\s+·\s+send a message to interrupt)?\s*$'] + +[[rules]] +id = "osc_progress_working" +state = "working" +priority = 1150 +region = "osc_progress" +visible_working = true +regex = ['^4;1;-1$'] + +[[rules]] +id = "osc_title_idle" +state = "idle" +priority = 1100 +region = "osc_title" +visible_idle = true +regex = ['(?:^| - )grok$'] +not = [ + { regex = ['[\x{2800}-\x{28FF}]'] }, +] + +# Title items are configurable; omitting "grok" does not imply activity. +# Require a spinner, with OSC progress covering titles that omit it. +[[rules]] +id = "osc_title_working" +state = "working" +priority = 1000 +region = "osc_title" +visible_working = true +regex = ['(?:^|\s)[\x{2801}-\x{28FF}](?:\s|$)'] + +[[rules]] +id = "osc_progress_idle" +state = "idle" +priority = 950 +region = "osc_progress" +visible_idle = true +regex = ['^4;0;0$'] + +# Visible activity outranks idle OSC signals: disabling title updates can +# leave the startup "grok" title unchanged throughout a turn. +[[rules]] +id = "spinner_status_working" +state = "working" +priority = 1160 +region = "whole_recent" +visible_working = true +line_regex = ['^\s*[\x{2801}-\x{28FF}]\s.*\[stop\]\s*$'] + +[[rules]] +id = "esc_cancel_hints_working" +state = "working" +priority = 1155 +region = "bottom_non_empty_lines(2)" +visible_working = true +contains = ["ctrl+.:shortcuts"] +any = [ + { contains = ["esc:cancel"] }, + { contains = ["ctrl+c:cancel"] }, +] + +# Pre-0.2.x working chrome kept for older Grok Build releases. +[[rules]] +id = "waiting_tool_working" +state = "working" +priority = 1140 +region = "whole_recent" +visible_working = true +any = [ + { all = [{ contains = ["ctrl+c:cancel", "ctrl+enter:interject"] }, { contains = ["waiting"] }] }, + { line_regex = ['^\s*[\x{2801}-\x{28FF}]\s+(Run|Read|Search|List)\b'] }, +] + +[[rules]] +id = "prompt_hints_idle" +state = "idle" +priority = 100 +region = "bottom_non_empty_lines(2)" +visible_idle = true +contains = ["ctrl+.:shortcuts"] +not = [ + { contains = ["esc:cancel"] }, + { contains = ["ctrl+c:cancel"] }, +] diff --git a/crates/rover-agents/src/herdr_manifests/hermes.toml b/crates/rover-agents/src/herdr_manifests/hermes.toml new file mode 100644 index 0000000..1754218 --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/hermes.toml @@ -0,0 +1,102 @@ +id = "hermes" +version = "2026.07.24.1" +min_engine_version = 2 +updated_at = "2026-07-24T19:12:54Z" +aliases = ["hermes-agent"] + +[[rules]] +id = "osc_title_blocked" +state = "blocked" +priority = 1100 +region = "osc_title" +visible_blocker = true +regex = ['^⚠[\u{fe0e}\u{fe0f}]?(?:\s|$)'] + +[[rules]] +id = "osc_title_working" +state = "working" +priority = 1050 +region = "osc_title" +visible_working = true +regex = ['^⏳[\u{fe0e}\u{fe0f}]?(?:\s|$)'] + +[[rules]] +id = "dangerous_command_approval" +state = "blocked" +priority = 900 +region = "bottom_non_empty_lines(14)" +visible_blocker = true +any = [ + { contains = ["dangerous"] }, + { contains = ["approval"] }, + { contains = ["allow once", "deny"] }, + { line_regex = ['(?i)^\s*[▸>]?\s*1\.\s*allow'] }, +] +all = [ + { any = [{ contains = ["enter confirm"] }, { contains = ["enter to confirm"] }, { contains = ["↑/↓ to select"] }, { contains = ["show full command"] }] }, +] + +[[rules]] +id = "clarification_prompt" +state = "blocked" +priority = 900 +region = "bottom_non_empty_lines(14)" +visible_blocker = true +any = [ + { contains = ["hermes needs your"] }, + { line_regex = ['^\s*ask\s+\S'] }, + { contains = ["type your answer"] }, +] +all = [ + { any = [{ contains = ["enter confirm"] }, { contains = ["enter to confirm"] }, { contains = ["enter send"] }, { contains = ["press enter"] }, { contains = ["↑/↓ select"] }, { contains = ["↑/↓ to select"] }, { contains = ["other (type"] }] }, +] + +[[rules]] +id = "credential_prompt" +state = "blocked" +priority = 900 +region = "bottom_non_empty_lines(14)" +visible_blocker = true +any = [ + { contains = ["sudo password"] }, + { contains = ["skill setup"] }, + { contains = ["🔑", "for "] }, +] + +[[rules]] +id = "confirmation_prompt" +state = "blocked" +priority = 900 +region = "bottom_non_empty_lines(14)" +visible_blocker = true +all = [ + { any = [{ contains = ["approve once", "cancel"] }, { contains = ["start a new session", "keep going"] }] }, + { any = [{ contains = ["enter to confirm"] }, { contains = ["enter confirm"] }, { contains = ["type 1/2/3"] }, { contains = ["y/n quick"] }] }, +] + +[[rules]] +id = "interrupt_status_working" +state = "working" +priority = 950 +region = "bottom_non_empty_lines(5)" +visible_working = true +any = [ + { contains = ["msg=interrupt"] }, + { contains = ["ctrl+c to interrupt"] }, +] + +[[rules]] +id = "classic_cancel_working" +state = "working" +priority = 500 +region = "bottom_non_empty_lines(5)" +visible_working = true +contains = ["ctrl+c cancel"] + +[[rules]] +id = "osc_title_idle" +state = "idle" +priority = 100 +region = "osc_title" +visible_idle = true +regex = ['^✓[\u{fe0e}\u{fe0f}]?(?:\s|$)'] diff --git a/crates/rover-agents/src/herdr_manifests/kilo.toml b/crates/rover-agents/src/herdr_manifests/kilo.toml new file mode 100644 index 0000000..4ef004e --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/kilo.toml @@ -0,0 +1,24 @@ +id = "kilo" +version = "2026.06.10.1" +min_engine_version = 1 +updated_at = "2026-06-10T00:00:00Z" +aliases = ["kilo-code", "kilo code", "herdr:kilo"] + +[[rules]] +id = "opencode_permission" +state = "blocked" +priority = 300 +region = "whole_recent" +visible_blocker = true +any = [ + { contains = ["△ Permission required"] }, + { contains = ["esc dismiss"], any = [{ contains = ["enter confirm"] }, { contains = ["enter submit"] }, { contains = ["enter toggle"] }], all = [{ any = [{ contains = ["↑↓ select"] }, { contains = ["⇆ tab"] }] }] }, +] + +[[rules]] +id = "esc_interrupt_working" +state = "working" +priority = 100 +region = "whole_recent" +visible_working = true +contains = ["esc interrupt"] diff --git a/crates/rover-agents/src/herdr_manifests/kimi.toml b/crates/rover-agents/src/herdr_manifests/kimi.toml new file mode 100644 index 0000000..b4d0100 --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/kimi.toml @@ -0,0 +1,77 @@ +id = "kimi" +version = "2026.06.10.1" +min_engine_version = 1 +updated_at = "2026-06-10T00:00:00Z" +aliases = ["kimi-code", "kimi code"] + +[[rules]] +id = "current_approval_panel" +state = "blocked" +priority = 400 +region = "whole_recent" +visible_blocker = true +contains = ["↵ confirm"] +any = [ + { contains = ["run this command?"] }, + { contains = ["write this file?"] }, + { contains = ["apply these edits?"] }, + { contains = ["stop this task?"] }, + { contains = ["ready to build with this plan?"] }, + { line_regex = ['(?i)^\s*▶?\s*approve .*\?$'] }, +] +all = [ + { contains = [" choose"] }, + { any = [{ contains = ["approve"] }, { contains = ["reject"] }, { contains = ["revise"] }] }, +] + +[[rules]] +id = "question_panel" +state = "blocked" +priority = 390 +region = "whole_recent" +visible_blocker = true +contains = ["↑↓ select", "esc cancel"] +line_regex = ['^\s*question\s*$', '^\s*\? '] +any = [ + { contains = ["↵ choose"] }, + { contains = ["↵ toggle"] }, + { contains = ["↵ save"] }, +] + +[[rules]] +id = "legacy_approval_panel" +state = "blocked" +priority = 300 +region = "whole_recent" +contains = ["requesting approval", "reject"] +any = [ + { contains = ["approve once"] }, + { contains = ["approve for this session"] }, +] +all = [ + { any = [{ contains = ["1/2/3/4 choose"] }, { contains = ["↵ confirm"] }] }, +] + +[[rules]] +id = "background_agent_status_working" +state = "working" +priority = 120 +region = "bottom_non_empty_lines(3)" +visible_working = true +line_regex = ['(?i)\bkimi[-\w.]*\s+thinking\b.*\[[1-9][0-9]*\s+agents?\s+running\]'] + +[[rules]] +id = "moon_spinner_working" +state = "working" +priority = 100 +region = "whole_recent" +visible_working = true +line_regex = ['^\s*(🌕|🌖|🌗|🌘|🌑|🌒|🌓|🌔)\s*$'] + +[[rules]] +id = "braille_spinner_working" +state = "working" +priority = 90 +region = "whole_recent" +visible_working = true +line_regex = ['(?i)^\s*[\u2800-\u28FF]+\s*(thinking\.\.\.|working\.\.\.|using )'] diff --git a/crates/rover-agents/src/herdr_manifests/kiro.toml b/crates/rover-agents/src/herdr_manifests/kiro.toml new file mode 100644 index 0000000..9d50f93 --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/kiro.toml @@ -0,0 +1,97 @@ +id = "kiro" +version = "2026.09.19.1" +min_engine_version = 2 +updated_at = "2026-09-19T00:00:00Z" +aliases = ["kiro-cli"] + +[[rules]] +id = "live_prompt_idle" +state = "idle" +priority = 1100 +region = "bottom_non_empty_lines(4)" +visible_idle = true +line_regex = ['(?i)^\s*[>›]\s*ask a question or describe a task(?:\s+(?:enter|↵))?\s*$'] + +[[rules]] +id = "tool_approval" +state = "blocked" +priority = 1050 +region = "bottom_non_empty_lines(8)" +visible_blocker = true +any = [ + { all = [ + { regex = ['(?is)(?:^|\n)\s*esc\s+to\s+close\b(?:[^\n]*\bto\s+navigate\b[^\n]*|.*\bto\s+navigate\b.*\bto\s+select\b.*\btab\s+to\s+edit|[^\n]*\benter\s+to\s+see\s+more\s+options)\s*\z'] }, + { any = [ + { line_regex = [ + '(?i)^\s*[>❯]\s*(?:allow|always allow|deny|always deny)\s*$', + '(?i)^\s*(?:[>❯]\s*)?allow\s*$', + '(?i)^\s*(?:[>❯]\s*)?always allow\s*$', + '(?i)^\s*(?:[>❯]\s*)?deny\s*$', + '(?i)^\s*(?:[>❯]\s*)?always deny\s*$', + ] }, + { line_regex = [ + '(?i)^\s*[>❯]\s*(?:yes, single permission|trust, always allow in this session|no \(tab to edit\))\s*$', + '(?i)^\s*(?:[>❯]\s*)?yes, single permission\s*$', + '(?i)^\s*(?:[>❯]\s*)?trust, always allow in this session\s*$', + '(?i)^\s*(?:[>❯]\s*)?no \(tab to edit\)\s*$', + ] }, + { line_regex = [ + '(?i)^\s*[>❯]\s*(?:trust\b.*|entire tool)\s*$', + '(?i)^\s*(?:[>❯]\s*)?(?:trust )?entire tool(?:\s+\([^\n]*\))?(?:\s+(?:session|workspace|always))?\s*$', + ] }, + ] }, + ] }, +] + +[[rules]] +id = "tool_approval_edit" +state = "blocked" +priority = 1045 +region = "whole_recent" +visible_blocker = true +regex = ['(?im)(?:^|\n)[ \t]*[-─]+[ \t]*\n[^\n]*requires\s+approval\s*[·.]\s*modify\s+request[ \t]*\n(?:[ \t]*\n|[^\n]*[^\s─-][^\n]*\n)*?[ \t]*[>›][ \t]*[^\n]*\n(?:[ \t]*\n|[^\n]*[^\s─-][^\n]*\n)*[ \t]*[-─]+[ \t]*\n[ \t]*esc[ \t]+to[ \t]+close[ \t]*\n?\z'] + +[[rules]] +id = "crew_approval" +state = "blocked" +priority = 1040 +region = "bottom_non_empty_lines(8)" +visible_blocker = true +contains = [ + "tool approval", + "approve all pending", + "configure individually (agent monitor)", + "exit (cancel subagents)", +] + +[[rules]] +id = "question_panel" +state = "blocked" +priority = 1030 +region = "bottom_non_empty_lines(8)" +visible_blocker = true +contains = ["to navigate", "to submit", "esc to cancel"] + +[[rules]] +id = "live_working_footer" +state = "working" +priority = 950 +region = "bottom_non_empty_lines(4)" +visible_working = true +contains = ["kiro is working", "type to steer", "ctrl+s to queue"] + +[[rules]] +id = "osc_title_working" +state = "working" +priority = 900 +region = "osc_title" +visible_working = true +regex = ['(?i)^[◐◓◑◒/|\\-]\s+kiro:'] + +[[rules]] +id = "osc_progress_working" +state = "working" +priority = 890 +region = "osc_progress" +visible_working = true +regex = ['^4;3;?$'] diff --git a/crates/rover-agents/src/herdr_manifests/letta.toml b/crates/rover-agents/src/herdr_manifests/letta.toml new file mode 100644 index 0000000..e27e2aa --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/letta.toml @@ -0,0 +1,95 @@ +id = "letta" +version = "2026.08.24.1" +min_engine_version = 3 +updated_at = "2026-08-24T00:00:00Z" +aliases = ["letta-code", "letta code"] + +# Letta keeps completed tool rows and spinners in the transcript. A standalone +# screen spinner is not working evidence. Prefer its live OSC title/progress +# signals, with visible status chrome and running tool rows as fallbacks. +[[rules]] +id = "osc_progress_blocked" +state = "blocked" +priority = 1400 +region = "osc_progress" +visible_blocker = true +regex = ['^4;3(?:;|$)'] + +[[rules]] +id = "osc_title_blocked" +state = "blocked" +priority = 1300 +region = "osc_title" +visible_blocker = true +regex = ['^\[ [!.] \] Action Required(?: \| |$)'] + +[[rules]] +id = "command_approval" +state = "blocked" +priority = 1200 +region = "bottom_non_empty_lines(20)" +visible_blocker = true +contains = ["Run this command?", "Enter to select · Esc to cancel"] + +[[rules]] +id = "osc_title_working" +state = "working" +priority = 900 +region = "osc_title" +visible_working = true +regex = ['(?:^| )[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏](?: |$)'] + +[[rules]] +id = "active_status" +state = "working" +priority = 850 +region = "bottom_non_empty_lines(8)" +visible_working = true +line_regex = ['^\s*(?:\S+\s+)+is(?: \S+)*… \((?:esc to interrupt(?: · .*)?|interrupting)\)\s*$'] + +[[rules]] +id = "running_tool" +state = "working" +priority = 800 +region = "bottom_non_empty_lines(8)" +visible_working = true +line_regex = ['^\s*(?:└\s*)?Running\.\.\.\s*(?:\(.*\))?$'] + +[[rules]] +id = "profile_selector" +state = "unknown" +priority = 700 +region = "bottom_non_empty_lines(12)" +contains = ["Create a new agent (--new)", "Enter select · Esc exit"] + +[[rules]] +id = "composer_input" +state = "unknown" +priority = 150 +region = "bottom_non_empty_lines(8)" +line_regex = ['^\s*›\s+\S.*$'] +not = [ + { line_regex = ['^\s*›\s+Try\s+"'] }, +] + +[[rules]] +id = "composer_idle" +state = "idle" +priority = 100 +region = "bottom_non_empty_lines(8)" +visible_idle = true +any = [ + { line_regex = ['^\s*›\s*$'] }, + { line_regex = ['^\s*›\s+Try\s+"'] }, +] +not = [ + { line_regex = ['^\s*(?:\S+\s+)+is(?: \S+)*… \((?:esc to interrupt(?: · .*)?|interrupting)\)\s*$'] }, + { line_regex = ['^\s*(?:└\s*)?Running\.\.\.\s*(?:\(.*\))?$'] }, +] + +[[rules]] +id = "no_live_state_evidence" +state = "unknown" +priority = 0 +region = "whole_recent" +regex = ['(?s)^.*$'] diff --git a/crates/rover-agents/src/herdr_manifests/maki.toml b/crates/rover-agents/src/herdr_manifests/maki.toml new file mode 100644 index 0000000..5c58404 --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/maki.toml @@ -0,0 +1,68 @@ +id = "maki" +version = "2026.07.09.2" +min_engine_version = 1 +updated_at = "2026-07-09T00:00:00Z" + +# Maki renders a persistent one-line status bar on the bottom row. It starts +# with the mode label "[BUILD]", "[PLAN]", or "[BASH]" when idle and gets a +# leading braille spinner cell while the agent is streaming. Permission +# requests and the plan-complete form replace the input box above the status +# bar. Maki does not set OSC title or OSC 9;4 progress. + +[[rules]] +id = "permission_prompt" +state = "blocked" +priority = 980 +region = "whole_recent" +visible_blocker = true +contains = ["permission required"] +any = [ + { contains = ["y allow", "n deny"] }, + { contains = ["confirm allow"] }, + { contains = ["confirm deny"] }, + { contains = ["enter deny", "esc cancel"] }, +] + +[[rules]] +id = "plan_complete_form" +state = "blocked" +priority = 970 +region = "whole_recent" +visible_blocker = true +contains = ["plan complete", "enter confirm"] +any = [ + { contains = ["space toggle parallel"] }, + { contains = ["edit plan"] }, +] + +[[rules]] +id = "status_bar_spinner_working" +state = "working" +priority = 900 +region = "bottom_non_empty_lines(1)" +visible_working = true +line_regex = ['^( [\x{2800}-\x{28FF}]){1,2} \[(BUILD|PLAN|BASH)\]'] + +[[rules]] +id = "status_bar_idle" +state = "idle" +priority = 850 +region = "bottom_non_empty_lines(1)" +visible_idle = true +line_regex = ['^ \[(BUILD|PLAN|BASH)\]'] + +# On narrow panes the right side of the status bar overwrites the mode label, +# so fall back to the prompt chevron above the input box border. The not-gates +# keep this from matching the streaming placeholder or a status bar that still +# shows the spinner. +[[rules]] +id = "prompt_box_idle" +state = "idle" +priority = 840 +region = "bottom_non_empty_lines(3)" +visible_idle = true +line_regex = ['^❯ '] +not = [ + { contains = ["queue another prompt"] }, + { line_regex = ['^( [\x{2800}-\x{28FF}]){1,2} '] }, +] diff --git a/crates/rover-agents/src/herdr_manifests/muse.toml b/crates/rover-agents/src/herdr_manifests/muse.toml new file mode 100644 index 0000000..5081842 --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/muse.toml @@ -0,0 +1,113 @@ +id = "muse" +version = "2026.08.26.1" +min_engine_version = 2 +updated_at = "2026-08-26T00:00:00Z" +aliases = ["muse-code", "muse-cli"] + +# Evidence: live bottom-buffer captures from Muse Code 0.2.1 in Herdr, using a local +# deterministic Responses provider so each UI state could be held and inspected. +# +# Idle has a `⟩` prompt and `model · effort · cwd` footer. Active turns show +# `◆ Working (... · esc to interrupt)` or another activity label with the same interrupt hint. +# +# Structured questions show two co-occurring footer controls: +# Enter to select · ↑/↓ to move · Tab for an optional note · Esc to interrupt +# Multi-select uses `Enter to toggle` instead. The paired controls distinguish a real picker +# from ordinary transcript text that happens to mention one action. +# +# First launch in an untrusted directory shows `Do you trust this workspace?` together with +# `Trust and continue`. This is a real blocker. User-opened `/theme` and `/skills` menus are +# not blockers; their paired footer controls identify overlays whose prior state must be kept. +# +# Muse 0.2.1 command approval shows `Allow this stage once` together with +# `Always allow in this workspace`. Muse 0.1 used `Allow once` with +# `Allow for this session`. Network approval shows `Yes, proceed` together with +# `Yes, don't ask again this session`. Each approval rule requires a pair because Muse can +# emit any one of these phrases as ordinary assistant text after a completed turn. + +[[rules]] +id = "workspace_trust_blocked" +state = "blocked" +priority = 970 +region = "bottom_non_empty_lines(12)" +visible_blocker = true +contains = ["Do you trust this workspace?"] +any = [ + { contains = ["Trust and continue"] }, + { contains = ["Use Up/Down"] }, +] + +[[rules]] +id = "pick_request_blocked" +state = "blocked" +priority = 950 +region = "bottom_non_empty_lines(8)" +visible_blocker = true +any = [ + { contains = ["Enter to select", "Tab for an optional note"] }, + { contains = ["Enter to toggle", "Esc to interrupt"] }, +] + +[[rules]] +id = "menu_overlay" +state = "unknown" +priority = 940 +region = "bottom_non_empty_lines(8)" +skip_state_update = true +any = [ + { contains = ["enter confirm", "esc go back"] }, + { contains = ["enter save", "esc go back"] }, + { contains = ["space toggle", "esc close", "type filter"] }, +] + +[[rules]] +id = "working_esc_interrupt" +state = "working" +priority = 900 +region = "bottom_non_empty_lines(8)" +visible_working = true +contains = ["esc to interrupt"] +not = [ + { contains = ["Enter to select", "Tab for an optional note"] }, + { contains = ["Enter to toggle", "Esc to interrupt"] }, +] + +[[rules]] +id = "blocked_approval" +state = "blocked" +priority = 850 +region = "bottom_non_empty_lines(8)" +visible_blocker = true +any = [ + { contains = ["Allow this stage once", "Always allow in this workspace"] }, + { contains = ["Allow once", "Allow for this session"] }, + { contains = ["Yes, proceed", "Yes, don't ask again this session"] }, +] + +[[rules]] +id = "idle_prompt" +state = "idle" +priority = 700 +region = "bottom_non_empty_lines(5)" +visible_idle = true +any = [ + { line_regex = ['^\s*⟩\s*$'] }, + { line_regex = ['^\s*⟩\s+\S'] }, +] +not = [ + { contains = ["esc to interrupt"] }, + { contains = ["Enter to select", "Tab for an optional note"] }, + { contains = ["Enter to toggle", "Esc to interrupt"] }, + { contains = ["enter confirm", "esc go back"] }, + { contains = ["enter save", "esc go back"] }, + { contains = ["space toggle", "esc close", "type filter"] }, +] + +[[rules]] +id = "idle_status_fallback" +state = "idle" +priority = 500 +region = "bottom_non_empty_lines(3)" +visible_idle = true +line_regex = ['^\s*\S+ · (none|minimal|low|medium|high|xhigh|ultra) · '] +not = [{ contains = ["esc to interrupt"] }] diff --git a/crates/rover-agents/src/herdr_manifests/opencode.toml b/crates/rover-agents/src/herdr_manifests/opencode.toml new file mode 100644 index 0000000..5245238 --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/opencode.toml @@ -0,0 +1,37 @@ +id = "opencode" +version = "2026.06.10.1" +min_engine_version = 1 +updated_at = "2026-06-10T00:00:00Z" +aliases = ["open-code", "herdr:opencode"] + +[[rules]] +id = "permission_required" +state = "blocked" +priority = 300 +region = "whole_recent" +visible_blocker = true +any = [ + { contains = ["△ Permission required"] }, + { contains = ["esc dismiss"], any = [{ contains = ["enter confirm"] }, { contains = ["enter submit"] }, { contains = ["enter toggle"] }], all = [{ any = [{ contains = ["↑↓ select"] }, { contains = ["⇆ tab"] }] }] }, +] + +[[rules]] +id = "interrupt_hint_working" +state = "working" +priority = 110 +region = "whole_recent" +visible_working = true +any = [ + { contains = ["esc to interrupt"] }, + { contains = ["ctrl+c to interrupt"] }, + { contains = ["press esc to interrupt"] }, + { line_regex = ['(?i).*opencode.*esc (again to )?interrupt'] }, +] + +[[rules]] +id = "progress_bar_working" +state = "working" +priority = 100 +region = "whole_recent" +visible_working = true +regex = ['(■|⬝){4,}'] diff --git a/crates/rover-agents/src/herdr_manifests/pi.toml b/crates/rover-agents/src/herdr_manifests/pi.toml new file mode 100644 index 0000000..77b2d63 --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/pi.toml @@ -0,0 +1,21 @@ +id = "pi" +version = "2026.09.14.1" +min_engine_version = 1 +updated_at = "2026-09-14T00:00:00Z" +aliases = ["herdr:pi"] + +[[rules]] +id = "working_literal" +state = "working" +priority = 100 +region = "whole_recent" +visible_working = true +contains = ["Working..."] + +[[rules]] +id = "working_border" +state = "working" +priority = 100 +region = "bottom_non_empty_lines(12)" +visible_working = true +line_regex = ['^── [⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏] Working ─+$'] diff --git a/crates/rover-agents/src/herdr_manifests/qodercli.toml b/crates/rover-agents/src/herdr_manifests/qodercli.toml new file mode 100644 index 0000000..51ca805 --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/qodercli.toml @@ -0,0 +1,38 @@ +id = "qodercli" +version = "2026.06.10.1" +min_engine_version = 1 +updated_at = "2026-06-10T00:00:00Z" +aliases = ["qoderclicn", "qoder", "qodercn"] + +[[rules]] +id = "confirmation_or_input_blocker" +state = "blocked" +priority = 300 +region = "whole_recent" +visible_blocker = true +any = [ + { contains = ["waiting for user confirmation"], any = [{ contains = ["yes"] }, { contains = ["no"] }, { contains = ["allow"] }, { contains = ["reject"] }] }, + { contains = ["awaiting approval"], any = [{ contains = ["allow"] }, { contains = ["reject"] }] }, + { contains = ["permission required"] }, + { contains = ["allow once or always?"] }, + { contains = ["asking user"] }, + { contains = ["enter your response"] }, + { contains = ["review your answers:"] }, + { contains = ["shell awaiting input"] }, +] + +[[rules]] +id = "cancel_hint_working" +state = "working" +priority = 100 +region = "whole_recent" +visible_working = true +contains = ["(esc to cancel,"] + +[[rules]] +id = "spinner_working" +state = "working" +priority = 90 +region = "whole_recent" +visible_working = true +line_regex = ['^\s*[\u2800-\u28FF]\s+.*\p{Alphabetic}'] diff --git a/crates/rover-agents/src/herdr_manifests/qwen.toml b/crates/rover-agents/src/herdr_manifests/qwen.toml new file mode 100644 index 0000000..bbed237 --- /dev/null +++ b/crates/rover-agents/src/herdr_manifests/qwen.toml @@ -0,0 +1,119 @@ +id = "qwen" +version = "2026.08.14.1" +min_engine_version = 2 +updated_at = "2026-08-14T00:00:00Z" +aliases = ["qwen-code", "qwen code"] + +# Qwen Code keeps its composer visible while responding, so the prompt box is +# not idle evidence by itself. Its status-prefixed terminal titles are the +# primary locale-independent signals when ui.showStatusInTitle is enabled. +# Exact screen fallbacks cover built-in locales and narrow terminals. OSC 9;4 +# is supplemental because Qwen emits it only while a tool is executing and +# only in terminals that advertise progress support. + +[[rules]] +id = "osc_title_blocked" +state = "blocked" +priority = 1200 +region = "osc_title" +visible_blocker = true +regex = ['^\x{2733}\x{FE0E}? '] + +[[rules]] +id = "osc_title_working" +state = "working" +priority = 1100 +region = "osc_title" +visible_working = true +regex = ['^\x{25D0}\x{FE0E}? '] + +[[rules]] +id = "waiting_for_confirmation" +state = "blocked" +priority = 1000 +region = "bottom_non_empty_lines(20)" +visible_blocker = true +line_regex = ['^\s*⠏\s+.*\.\.\.\s*$'] +any = [ + { contains = ["Waiting for user confirmation..."] }, + { contains = ["等待用户确认..."] }, + { contains = ["等待用戶確認..."] }, + { contains = ["Warten auf Benutzerbestätigung..."] }, + { contains = ["En attente de la confirmation de l'utilisateur..."] }, + { contains = ["ユーザーの確認を待っています..."] }, + { contains = ["Aguardando confirmação do usuário..."] }, + { contains = ["Ожидание подтверждения от пользователя..."] }, + { contains = ["Esperant la confirmació de l'usuari..."] }, +] + +[[rules]] +id = "tool_confirmation" +state = "blocked" +priority = 990 +region = "bottom_non_empty_lines(20)" +visible_blocker = true +contains = ["yes, allow once"] +any = [ + { contains = ["apply this change?"] }, + { contains = ["allow execution of:"] }, + { contains = ["allow execution of mcp tool"] }, + { contains = ["do you want to proceed?"] }, + { contains = ["shell command execution"] }, +] + +[[rules]] +id = "question_dialog" +state = "blocked" +priority = 980 +region = "bottom_non_empty_lines(20)" +visible_blocker = true +line_regex = [ + '^\s*[❯›]\s*(?:\[(?: |✓)\]\s*)?\d+\.\s+', + '^\s*↑/↓\s*:.*(?:Enter|Return)\s*:', +] + +[[rules]] +id = "folder_trust_dialog" +state = "blocked" +priority = 970 +region = "bottom_non_empty_lines(20)" +visible_blocker = true +contains = ["do you trust this folder?", "trust folder (", "don't trust (esc)"] + +[[rules]] +id = "cancel_hint_working" +state = "working" +priority = 900 +region = "bottom_non_empty_lines(8)" +visible_working = true +line_regex = ['^\s*(?:[⠁-⣿]|\.{1,2})\s+.*\(\d+(?:m(?:\s+\d+s)?|s).*\s·\sesc to cancel\)\s*$'] + +[[rules]] +id = "narrow_cancel_hint_working" +state = "working" +priority = 890 +region = "bottom_non_empty_lines(8)" +visible_working = true +line_regex = ['^\s*\(\d+(?:m(?:\s+\d+s)?|s)\s·\sesc to cancel\)\s*$'] + +[[rules]] +id = "osc_tool_progress_working" +state = "working" +priority = 850 +region = "osc_progress" +visible_working = true +regex = ['^4;3(?:;|$)'] + +[[rules]] +id = "composer_idle" +state = "idle" +priority = 100 +region = "bottom_non_empty_lines(30)" +visible_idle = true +line_regex = ['^\s*>\s*(?:type\s*)?.*$'] +any = [ + { contains = ["type your message"] }, + { contains = ["your message or @path/to/file"] }, + { contains = ["@path/to/file"] }, + { contains = ["type", "mes", "sage", "@pat", "h/to", "/fil"] }, +] diff --git a/crates/rover-agents/src/lib.rs b/crates/rover-agents/src/lib.rs new file mode 100644 index 0000000..693e1cc --- /dev/null +++ b/crates/rover-agents/src/lib.rs @@ -0,0 +1,422 @@ +//! Declarative agent profile registry. +//! +//! A descriptor reports the adapter surface Rover declares. It does not +//! certify that a provider is installed, authenticated, or live-compatible. + +use serde::Serialize; +use std::io; + +mod detect; +mod herdr_manifest; +mod lifecycle; +mod process_identity; +mod rollup; +mod sessions; +mod transcript; + +pub use detect::{ + detect_agent, AgentState, Confidence, DetectionEvidence, DetectionRequest, DetectionResult, + DetectionVisibility, ProcessSample, ScreenManifest, ScreenRule, +}; +pub use herdr_manifest::{ + bundled_herdr_manifests, bundled_manifest_for_executable, evaluate_herdr_manifest, + parse_herdr_manifest, HerdrManifest, +}; +pub use lifecycle::{AuthorityDecision, LifecycleAuthority, LifecycleEvent, LifecycleResult}; +pub use process_identity::{ + identify_foreground_process_group, ProcessArgvSample, ProcessGroupIdentity, +}; +pub use rollup::{ + rollup_pane, rollup_tab, rollup_workspace, AgentObservation, DisplayState, Rollup, +}; +pub use sessions::{ + clear_session_binding, list_session_bindings, load_session_binding, save_session_binding, + AgentSessionBinding, AgentSessionSource, SessionBindingError, +}; +pub use transcript::{parse_transcript, AgentResult}; + +/// A declared CLI/PTY adapter and its evidence boundary. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)] +pub struct Descriptor { + /// Stable adapter profile name. + pub name: &'static str, + /// Expected executable, or `user-defined` for generic adapters. + pub executable: &'static str, + /// The adapter uses a structured output contract. + pub structured: bool, + /// The adapter requires interactive terminal input. + pub interactive: bool, + /// Whether Rover mediates provider permission prompts. + pub permission_mediation: bool, + /// Current validation evidence and limitations. + pub validation: &'static str, +} + +/// Options for constructing one safe, explicit agent process invocation. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct InvocationOptions { + /// Optional native CLI executable override. + pub executable: Option, + /// Optional provider model name. + pub model: Option, + /// Whether the native CLI may make workspace edits. + pub write: bool, + /// Claude CLI tool allowlist. + pub allowed_tools: Vec, + /// Optional Claude CLI turn limit; nonpositive means unspecified. + pub max_turns: i32, + /// Generic profile argv template. + pub argv: Vec, + /// Generic profile repair argv template. + pub repair_argv: Vec, +} + +const PROFILES: [Descriptor; 4] = [ + Descriptor { + name: "generic-headless", + executable: "user-defined", + structured: false, + interactive: false, + permission_mediation: false, + validation: "local command integration", + }, + Descriptor { + name: "generic-pty", + executable: "user-defined", + structured: false, + interactive: true, + permission_mediation: false, + validation: "Linux PTY; local advisory only", + }, + Descriptor { + name: "codex-exec", + executable: "codex", + structured: true, + interactive: false, + permission_mediation: false, + validation: "documented CLI; fixture conformance; live-account testing required", + }, + Descriptor { + name: "claude-print", + executable: "claude", + structured: true, + interactive: false, + permission_mediation: false, + validation: "documented CLI; fixture conformance; live-account testing required", + }, +]; + +/// Return every declared adapter profile in stable order. +#[must_use] +pub const fn profiles() -> &'static [Descriptor] { + &PROFILES +} + +/// Resolve one declared profile, failing closed for unknown names. +/// +/// # Errors +/// +/// Returns `NotFound` when `name` is not a declared adapter profile. +pub fn profile(name: &str) -> io::Result<&'static Descriptor> { + PROFILES + .iter() + .find(|descriptor| descriptor.name == name) + .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "unknown agent adapter")) +} + +/// Build argv for one declared profile without invoking the executable. +/// +/// The returned vector includes the executable as element zero. Generic +/// profiles use the caller-provided argv and replace exact `{{objective}}` +/// arguments. Native adapters emit only reviewed flags. +/// +/// # Errors +/// +/// Returns `NotFound` for unknown profiles and `InvalidInput` for missing +/// generic argv or malformed native executable, model, or tool values. +pub fn argv( + profile_name: &str, + options: &InvocationOptions, + prompt: &str, + repair: bool, +) -> io::Result> { + match profile_name { + "generic-headless" | "generic-pty" => { + let template = if repair { + &options.repair_argv + } else { + &options.argv + }; + if template.is_empty() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "executable required", + )); + } + Ok(template + .iter() + .map(|argument| { + if argument == "{{objective}}" { + prompt.to_owned() + } else { + argument.clone() + } + }) + .collect()) + } + "codex-exec" => { + let executable = native_executable(options.executable.as_deref(), "codex")?; + validate_model(options.model.as_deref())?; + let sandbox = if options.write { + "workspace-write" + } else { + "read-only" + }; + let mut result = vec![executable, "exec".to_owned(), "--json".to_owned()]; + result.push("--sandbox".to_owned()); + result.push(sandbox.to_owned()); + if let Some(model) = nonempty(options.model.as_deref()) { + result.push("--model".to_owned()); + result.push(model.to_owned()); + } + result.push("--".to_owned()); + result.push(prompt.to_owned()); + Ok(result) + } + "claude-print" => { + let executable = native_executable(options.executable.as_deref(), "claude")?; + validate_model(options.model.as_deref())?; + for tool in &options.allowed_tools { + if !valid_tool(tool) { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "invalid allowed tool", + )); + } + } + let mode = if options.write { "acceptEdits" } else { "plan" }; + let mut result = vec![ + executable, + "--print".to_owned(), + "--output-format".to_owned(), + "stream-json".to_owned(), + "--verbose".to_owned(), + "--permission-mode".to_owned(), + mode.to_owned(), + ]; + if options.max_turns > 0 { + result.push("--max-turns".to_owned()); + result.push(options.max_turns.to_string()); + } + if let Some(model) = nonempty(options.model.as_deref()) { + result.push("--model".to_owned()); + result.push(model.to_owned()); + } + if !options.allowed_tools.is_empty() { + result.push("--allowedTools".to_owned()); + result.push(options.allowed_tools.join(",")); + } + result.push("--".to_owned()); + result.push(prompt.to_owned()); + Ok(result) + } + _ => Err(io::Error::new( + io::ErrorKind::NotFound, + "unknown agent adapter", + )), + } +} + +fn native_executable(value: Option<&str>, default: &str) -> io::Result { + let executable = value.filter(|value| !value.is_empty()).unwrap_or(default); + if executable.len() > 1024 + || executable.starts_with('-') + || executable == "." + || executable == ".." + || executable.contains('\0') + || executable.split('/').any(|component| component == "..") + { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "invalid agent executable", + )); + } + Ok(executable.to_owned()) +} + +fn validate_model(model: Option<&str>) -> io::Result<()> { + if let Some(model) = nonempty(model) { + if model.len() > 256 + || model.starts_with('-') + || !model + .bytes() + .all(|byte| byte.is_ascii_alphanumeric() || b"._:-".contains(&byte)) + { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "invalid agent model", + )); + } + } + Ok(()) +} + +fn valid_tool(tool: &str) -> bool { + !tool.is_empty() + && tool.len() <= 128 + && !tool.starts_with('-') + && tool + .bytes() + .all(|byte| byte.is_ascii_alphanumeric() || b"._-".contains(&byte)) +} + +fn nonempty(value: Option<&str>) -> Option<&str> { + value.filter(|value| !value.is_empty()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn declares_go_compatible_profiles_and_evidence_labels() { + assert_eq!(profiles().len(), 4); + assert_eq!( + profile("generic-headless").unwrap().executable, + "user-defined" + ); + assert!(profile("generic-pty").unwrap().interactive); + assert_eq!(profile("codex-exec").unwrap().executable, "codex"); + assert_eq!(profile("claude-print").unwrap().executable, "claude"); + for descriptor in profiles() { + assert!(!descriptor.permission_mediation); + } + assert!(profile("codex-exec") + .unwrap() + .validation + .contains("live-account testing required")); + } + + #[test] + fn unknown_profile_is_not_downgraded_to_a_generic_adapter() { + assert_eq!( + profile("custom-unknown").unwrap_err().kind(), + io::ErrorKind::NotFound + ); + assert_eq!( + profile("custom-unknown").unwrap_err().to_string(), + "unknown agent adapter" + ); + } + + #[test] + fn generic_profiles_replace_only_exact_objective_arguments() { + let options = InvocationOptions { + argv: vec!["agent".to_owned(), "{{objective}}".to_owned()], + repair_argv: vec!["agent".to_owned(), "repair".to_owned()], + ..InvocationOptions::default() + }; + assert_eq!( + argv("generic-headless", &options, "fix issue", false).unwrap(), + ["agent", "fix issue"] + ); + assert_eq!( + argv("generic-pty", &options, "unused", true).unwrap(), + ["agent", "repair"] + ); + assert!(argv( + "generic-headless", + &InvocationOptions::default(), + "x", + false + ) + .is_err()); + } + + #[test] + fn native_argv_matches_documented_modes_and_keeps_prompt_after_separator() { + let codex = argv( + "codex-exec", + &InvocationOptions { + model: Some("gpt-5.4".to_owned()), + ..InvocationOptions::default() + }, + "--dangerous prompt", + false, + ) + .unwrap(); + assert_eq!( + codex, + [ + "codex", + "exec", + "--json", + "--sandbox", + "read-only", + "--model", + "gpt-5.4", + "--", + "--dangerous prompt" + ] + ); + + let claude = argv( + "claude-print", + &InvocationOptions { + write: true, + allowed_tools: vec!["Read".to_owned(), "Edit".to_owned()], + max_turns: 4, + ..InvocationOptions::default() + }, + "update file", + false, + ) + .unwrap(); + assert!(claude + .windows(2) + .any(|pair| pair == ["--permission-mode", "acceptEdits"])); + assert!(claude + .windows(2) + .any(|pair| pair == ["--allowedTools", "Read,Edit"])); + assert!(!claude.iter().any(|argument| argument.contains("bypass"))); + assert_eq!(claude.last().map(String::as_str), Some("update file")); + } + + #[test] + fn native_argv_rejects_option_injection_and_malformed_values() { + for executable in ["-evil", "../evil", "dir/../evil", ".", "bad\0path"] { + assert!(argv( + "codex-exec", + &InvocationOptions { + executable: Some(executable.to_owned()), + ..InvocationOptions::default() + }, + "prompt", + false + ) + .is_err()); + } + for model in ["-evil", "model with spaces", "m,evil"] { + assert!(argv( + "codex-exec", + &InvocationOptions { + model: Some(model.to_owned()), + ..InvocationOptions::default() + }, + "prompt", + false + ) + .is_err()); + } + assert!(argv( + "claude-print", + &InvocationOptions { + allowed_tools: vec!["--evil".to_owned()], + ..InvocationOptions::default() + }, + "prompt", + false + ) + .is_err()); + } +} diff --git a/crates/rover-agents/src/lifecycle.rs b/crates/rover-agents/src/lifecycle.rs new file mode 100644 index 0000000..8a89322 --- /dev/null +++ b/crates/rover-agents/src/lifecycle.rs @@ -0,0 +1,269 @@ +//! Sequenced lifecycle authority over screen-based estimates. +//! +//! This bounded in-memory registry is scoped to one pane by its owner. It +//! provides source ordering and release, not transport, persistence, or hooks. + +use std::collections::BTreeMap; +use std::io; + +use crate::AgentState; + +const MAX_SOURCES_PER_PANE: usize = 32; +const MAX_SOURCE_ID_BYTES: usize = 80; + +/// One source-scoped lifecycle event. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum LifecycleEvent { + /// Report semantic state with a monotonically increasing source sequence. + Report { + source: String, + sequence: u64, + state: AgentState, + }, + /// Release a source with a monotonically increasing source sequence. + Release { source: String, sequence: u64 }, +} + +/// Result of applying one ordered lifecycle event. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum LifecycleResult { + /// Event became the latest state for its source. + Applied, + /// Event was valid but its sequence was older than or equal to the latest. + IgnoredOutOfOrder, +} + +/// Effective state, authority owner, and screen fallback disposition. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AuthorityDecision { + /// Active source state, screen state, or unknown on active-source conflict. + pub state: AgentState, + /// Sole active lifecycle source; absent during fallback or conflict. + pub source: Option, + /// Whether lifecycle reports suppress screen fallback. + pub screen_suppressed: bool, + /// Whether multiple active sources made the state ambiguous. + pub source_conflict: bool, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +struct SourceState { + last_sequence: u64, + active: bool, + state: AgentState, +} + +/// Bounded lifecycle authority registry for one pane. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct LifecycleAuthority { + sources: BTreeMap, +} + +impl LifecycleAuthority { + /// Apply a source-sequenced report or release. + /// + /// Reports accept only idle, working, and blocked. High-water marks remain + /// after release for this registry's lifetime to prevent delayed revival. + /// + /// # Errors + /// + /// Returns `InvalidInput` for unsafe source IDs, unsupported report states, + /// or source-limit overflow. + pub fn apply(&mut self, event: LifecycleEvent) -> io::Result { + let (source, sequence, state) = match event { + LifecycleEvent::Report { + source, + sequence, + state, + } => { + if !matches!( + state, + AgentState::Idle | AgentState::Working | AgentState::Blocked + ) { + return Err(invalid_input("unsupported lifecycle report state")); + } + (source, sequence, Some(state)) + } + LifecycleEvent::Release { source, sequence } => (source, sequence, None), + }; + if !valid_source(&source) { + return Err(invalid_input("invalid lifecycle source")); + } + if !self.sources.contains_key(&source) && self.sources.len() == MAX_SOURCES_PER_PANE { + return Err(invalid_input("lifecycle source limit exceeded")); + } + if self + .sources + .get(&source) + .is_some_and(|record| sequence <= record.last_sequence) + { + return Ok(LifecycleResult::IgnoredOutOfOrder); + } + let record = self.sources.entry(source).or_insert(SourceState { + last_sequence: sequence, + active: false, + state: AgentState::Unknown, + }); + record.last_sequence = sequence; + if let Some(state) = state { + record.active = true; + record.state = state; + } else { + record.active = false; + record.state = AgentState::Unknown; + } + Ok(LifecycleResult::Applied) + } + + /// Resolve lifecycle authority over a screen estimate. + /// + /// One active lifecycle source suppresses screen state. Multiple active + /// sources also suppress fallback but resolve to unknown. With no active + /// source, the provided screen state is returned unchanged. + #[must_use] + pub fn resolve(&self, screen_state: AgentState) -> AuthorityDecision { + let mut active = self.sources.iter().filter(|(_, record)| record.active); + let Some((source, record)) = active.next() else { + return AuthorityDecision { + state: screen_state, + source: None, + screen_suppressed: false, + source_conflict: false, + }; + }; + if active.next().is_some() { + return AuthorityDecision { + state: AgentState::Unknown, + source: None, + screen_suppressed: true, + source_conflict: true, + }; + } + AuthorityDecision { + state: record.state, + source: Some(source.clone()), + screen_suppressed: true, + source_conflict: false, + } + } +} + +fn valid_source(value: &str) -> bool { + !value.is_empty() + && value.len() <= MAX_SOURCE_ID_BYTES + && value.chars().all(|character| { + character.is_ascii_alphanumeric() || matches!(character, ':' | '.' | '_' | '-') + }) +} + +fn invalid_input(message: &'static str) -> io::Error { + io::Error::new(io::ErrorKind::InvalidInput, message) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn report(source: &str, sequence: u64, state: AgentState) -> LifecycleEvent { + LifecycleEvent::Report { + source: source.to_owned(), + sequence, + state, + } + } + + fn release(source: &str, sequence: u64) -> LifecycleEvent { + LifecycleEvent::Release { + source: source.to_owned(), + sequence, + } + } + + #[test] + fn authoritative_report_suppresses_screen_until_release() { + let mut registry = LifecycleAuthority::default(); + registry + .apply(report("hook:agent", 1, AgentState::Working)) + .unwrap(); + let decision = registry.resolve(AgentState::Blocked); + assert_eq!(decision.state, AgentState::Working); + assert_eq!(decision.source.as_deref(), Some("hook:agent")); + assert!(decision.screen_suppressed); + registry.apply(release("hook:agent", 2)).unwrap(); + let decision = registry.resolve(AgentState::Blocked); + assert_eq!(decision.state, AgentState::Blocked); + assert!(!decision.screen_suppressed); + } + + #[test] + fn old_events_cannot_reorder_state_or_revive_released_authority() { + let mut registry = LifecycleAuthority::default(); + registry + .apply(report("hook:agent", 4, AgentState::Working)) + .unwrap(); + assert_eq!( + registry + .apply(report("hook:agent", 3, AgentState::Blocked)) + .unwrap(), + LifecycleResult::IgnoredOutOfOrder + ); + registry.apply(release("hook:agent", 5)).unwrap(); + assert_eq!( + registry + .apply(report("hook:agent", 4, AgentState::Working)) + .unwrap(), + LifecycleResult::IgnoredOutOfOrder + ); + assert_eq!(registry.resolve(AgentState::Idle).state, AgentState::Idle); + } + + #[test] + fn equal_sequence_is_ignored_and_zero_is_valid_first_sequence() { + let mut registry = LifecycleAuthority::default(); + registry + .apply(report("hook:agent", 0, AgentState::Idle)) + .unwrap(); + assert_eq!( + registry + .apply(report("hook:agent", 0, AgentState::Working)) + .unwrap(), + LifecycleResult::IgnoredOutOfOrder + ); + assert_eq!( + registry.resolve(AgentState::Blocked).state, + AgentState::Idle + ); + } + + #[test] + fn multiple_active_sources_suppress_screen_and_fail_closed() { + let mut registry = LifecycleAuthority::default(); + registry + .apply(report("hook:a", 1, AgentState::Working)) + .unwrap(); + registry + .apply(report("hook:b", 1, AgentState::Working)) + .unwrap(); + let decision = registry.resolve(AgentState::Idle); + assert_eq!(decision.state, AgentState::Unknown); + assert!(decision.screen_suppressed); + assert!(decision.source_conflict); + } + + #[test] + fn unsafe_states_sources_and_source_overflow_are_rejected() { + let mut registry = LifecycleAuthority::default(); + assert!(registry + .apply(report("hook", 1, AgentState::Unknown)) + .is_err()); + assert!(registry + .apply(report("bad source", 1, AgentState::Idle)) + .is_err()); + for index in 0..MAX_SOURCES_PER_PANE { + registry + .apply(release(&format!("hook:{index}"), 1)) + .unwrap(); + } + assert!(registry.apply(release("hook:overflow", 1)).is_err()); + } +} diff --git a/crates/rover-agents/src/process_identity.rs b/crates/rover-agents/src/process_identity.rs new file mode 100644 index 0000000..ea3785f --- /dev/null +++ b/crates/rover-agents/src/process_identity.rs @@ -0,0 +1,636 @@ +//! Bounded foreground process-group identity resolution. +//! +//! Wrapper rules were traced against the pinned Herdr `src/detect/mod.rs` in +//! `docs/design/UPSTREAM_SOURCE_AUDIT.md`. Only argv observed from Rover's +//! owned PTY sampler is accepted; caller-supplied command text is not used. + +use crate::bundled_manifest_for_executable; + +/// Process executable and bounded argv evidence from an owned PTY sampler. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ProcessArgvSample { + /// Executable basename obtained from the platform process record. + pub executable: String, + /// Argument vector, when the platform could read it safely. + pub argv: Option>, +} + +/// Result of resolving the group identity by Herdr-compatible priority. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ProcessGroupIdentity { + /// Canonical executable label for the selected agent. + pub executable: String, + /// True when equally ranked processes resolve to different agents. + pub ambiguous: bool, + /// Every equally ranked candidate when `ambiguous`, starting with + /// `executable`. Empty otherwise. Lets a caller report *which* agents + /// disagreed instead of only that they did. + pub conflicting: Vec, +} + +/// Resolve direct agent executables and audited runtime/shell wrappers. +/// +/// A recognized leader wins. Otherwise wrapper candidates outrank direct +/// executable names; equal-ranked disagreement is marked ambiguous so callers +/// can preserve an unknown identity instead of selecting arbitrarily. +#[must_use] +pub fn identify_foreground_process_group( + leader: Option<&ProcessArgvSample>, + processes: &[ProcessArgvSample], +) -> Option { + if let Some(leader) = leader { + if let Some(candidate) = normalized_process_name(leader) { + if candidate != "letta" || letta_is_interactive(leader) { + return Some(ProcessGroupIdentity { + executable: candidate, + ambiguous: false, + conflicting: Vec::new(), + }); + } + } + } + + let mut best: Option<(u8, String, Vec)> = None; + for process in processes { + let Some(candidate) = normalized_process_name(process) else { + continue; + }; + if candidate == "letta" && !letta_is_interactive(process) { + continue; + } + let score = process_priority(process, &candidate); + match &mut best { + Some((best_score, _, _)) if *best_score > score => {} + Some((best_score, best_name, tied)) if *best_score == score => { + if best_name != &candidate && !tied.iter().any(|held| held == &candidate) { + tied.push(candidate); + } + } + _ => best = Some((score, candidate, Vec::new())), + } + } + let (_, executable, tied) = best?; + let ambiguous = !tied.is_empty(); + let conflicting = if ambiguous { + let mut all = Vec::with_capacity(tied.len() + 1); + all.push(executable.clone()); + all.extend(tied); + all + } else { + Vec::new() + }; + Some(ProcessGroupIdentity { + executable, + ambiguous, + conflicting, + }) +} + +fn normalized_process_name(process: &ProcessArgvSample) -> Option { + let effective = process + .argv + .as_ref() + .and_then(|argv| argv.first()) + .map_or(process.executable.as_str(), String::as_str); + let effective_name = lookup_name(effective); + + if is_generic_runtime_or_shell(&effective_name) { + if let Some(candidate) = + wrapped_agent_name_from_runtime_argv(&effective_name, process.argv.as_deref()) + { + return Some(candidate); + } + } + if let Some(candidate) = agent_name_from_path_token(effective) { + return Some(candidate); + } + + if let Some(argv) = process.argv.as_deref() { + if let Some(runtime) = argv.first() { + let runtime_name = lookup_name(runtime); + if matches!(runtime_name.as_str(), "node" | "bun") { + if let Some(candidate) = wrapped_agent_name_from_runtime_argv(runtime, Some(argv)) { + if matches!(candidate.as_str(), "qwen" | "cline" | "letta") { + return Some(candidate); + } + } + } + } + if let Some(candidate) = argv.first().and_then(|arg| agent_name_from_path_token(arg)) { + return Some(candidate); + } + } + None +} + +fn wrapped_agent_name_from_runtime_argv(runtime: &str, argv: Option<&[String]>) -> Option { + let argv = argv?; + match lookup_name(runtime).as_str() { + "node" => cursor_agent_from_node_argv(argv) + .or_else(|| script_agent_from_argv(argv, &["-e", "--eval", "-p", "--print"], &[])), + "bun" => script_agent_from_argv(argv, &["-e", "--eval", "-p", "--print"], &[]), + name if is_python_runtime(name) => script_agent_from_argv(argv, &["-c"], &["-m"]), + "sh" | "bash" | "zsh" | "fish" => shell_command_agent_from_argv(argv), + "cmd" => command_agent_from_argv(argv), + "powershell" | "pwsh" => powershell_agent_from_argv(argv), + _ => None, + } +} + +fn cursor_agent_from_node_argv(argv: &[String]) -> Option { + let runtime = argv.first()?; + let script = argv.get(1)?; + let runtime_parent = parent_and_basename(runtime)?; + let script_parent = parent_and_basename(script)?; + if !runtime_parent.1.eq_ignore_ascii_case("node.exe") + || !script_parent.1.eq_ignore_ascii_case("index.js") + || !runtime_parent.0.eq_ignore_ascii_case(script_parent.0) + { + return None; + } + let parts = path_components(runtime_parent.0); + (parts.len() >= 3 + && parts[parts.len() - 3].eq_ignore_ascii_case("cursor-agent") + && parts[parts.len() - 2].eq_ignore_ascii_case("versions") + && parts[parts.len() - 1].chars().any(|ch| ch.is_ascii_digit())) + .then(|| "cursor-agent".to_owned()) +} + +fn parent_and_basename(path: &str) -> Option<(&str, &str)> { + let split = path.rfind(['/', '\\'])?; + let parent = path[..split].trim_end_matches(['/', '\\']); + let basename = &path[split + 1..]; + (!parent.is_empty() && !basename.is_empty()).then_some((parent, basename)) +} + +fn script_agent_from_argv( + argv: &[String], + eval_flags: &[&str], + module_flags: &[&str], +) -> Option { + let mut rest = argv.iter().skip(1); + while let Some(arg) = rest.next() { + if arg == "--" { + return rest + .next() + .and_then(|token| agent_name_from_path_token(token)); + } + if flag_matches(arg, eval_flags) || flag_matches(arg, module_flags) { + return None; + } + if arg.starts_with('-') { + if option_takes_value(arg) { + let _ = rest.next(); + } + continue; + } + return agent_name_from_path_token(arg); + } + None +} + +fn shell_command_agent_from_argv(argv: &[String]) -> Option { + let mut rest = argv.iter().skip(1); + while let Some(arg) = rest.next() { + if arg == "-c" { + return rest.next().and_then(|command| command_text_agent(command)); + } + if arg.starts_with('-') { + continue; + } + break; + } + None +} + +fn command_agent_from_argv(argv: &[String]) -> Option { + let mut rest = argv.iter().skip(1); + while let Some(arg) = rest.next() { + match arg.trim_matches('"').to_ascii_lowercase().as_str() { + "/c" | "/k" => return rest.next().and_then(|command| command_text_agent(command)), + _ => {} + } + } + None +} + +fn powershell_agent_from_argv(argv: &[String]) -> Option { + let mut rest = argv.iter().skip(1); + while let Some(arg) = rest.next() { + match arg.trim_matches('"').to_ascii_lowercase().as_str() { + "-file" | "-f" | "/file" => { + return rest + .next() + .and_then(|path| agent_name_from_path_token(path)); + } + "-command" | "-c" | "/command" | "/c" => { + return rest.next().and_then(|command| command_text_agent(command)); + } + "-encodedcommand" | "-enc" | "/encodedcommand" | "/enc" => return None, + _ => {} + } + } + None +} + +fn command_text_agent(command: &str) -> Option { + let mut rest = command; + while let Some((token, next)) = command_text_token(rest) { + let token = token.trim(); + if ["&", ".", "call"] + .iter() + .any(|prefix| token.eq_ignore_ascii_case(prefix)) + { + rest = next; + continue; + } + return agent_name_from_path_token(token); + } + None +} + +fn command_text_token(input: &str) -> Option<(&str, &str)> { + let input = input.trim_start(); + let quote = input.chars().next()?; + if quote == '"' || quote == '\'' { + let start = quote.len_utf8(); + if let Some(end) = input[start..].find(quote) { + let end = start + end; + return Some((&input[start..end], &input[end + quote.len_utf8()..])); + } + return Some((&input[start..], "")); + } + let end = input.find(char::is_whitespace).unwrap_or(input.len()); + Some((&input[..end], &input[end..])) +} + +fn letta_is_interactive(process: &ProcessArgvSample) -> bool { + let Some(argv) = process.argv.as_deref() else { + return true; + }; + let Some(entrypoint) = letta_entrypoint_index(argv) else { + return true; + }; + let rest = &argv[entrypoint + 1..]; + if rest.iter().any(|arg| { + let option = arg.split_once('=').map_or(arg.as_str(), |(name, _)| name); + matches!( + option, + "-p" | "--print" + | "--prompt" + | "--json" + | "--stream-json" + | "--run" + | "--disable-memory-guard" + | "--output-format" + | "--input-format" + | "--include-partial-messages" + | "--from-agent" + | "--environment" + | "--env" + | "--pre-load-skills" + | "--tags" + | "--ephemeral" + | "--stateless" + | "--max-turns" + | "--memfs-startup" + | "-h" + | "--help" + | "-v" + | "--version" + | "--info" + | "--update" + | "--upgrade" + ) + }) { + return false; + } + first_letta_cli_argument(rest).is_none_or(|argument| argument.starts_with('-')) +} + +fn letta_entrypoint_index(argv: &[String]) -> Option { + if argv + .first() + .is_some_and(|arg| agent_name_from_path_token(arg).as_deref() == Some("letta")) + { + return Some(0); + } + let runtime = lookup_name(argv.first()?); + if !matches!(runtime.as_str(), "node" | "bun") { + return None; + } + let mut index = 1; + while let Some(arg) = argv.get(index) { + if arg == "--" { + return argv + .get(index + 1) + .is_some_and(|arg| agent_name_from_path_token(arg).as_deref() == Some("letta")) + .then_some(index + 1); + } + if flag_matches(arg, &["-e", "--eval", "-p", "--print"]) { + return None; + } + if arg.starts_with('-') { + index += if option_takes_value(arg) { 2 } else { 1 }; + continue; + } + return (agent_name_from_path_token(arg).as_deref() == Some("letta")).then_some(index); + } + None +} + +fn first_letta_cli_argument(args: &[String]) -> Option<&str> { + let mut args = args.iter(); + while let Some(arg) = args.next() { + if arg == "--backend" { + let _ = args.next(); + continue; + } + if arg.starts_with("--backend=") { + continue; + } + return Some(arg); + } + None +} + +fn agent_name_from_path_token(token: &str) -> Option { + let token = token.trim_matches(['"', '\'']); + if token.is_empty() || token.starts_with('-') { + return None; + } + let base = lookup_name(token); + if let Some(agent) = canonical_manifest_agent(&base) { + return Some(agent); + } + if base + .strip_prefix("muse-bin-") + .is_some_and(|suffix| suffix.starts_with(|ch: char| ch.is_ascii_digit())) + { + return Some("muse".to_owned()); + } + let components = path_components(token) + .into_iter() + .map(lookup_name) + .collect::>(); + let suffixes: &[(&[&str], &str)] = &[ + ( + &[ + "node_modules", + "@earendil-works", + "pi-coding-agent", + "dist", + "cli", + ], + "pi", + ), + ( + &[ + "node_modules", + "@earendil-works", + "pi-coding-agent", + "dist", + "bundle", + "cli", + ], + "pi", + ), + ( + &[ + "node_modules", + "@oh-my-pi", + "pi-coding-agent", + "dist", + "cli", + ], + "omp", + ), + ( + &[ + "node_modules", + "@moonshot-ai", + "kimi-code", + "dist", + "main.mjs", + ], + "kimi", + ), + ( + &["node_modules", "@qwen-code", "qwen-code", "dist", "index"], + "qwen", + ), + (&["node_modules", "mastracode", "dist", "cli"], "mastracode"), + ( + &["node_modules", "@letta-ai", "letta-code", "letta"], + "letta", + ), + ]; + suffixes.iter().find_map(|(suffix, agent)| { + (components.len() >= suffix.len() + && components[components.len() - suffix.len()..] + .iter() + .map(String::as_str) + .eq(suffix.iter().copied())) + .then(|| (*agent).to_owned()) + }) +} + +fn canonical_manifest_agent(name: &str) -> Option { + let alias = match name { + "claude-code" => "claude", + "cursor" => "cursor-agent", + "devin-cli" | "devin cli" => "devin", + "antigravity" | "antigravity-cli" => "agy", + ".cline" => "cline", + "open-code" | "opencode2" => "opencode", + "github-copilot" | "ghcs" => "copilot", + "kimi-code" | "kimi code" => "kimi", + "kiro" => "kiro-cli", + "amp-local" => "amp", + "grok-build" => "grok", + "hermes-agent" => "hermes", + "kilo-code" | "kilo code" => "kilo", + "qoderclicn" | "qoder" | "qodercn" => "qodercli", + "qwen-code" | "qwen code" => "qwen", + "letta-code" | "letta code" => "letta", + "muse-code" | "muse-cli" => "muse", + _ => name, + }; + bundled_manifest_for_executable(alias) + .ok() + .flatten() + .map(|manifest| manifest.id.clone()) + .or_else(|| matches!(alias, "omp" | "mastracode").then(|| alias.to_owned())) +} + +fn lookup_name(token: &str) -> String { + let basename = token + .rsplit(['/', '\\']) + .find(|component| !component.is_empty()) + .unwrap_or(token); + let mut name = basename.to_ascii_lowercase(); + for suffix in [".exe", ".cmd", ".bat", ".ps1", ".js"] { + if let Some(stripped) = name.strip_suffix(suffix) { + name = stripped.to_owned(); + break; + } + } + name +} + +fn path_components(path: &str) -> Vec<&str> { + path.split(['/', '\\']) + .filter(|component| !component.is_empty()) + .collect() +} + +fn is_generic_runtime_or_shell(name: &str) -> bool { + is_python_runtime(name) + || matches!( + name, + "sh" | "bash" + | "zsh" + | "fish" + | "tmux" + | "node" + | "bun" + | "cmd" + | "powershell" + | "pwsh" + ) +} + +fn is_python_runtime(name: &str) -> bool { + name == "python" + || name.strip_prefix("python").is_some_and(|version| { + !version.is_empty() + && version + .split('.') + .all(|part| !part.is_empty() && part.chars().all(|ch| ch.is_ascii_digit())) + }) +} + +fn process_priority(process: &ProcessArgvSample, candidate: &str) -> u8 { + if candidate != process.executable.to_ascii_lowercase() { + 3 + } else if is_generic_runtime_or_shell(candidate) { + 1 + } else { + 2 + } +} + +fn flag_matches(arg: &str, flags: &[&str]) -> bool { + flags.iter().any(|flag| { + arg == *flag + || (flag.starts_with('-') + && !flag.starts_with("--") + && arg.starts_with(flag) + && arg.len() > flag.len()) + || (flag.starts_with("--") + && arg + .strip_prefix(flag) + .is_some_and(|rest| rest.starts_with('='))) + }) +} + +fn option_takes_value(arg: &str) -> bool { + matches!( + arg, + "-r" | "--require" + | "--loader" + | "--import" + | "--experimental-loader" + | "--inspect-port" + | "-W" + | "-X" + | "-S" + | "-L" + | "-o" + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn process(name: &str, argv: &[&str]) -> ProcessArgvSample { + ProcessArgvSample { + executable: name.to_owned(), + argv: Some(argv.iter().map(|arg| (*arg).to_owned()).collect()), + } + } + + #[test] + fn node_package_and_python_script_wrappers_resolve_agent_names() { + let node = process( + "node", + &[ + "/usr/bin/node", + "/work/node_modules/@qwen-code/qwen-code/dist/index.js", + ], + ); + assert_eq!( + identify_foreground_process_group(None, &[node]).map(|identity| identity.executable), + Some("qwen".to_owned()) + ); + let python = process("python3.12", &["python3.12", "/usr/local/bin/claude"]); + assert_eq!( + identify_foreground_process_group(None, &[python]).map(|identity| identity.executable), + Some("claude".to_owned()) + ); + } + + #[test] + fn shell_commands_and_unrelated_script_mentions_do_not_false_match() { + let shell = process("bash", &["bash", "-c", "codex --help"]); + assert_eq!( + identify_foreground_process_group(None, &[shell]).map(|identity| identity.executable), + Some("codex".to_owned()) + ); + let unrelated = process("node", &["node", "-e", "console.log('codex')"]); + assert_eq!(identify_foreground_process_group(None, &[unrelated]), None); + } + + #[test] + fn letta_noninteractive_flags_are_filtered_but_interactive_is_retained() { + let interactive = process( + "node", + &[ + "node", + "node_modules/@letta-ai/letta-code/letta", + "--backend", + "local", + ], + ); + assert_eq!( + identify_foreground_process_group(None, &[interactive]) + .map(|identity| identity.executable), + Some("letta".to_owned()) + ); + let print = process("letta", &["letta", "--print", "hello"]); + assert_eq!(identify_foreground_process_group(None, &[print]), None); + } + + #[test] + fn leader_priority_and_equal_rank_disagreement_are_explicit() { + let leader = process("amp", &["amp"]); + let other = process("codex", &["codex"]); + assert_eq!( + identify_foreground_process_group(Some(&leader), &[other.clone()]), + Some(ProcessGroupIdentity { + executable: "amp".to_owned(), + ambiguous: false, + conflicting: Vec::new() + }) + ); + let tie = identify_foreground_process_group(None, &[other, process("claude", &["claude"])]) + .expect("two recognized direct names"); + assert!(tie.ambiguous); + assert_eq!(tie.executable, "codex"); + assert_eq!( + tie.conflicting, + vec!["codex".to_owned(), "claude".to_owned()] + ); + } +} diff --git a/crates/rover-agents/src/rollup.rs b/crates/rover-agents/src/rollup.rs new file mode 100644 index 0000000..b13ecd1 --- /dev/null +++ b/crates/rover-agents/src/rollup.rs @@ -0,0 +1,332 @@ +//! Deterministic, freshness-aware agent state aggregation. +//! +//! The caller supplies snapshots with wall-clock Unix timestamps and a +//! freshness window. This module does not sample processes or infer lifecycle +//! events. `done_unseen` is an explicit input from an event/viewed-state owner. + +use std::io; + +use crate::AgentState; + +const MAX_OBSERVATIONS: usize = 4096; +const MAX_ID_BYTES: usize = 128; + +/// UI state after applying explicit completion and freshness information. +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum DisplayState { + /// No usable state evidence exists. + Unknown, + /// A fresh observation indicates no current work. + Idle, + /// An agent finished and its completion has not been viewed. + Done, + /// At least one fresh agent is actively working. + Working, + /// At least one fresh agent needs human input. + Blocked, + /// Observations exist, but every observation is outside the freshness window. + Stale, +} + +/// One agent's latest state snapshot. `observed_at_ms` is a Unix timestamp in +/// milliseconds from the same wall clock used as the rollup's `now_ms`. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AgentObservation { + /// Workspace containing the pane. + pub workspace_id: String, + /// Tab containing the pane. + pub tab_id: String, + /// Stable pane identifier; must be unique in one rollup input. + pub pane_id: String, + /// State classified by detection or an authoritative lifecycle event. + pub state: AgentState, + /// Whether an idle state represents an unviewed completion event. + pub done_unseen: bool, + /// Time at which this state was observed, in Unix milliseconds. + pub observed_at_ms: u64, +} + +/// Aggregate for a pane, tab, or workspace, with freshness counts retained. +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] +pub struct Rollup { + /// Scope type (`pane`, `tab`, or `workspace`). + pub scope: &'static str, + /// Stable identifier of the selected scope. + pub id: String, + /// Highest-priority state among fresh observations, or `stale`/`unknown`. + pub state: DisplayState, + /// Number of observations in scope. + pub observation_count: usize, + /// Number of observations at or inside the freshness window. + pub fresh_count: usize, + /// Number of observations outside the freshness window. + pub stale_count: usize, + /// Number of fresh unknown observations. + pub unknown_count: usize, + /// Number of fresh, unviewed completion observations. + pub done_unseen_count: usize, + /// Pane identifiers contributing a fresh blocked state, in sorted order. + pub blocked_pane_ids: Vec, +} + +/// Aggregate observations for one pane. +/// +/// # Errors +/// +/// Returns `InvalidInput` for invalid IDs, freshness bounds, duplicate pane +/// observations, or completion flags inconsistent with the semantic state. +pub fn rollup_pane( + pane_id: &str, + observations: &[AgentObservation], + now_ms: u64, + stale_after_ms: u64, +) -> io::Result { + if !valid_id(pane_id) { + return Err(invalid_input("invalid pane id")); + } + let selected = observations + .iter() + .filter(|item| item.pane_id == pane_id) + .collect::>(); + aggregate("pane", pane_id, &selected, now_ms, stale_after_ms) +} + +/// Aggregate observations for one tab. +/// +/// # Errors +/// +/// Returns `InvalidInput` for invalid IDs, freshness bounds, duplicate pane +/// observations, or completion flags inconsistent with the semantic state. +pub fn rollup_tab( + workspace_id: &str, + tab_id: &str, + observations: &[AgentObservation], + now_ms: u64, + stale_after_ms: u64, +) -> io::Result { + if !valid_id(workspace_id) || !valid_id(tab_id) { + return Err(invalid_input("invalid workspace or tab id")); + } + let selected = observations + .iter() + .filter(|item| item.workspace_id == workspace_id && item.tab_id == tab_id) + .collect::>(); + aggregate("tab", tab_id, &selected, now_ms, stale_after_ms) +} + +/// Aggregate observations for one workspace. +/// +/// # Errors +/// +/// Returns `InvalidInput` for invalid IDs, freshness bounds, duplicate pane +/// observations, or completion flags inconsistent with the semantic state. +pub fn rollup_workspace( + workspace_id: &str, + observations: &[AgentObservation], + now_ms: u64, + stale_after_ms: u64, +) -> io::Result { + if !valid_id(workspace_id) { + return Err(invalid_input("invalid workspace id")); + } + let selected = observations + .iter() + .filter(|item| item.workspace_id == workspace_id) + .collect::>(); + aggregate("workspace", workspace_id, &selected, now_ms, stale_after_ms) +} + +fn aggregate( + scope: &'static str, + id: &str, + items: &[&AgentObservation], + now_ms: u64, + stale_after_ms: u64, +) -> io::Result { + if stale_after_ms == 0 || items.len() > MAX_OBSERVATIONS { + return Err(invalid_input("invalid agent rollup bounds")); + } + let mut pane_ids = std::collections::BTreeSet::new(); + for item in items { + if !valid_id(&item.workspace_id) + || !valid_id(&item.tab_id) + || !valid_id(&item.pane_id) + || !pane_ids.insert(item.pane_id.as_str()) + || (item.done_unseen && item.state != AgentState::Idle) + { + return Err(invalid_input("invalid or duplicate agent observation")); + } + } + + let mut fresh_count = 0; + let mut stale_count = 0; + let mut unknown_count = 0; + let mut done_unseen_count = 0; + let mut blocked_pane_ids = Vec::new(); + let mut state = DisplayState::Unknown; + for item in items { + let age_ms = now_ms.saturating_sub(item.observed_at_ms); + if age_ms > stale_after_ms { + stale_count += 1; + continue; + } + fresh_count += 1; + let candidate = match item.state { + AgentState::Unknown => { + unknown_count += 1; + DisplayState::Unknown + } + AgentState::Idle if item.done_unseen => { + done_unseen_count += 1; + DisplayState::Done + } + AgentState::Idle => DisplayState::Idle, + AgentState::Working => DisplayState::Working, + AgentState::Blocked => { + blocked_pane_ids.push(item.pane_id.clone()); + DisplayState::Blocked + } + }; + state = state.max(candidate); + } + if fresh_count == 0 && stale_count > 0 { + state = DisplayState::Stale; + } + blocked_pane_ids.sort(); + Ok(Rollup { + scope, + id: id.to_owned(), + state, + observation_count: items.len(), + fresh_count, + stale_count, + unknown_count, + done_unseen_count, + blocked_pane_ids, + }) +} + +fn valid_id(value: &str) -> bool { + !value.is_empty() && value.len() <= MAX_ID_BYTES && !value.chars().any(char::is_control) +} + +fn invalid_input(message: &'static str) -> io::Error { + io::Error::new(io::ErrorKind::InvalidInput, message) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn observation(pane_id: &str, state: AgentState) -> AgentObservation { + AgentObservation { + workspace_id: "ws".to_owned(), + tab_id: "tab".to_owned(), + pane_id: pane_id.to_owned(), + state, + done_unseen: false, + observed_at_ms: 1_000, + } + } + + #[test] + fn priority_is_blocked_then_working_then_done_then_idle() { + let mut done = observation("done", AgentState::Idle); + done.done_unseen = true; + let items = vec![ + observation("idle", AgentState::Idle), + done, + observation("working", AgentState::Working), + observation("blocked", AgentState::Blocked), + ]; + let result = rollup_workspace("ws", &items, 1_000, 500).unwrap(); + assert_eq!(result.state, DisplayState::Blocked); + assert_eq!(result.done_unseen_count, 1); + assert_eq!(result.blocked_pane_ids, ["blocked"]); + } + + #[test] + fn done_remains_visible_until_the_owner_clears_unseen_flag() { + let mut item = observation("p1", AgentState::Idle); + item.done_unseen = true; + assert_eq!( + rollup_pane("p1", &[item.clone()], 1_000, 500) + .unwrap() + .state, + DisplayState::Done + ); + item.done_unseen = false; + assert_eq!( + rollup_pane("p1", &[item], 1_000, 500).unwrap().state, + DisplayState::Idle + ); + } + + #[test] + fn stale_is_shown_only_when_every_observation_is_stale() { + let stale = observation("old", AgentState::Blocked); + let result = rollup_workspace("ws", &[stale.clone()], 2_000, 500).unwrap(); + assert_eq!(result.state, DisplayState::Stale); + assert_eq!(result.stale_count, 1); + let mut current = observation("current", AgentState::Idle); + current.observed_at_ms = 1_900; + let mixed = rollup_workspace("ws", &[stale, current], 2_000, 500).unwrap(); + assert_eq!(mixed.state, DisplayState::Idle); + assert_eq!(mixed.stale_count, 1); + } + + #[test] + fn unknown_and_empty_inputs_do_not_invent_activity() { + let unknown = observation("p1", AgentState::Unknown); + assert_eq!( + rollup_tab("ws", "tab", &[unknown], 1_000, 500) + .unwrap() + .state, + DisplayState::Unknown + ); + assert_eq!( + rollup_tab("ws", "tab", &[], 1_000, 500).unwrap().state, + DisplayState::Unknown + ); + } + + #[test] + fn scope_filters_and_duplicate_or_inconsistent_records_fail_closed() { + let mut items = vec![observation("p1", AgentState::Working)]; + items.push(AgentObservation { + workspace_id: "elsewhere".into(), + ..observation("p2", AgentState::Blocked) + }); + assert_eq!( + rollup_workspace("ws", &items, 1_000, 500).unwrap().state, + DisplayState::Working + ); + items.push(observation("p1", AgentState::Idle)); + assert_eq!( + rollup_workspace("ws", &items, 1_000, 500) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidInput + ); + let mut invalid = observation("p3", AgentState::Working); + invalid.done_unseen = true; + assert!(rollup_workspace("ws", &[invalid], 1_000, 500).is_err()); + } + + #[test] + fn timestamps_are_inclusive_and_future_clock_skew_is_clamped() { + let item = observation("p1", AgentState::Working); + assert_eq!( + rollup_pane("p1", &[item.clone()], 1_500, 500) + .unwrap() + .fresh_count, + 1 + ); + assert_eq!( + rollup_pane("p1", &[item], 999, 500).unwrap().state, + DisplayState::Working + ); + assert!(rollup_workspace("ws", &[], 1_000, 0).is_err()); + } +} diff --git a/crates/rover-agents/src/sessions.rs b/crates/rover-agents/src/sessions.rs new file mode 100644 index 0000000..8a1c584 --- /dev/null +++ b/crates/rover-agents/src/sessions.rs @@ -0,0 +1,695 @@ +//! Project-bound exact agent session identities and explicit resume commands. +//! +//! This module does not scan provider stores or resume automatically. Callers +//! must bind an exact ID from a transcript, reviewed hook, or direct user +//! selection, then explicitly request argv for that same adapter. + +use std::fmt; + +use rover_core::{RepositoryIdentity, Sha256Digest}; +use rover_store::{Store, StoreError}; +use serde::{Deserialize, Serialize}; + +use super::{ + native_executable, nonempty, valid_tool, validate_model, AgentResult, InvocationOptions, +}; + +const RECORD_KIND: &str = "agent_session"; +const RECORD_SCHEMA: &str = "rover/agent-session/v1"; +const MAX_PANE_ID_BYTES: usize = 128; +const MAX_SESSION_ID_BYTES: usize = 256; +const MAX_SOURCE_BYTES: usize = 80; +const MAX_SESSION_BINDING_RECORDS: i64 = 1000; + +/// Evidence origin for an exact session identity. +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum AgentSessionSource { + /// Identity was emitted in a provider's structured transcript. + ProviderTranscript, + /// Identity was emitted by an installed agent integration hook. + OfficialHook, + /// The user explicitly selected or entered this exact identity. + ExplicitUser, +} + +impl AgentSessionSource { + /// Stable source label for user-visible evidence. + #[must_use] + pub const fn as_str(self) -> &'static str { + match self { + Self::ProviderTranscript => "provider transcript", + Self::OfficialHook => "agent integration hook", + Self::ExplicitUser => "user-selected", + } + } +} + +/// Exact native agent session bound to a Rover project pane. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct AgentSessionBinding { + schema: String, + project_id: String, + repository: String, + pane_id: String, + adapter: String, + session_id: String, + source: AgentSessionSource, +} + +impl AgentSessionBinding { + /// Bind the session ID emitted by a parsed native provider transcript. + /// + /// The transcript parser validates structure and identity consistency; it + /// does not prove that a provider's ID is genuine or resumable. + /// + /// # Errors + /// + /// Returns an error when the result lacks a supported adapter or session + /// identity, or when the project/pane binding is invalid. + pub fn from_transcript( + project: &RepositoryIdentity, + pane_id: impl Into, + result: &AgentResult, + ) -> Result { + if result.session.is_empty() + || !matches!(result.adapter.as_str(), "codex-exec" | "claude-print") + { + return Err(SessionBindingError::InvalidRecord); + } + Self::new( + project, + pane_id, + result.adapter.clone(), + result.session.clone(), + AgentSessionSource::ProviderTranscript, + ) + } + + /// Create a validated exact-ID binding for a supported native adapter. + /// + /// Only profiles for which Rover currently has reviewed argv contracts are + /// accepted. Paths and guessed or latest references are not IDs. + /// + /// # Errors + /// + /// Returns an error for unsupported adapters, invalid identifiers, or a + /// project path that cannot be represented as UTF-8. + pub fn new( + project: &RepositoryIdentity, + pane_id: impl Into, + adapter: impl Into, + session_id: impl Into, + source: AgentSessionSource, + ) -> Result { + let repository = project + .path() + .to_str() + .ok_or(SessionBindingError::InvalidRepository)? + .to_owned(); + let binding = Self { + schema: RECORD_SCHEMA.to_owned(), + project_id: project.project_id().to_hex(), + repository, + pane_id: pane_id.into(), + adapter: adapter.into(), + session_id: session_id.into(), + source, + }; + binding.validate()?; + Ok(binding) + } + + /// Stable pane identity within the bound project. + #[must_use] + pub fn pane_id(&self) -> &str { + &self.pane_id + } + + /// Native adapter profile associated with this session. + #[must_use] + pub fn adapter(&self) -> &str { + &self.adapter + } + + /// Exact opaque provider session ID. + #[must_use] + pub fn session_id(&self) -> &str { + &self.session_id + } + + /// Evidence origin for this binding. + #[must_use] + pub const fn source(&self) -> AgentSessionSource { + self.source + } + + /// Construct an explicit native resume invocation for this binding. + /// + /// The returned argv does not execute the program. It always targets the + /// bound session ID and validates that the requested adapter matches. + /// + /// # Errors + /// + /// Returns an error for an adapter mismatch or invalid native options. + pub fn resume_argv( + &self, + adapter: &str, + options: &InvocationOptions, + prompt: &str, + ) -> Result, SessionBindingError> { + self.validate()?; + if adapter != self.adapter { + return Err(SessionBindingError::AdapterMismatch); + } + let executable = native_executable( + options.executable.as_deref(), + if adapter == "codex-exec" { + "codex" + } else { + "claude" + }, + ) + .map_err(|_| SessionBindingError::InvalidOptions)?; + validate_model(options.model.as_deref()) + .map_err(|_| SessionBindingError::InvalidOptions)?; + match adapter { + "codex-exec" => { + let sandbox = if options.write { + "workspace-write" + } else { + "read-only" + }; + let mut argv = vec![ + executable, + "exec".to_owned(), + "resume".to_owned(), + self.session_id.clone(), + "--json".to_owned(), + "--sandbox".to_owned(), + sandbox.to_owned(), + ]; + if let Some(model) = nonempty(options.model.as_deref()) { + argv.extend(["--model".to_owned(), model.to_owned()]); + } + argv.extend(["--".to_owned(), prompt.to_owned()]); + Ok(argv) + } + "claude-print" => { + for tool in &options.allowed_tools { + if !valid_tool(tool) { + return Err(SessionBindingError::InvalidOptions); + } + } + let mode = if options.write { "acceptEdits" } else { "plan" }; + let mut argv = vec![ + executable, + "--resume".to_owned(), + self.session_id.clone(), + "--print".to_owned(), + "--output-format".to_owned(), + "stream-json".to_owned(), + "--verbose".to_owned(), + "--permission-mode".to_owned(), + mode.to_owned(), + ]; + if options.max_turns > 0 { + argv.extend(["--max-turns".to_owned(), options.max_turns.to_string()]); + } + if let Some(model) = nonempty(options.model.as_deref()) { + argv.extend(["--model".to_owned(), model.to_owned()]); + } + if !options.allowed_tools.is_empty() { + argv.extend(["--allowedTools".to_owned(), options.allowed_tools.join(",")]); + } + argv.extend(["--".to_owned(), prompt.to_owned()]); + Ok(argv) + } + _ => Err(SessionBindingError::InvalidRecord), + } + } + + /// Build an interactive native-session resume argv for the Rover TUI. + /// + /// The resulting command opens the provider's interactive terminal UI, + /// unlike `resume_argv` which uses Rover's headless transcript profiles. + /// + /// # Errors + /// + /// Returns `InvalidRecord` when this binding does not map to a supported + /// interactive native resume command. + pub fn resume_tui_argv(&self) -> Result, SessionBindingError> { + self.validate()?; + match self.adapter.as_str() { + "codex-exec" => Ok(vec![ + "codex".to_owned(), + "resume".to_owned(), + self.session_id.clone(), + ]), + "claude-print" => Ok(vec![ + "claude".to_owned(), + "--resume".to_owned(), + self.session_id.clone(), + ]), + _ => Err(SessionBindingError::InvalidRecord), + } + } + + fn validate(&self) -> Result<(), SessionBindingError> { + if self.schema != RECORD_SCHEMA + || !valid_pane_id(&self.pane_id) + || !valid_session_id(&self.session_id) + || !valid_source(&self.adapter) + || !matches!(self.adapter.as_str(), "codex-exec" | "claude-print") + || self.repository.is_empty() + || self.repository.len() > 4096 + || Sha256Digest::parse_hex(&self.project_id).is_err() + { + return Err(SessionBindingError::InvalidRecord); + } + Ok(()) + } +} + +/// Session binding persistence and validation error. +#[derive(Debug)] +pub enum SessionBindingError { + Store(StoreError), + InvalidRecord, + InvalidIdentifier, + InvalidOptions, + AdapterMismatch, + ProjectMismatch, + InvalidRepository, +} + +impl fmt::Display for SessionBindingError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Store(error) => write!(formatter, "agent session storage failed: {error}"), + Self::InvalidRecord => formatter.write_str("agent session binding is invalid"), + Self::InvalidIdentifier => formatter.write_str("agent session identifier is invalid"), + Self::InvalidOptions => formatter.write_str("agent resume options are invalid"), + Self::AdapterMismatch => { + formatter.write_str("agent session adapter does not match the requested adapter") + } + Self::ProjectMismatch => { + formatter.write_str("agent session binding belongs to another project") + } + Self::InvalidRepository => { + formatter.write_str("repository path cannot be represented as UTF-8") + } + } + } +} + +impl std::error::Error for SessionBindingError {} + +impl From for SessionBindingError { + fn from(error: StoreError) -> Self { + Self::Store(error) + } +} + +/// Save or replace the explicit binding for one pane in the project's store. +/// +/// # Errors +/// +/// Returns an error if the binding does not belong to project or storage +/// rejects the record. +pub fn save_session_binding( + store: &Store, + project: &RepositoryIdentity, + binding: &AgentSessionBinding, +) -> Result<(), SessionBindingError> { + validate_project(binding, project)?; + let id = session_record_id(project, binding.pane_id()); + store.put(RECORD_KIND, &id, binding, "agent_session.bound")?; + Ok(()) +} + +/// Load and verify the exact binding for one pane and project. +/// +/// # Errors +/// +/// Returns `NotFound` through the store error when no binding exists; rejects +/// malformed records or project/pane identity mismatches. +pub fn load_session_binding( + store: &Store, + project: &RepositoryIdentity, + pane_id: &str, +) -> Result { + if !valid_pane_id(pane_id) { + return Err(SessionBindingError::InvalidIdentifier); + } + let id = session_record_id(project, pane_id); + let binding: AgentSessionBinding = store.get_typed(RECORD_KIND, &id)?; + binding.validate()?; + validate_project(&binding, project)?; + if binding.pane_id != pane_id { + return Err(SessionBindingError::InvalidRecord); + } + Ok(binding) +} + +/// List exact session bindings for one canonical project, ordered by pane ID. +/// +/// Records for other projects are omitted. Malformed records claiming this +/// project's identity fail the entire query rather than producing a partial +/// inventory. +/// +/// # Errors +/// +/// Returns an error for an invalid project binding, malformed matching record, +/// duplicate pane binding, or a state store with more than 1,000 session rows. +pub fn list_session_bindings( + store: &Store, + project: &RepositoryIdentity, +) -> Result, SessionBindingError> { + let project_id = project.project_id().to_hex(); + let repository = project + .path() + .to_str() + .ok_or(SessionBindingError::InvalidRepository)?; + let records = store.list_all(RECORD_KIND, MAX_SESSION_BINDING_RECORDS)?; + let mut bindings = Vec::new(); + for record in records { + if record.string_at_path(&["project_id"]) != Some(project_id.as_str()) { + continue; + } + let binding: AgentSessionBinding = serde_json::from_value(record.as_value().clone()) + .map_err(|_| SessionBindingError::InvalidRecord)?; + binding.validate()?; + if binding.repository != repository { + return Err(SessionBindingError::ProjectMismatch); + } + bindings.push(binding); + } + bindings.sort_by(|left, right| left.pane_id.cmp(&right.pane_id)); + if bindings + .windows(2) + .any(|pair| pair[0].pane_id == pair[1].pane_id) + { + return Err(SessionBindingError::InvalidRecord); + } + Ok(bindings) +} + +/// Clear the explicit session binding for one project pane. +/// +/// # Errors +/// +/// Returns an error for an invalid pane ID, absent binding, or store failure. +pub fn clear_session_binding( + store: &Store, + project: &RepositoryIdentity, + pane_id: &str, +) -> Result<(), SessionBindingError> { + if !valid_pane_id(pane_id) { + return Err(SessionBindingError::InvalidIdentifier); + } + let id = session_record_id(project, pane_id); + store.delete(RECORD_KIND, &id, "agent_session.cleared")?; + Ok(()) +} + +fn validate_project( + binding: &AgentSessionBinding, + project: &RepositoryIdentity, +) -> Result<(), SessionBindingError> { + binding.validate()?; + let repository = project + .path() + .to_str() + .ok_or(SessionBindingError::InvalidRepository)?; + if binding.project_id != project.project_id().to_hex() || binding.repository != repository { + return Err(SessionBindingError::ProjectMismatch); + } + Ok(()) +} + +fn session_record_id(project: &RepositoryIdentity, pane_id: &str) -> String { + let project_id = project.project_id().to_hex(); + format!( + "as_{}", + Sha256Digest::of(format!("{project_id}\0{pane_id}").as_bytes()).to_hex() + ) +} + +fn valid_pane_id(value: &str) -> bool { + !value.is_empty() + && value.len() <= MAX_PANE_ID_BYTES + && value.chars().all(|character| { + character.is_ascii_alphanumeric() || matches!(character, ':' | '.' | '_' | '-') + }) +} + +fn valid_session_id(value: &str) -> bool { + !value.is_empty() + && !value.starts_with('-') + && value.len() <= MAX_SESSION_ID_BYTES + && value.chars().all(|character| { + character.is_ascii_alphanumeric() || matches!(character, '-' | '_' | '.') + }) +} + +fn valid_source(value: &str) -> bool { + !value.is_empty() + && value.len() <= MAX_SOURCE_BYTES + && value.chars().all(|character| { + character.is_ascii_alphanumeric() || matches!(character, ':' | '.' | '_' | '-') + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use rusqlite::Connection; + + fn store() -> Store { + Store::from_connection(Connection::open_in_memory().unwrap()).unwrap() + } + + fn project(path: &str) -> RepositoryIdentity { + RepositoryIdentity::resolve(path).unwrap() + } + + #[test] + fn exact_binding_is_project_pane_bound_and_reloadable() { + let store = store(); + let project = project("/tmp/rover-agent-session-project"); + let binding = AgentSessionBinding::new( + &project, + "w1:p2", + "codex-exec", + "019d1c08-c1b4-70c2-955f-840a5022c017", + AgentSessionSource::ProviderTranscript, + ) + .unwrap(); + save_session_binding(&store, &project, &binding).unwrap(); + assert_eq!( + load_session_binding(&store, &project, "w1:p2").unwrap(), + binding + ); + assert!(matches!( + load_session_binding(&store, &project, "w1:p3"), + Err(SessionBindingError::Store(StoreError::NotFound)) + )); + } + + #[test] + fn binding_from_transcript_preserves_adapter_identity_and_claim_provenance() { + let project = project("/tmp/rover-agent-session-transcript"); + let result = AgentResult { + adapter: "codex-exec".to_owned(), + session: "thread-abc".to_owned(), + ..AgentResult::default() + }; + let binding = AgentSessionBinding::from_transcript(&project, "pane-1", &result).unwrap(); + assert_eq!(binding.adapter(), "codex-exec"); + assert_eq!(binding.session_id(), "thread-abc"); + assert_eq!(binding.source(), AgentSessionSource::ProviderTranscript); + let mut invalid = result; + invalid.adapter = "generic-pty".to_owned(); + assert!(AgentSessionBinding::from_transcript(&project, "pane-1", &invalid).is_err()); + } + + #[test] + fn explicit_resume_uses_bound_identity_and_rejects_mismatch() { + let project = project("/tmp/rover-agent-resume-project"); + let binding = AgentSessionBinding::new( + &project, + "pane-1", + "claude-print", + "session_123", + AgentSessionSource::OfficialHook, + ) + .unwrap(); + let argv = binding + .resume_argv("claude-print", &InvocationOptions::default(), "continue") + .unwrap(); + assert_eq!(argv[0], "claude"); + assert_eq!(&argv[1..4], ["--resume", "session_123", "--print"]); + assert_eq!(&argv[argv.len() - 2..], ["--", "continue"]); + assert!(matches!( + binding.resume_argv("codex-exec", &InvocationOptions::default(), "x"), + Err(SessionBindingError::AdapterMismatch) + )); + } + + #[test] + fn tui_resume_uses_interactive_native_commands() { + let project = project("/tmp/rover-agent-tui-resume"); + let codex = AgentSessionBinding::new( + &project, + "pane-c", + "codex-exec", + "thread-abc", + AgentSessionSource::ProviderTranscript, + ) + .unwrap(); + let claude = AgentSessionBinding::new( + &project, + "pane-a", + "claude-print", + "session-123", + AgentSessionSource::OfficialHook, + ) + .unwrap(); + assert_eq!( + codex.resume_tui_argv().unwrap(), + ["codex", "resume", "thread-abc"] + ); + assert_eq!( + claude.resume_tui_argv().unwrap(), + ["claude", "--resume", "session-123"] + ); + } + + #[test] + fn codex_resume_keeps_permission_mode_and_arguments_separate() { + let project = project("/tmp/rover-agent-codex-resume"); + let binding = AgentSessionBinding::new( + &project, + "pane-2", + "codex-exec", + "thread-abc", + AgentSessionSource::ExplicitUser, + ) + .unwrap(); + let argv = binding + .resume_argv( + "codex-exec", + &InvocationOptions { + write: true, + model: Some("gpt-5-codex".to_owned()), + ..InvocationOptions::default() + }, + "follow-up; do not split", + ) + .unwrap(); + assert_eq!(&argv[..4], ["codex", "exec", "resume", "thread-abc"]); + assert!(argv + .windows(2) + .any(|pair| pair == ["--sandbox", "workspace-write"])); + assert_eq!(&argv[argv.len() - 2..], ["--", "follow-up; do not split"]); + } + + #[test] + fn malformed_and_cross_project_bindings_fail_closed() { + let store = store(); + let other = project("/tmp/rover-agent-session-b"); + let project = project("/tmp/rover-agent-session-a"); + let too_long = "x".repeat(257); + for session in ["", "-option", "id/../../other", "bad id", too_long.as_str()] { + assert!(AgentSessionBinding::new( + &project, + "pane", + "codex-exec", + session, + AgentSessionSource::ExplicitUser + ) + .is_err()); + } + let binding = AgentSessionBinding::new( + &project, + "pane", + "codex-exec", + "session-1", + AgentSessionSource::ExplicitUser, + ) + .unwrap(); + save_session_binding(&store, &project, &binding).unwrap(); + assert!(matches!( + load_session_binding(&store, &other, "pane"), + Err(SessionBindingError::Store(StoreError::NotFound) + | SessionBindingError::ProjectMismatch) + )); + assert!(AgentSessionBinding::new( + &project, + "pane", + "generic-pty", + "session-1", + AgentSessionSource::ExplicitUser + ) + .is_err()); + } + + #[test] + fn clearing_binding_removes_it_and_records_deletion() { + let store = store(); + let project = project("/tmp/rover-agent-session-clear"); + let binding = AgentSessionBinding::new( + &project, + "pane", + "claude-print", + "session-1", + AgentSessionSource::ExplicitUser, + ) + .unwrap(); + save_session_binding(&store, &project, &binding).unwrap(); + clear_session_binding(&store, &project, "pane").unwrap(); + assert!(matches!( + load_session_binding(&store, &project, "pane"), + Err(SessionBindingError::Store(StoreError::NotFound)) + )); + assert!(matches!( + clear_session_binding(&store, &project, "pane"), + Err(SessionBindingError::Store(StoreError::NotFound)) + )); + } + + #[test] + fn binding_inventory_is_project_scoped_sorted_and_exact() { + let store = store(); + let repository = project("/tmp/rover-agent-inventory-project"); + let other_project = project("/tmp/rover-agent-inventory-other-project"); + for (identity, pane, session) in [ + (&repository, "pane-z", "session-z"), + (&other_project, "pane-a", "session-a"), + (&repository, "pane-b", "session-b"), + ] { + let binding = AgentSessionBinding::new( + identity, + pane, + "codex-exec", + session, + AgentSessionSource::ExplicitUser, + ) + .unwrap(); + save_session_binding(&store, identity, &binding).unwrap(); + } + let bindings = list_session_bindings(&store, &repository).unwrap(); + assert_eq!( + bindings + .iter() + .map(|binding| (binding.pane_id(), binding.session_id())) + .collect::>(), + [("pane-b", "session-b"), ("pane-z", "session-z")] + ); + assert!(list_session_bindings(&store, &other_project) + .unwrap() + .iter() + .all(|binding| binding.pane_id() == "pane-a")); + } +} diff --git a/crates/rover-agents/src/transcript.rs b/crates/rover-agents/src/transcript.rs new file mode 100644 index 0000000..4cd060d --- /dev/null +++ b/crates/rover-agents/src/transcript.rs @@ -0,0 +1,389 @@ +//! Bounded JSONL parsing for the documented Codex and Claude CLI event forms. +//! +//! Provider-authored messages, completion flags, and usage are claims from +//! that process. They are not independent verification evidence. + +use std::collections::BTreeMap; +use std::io; + +const MAX_TRANSCRIPT_BYTES: usize = 1 << 20; +const MAX_LINE_BYTES: usize = 1 << 20; +const MAX_CLAIMS: usize = 256; +const PROVENANCE: &str = "provider-emitted claims/usage; not independent verification"; + +/// Parsed provider output with its source clearly identified as a provider +/// claim, not an independently checked result. +#[derive(Clone, Debug, Default, PartialEq, serde::Serialize)] +pub struct AgentResult { + /// Profile that produced this output. + pub adapter: String, + /// Provider session identity when the transcript supplies one. + #[serde(skip_serializing_if = "String::is_empty")] + pub session: String, + /// Provider's terminal completion claim. + pub completed: bool, + /// Provider failure detail, if present. + #[serde(skip_serializing_if = "String::is_empty")] + pub error: String, + /// Provider-authored response claims. + #[serde(skip_serializing_if = "Vec::is_empty")] + pub claims: Vec, + /// Nonnegative integer usage fields supplied by the provider. + #[serde(skip_serializing_if = "BTreeMap::is_empty")] + pub usage: BTreeMap, + /// Provider-reported cost estimate. + #[serde(skip_serializing_if = "Option::is_none")] + pub estimated_cost_usd: Option, + /// Count of event types the adapter did not interpret. + pub unknown_events: usize, + /// Provenance boundary for returned values. + pub provenance: String, +} + +/// Parse one bounded native CLI JSONL transcript. +/// +/// Codex requires a stable `thread.started` identity before `turn.completed`; +/// Claude requires a stable `session_id` and a boolean `is_error` in its +/// terminal result. Unknown adapter names intentionally return an empty result +/// as the Go compatibility contract does; adapter selection itself must use +/// [`crate::profile`] to fail closed before process dispatch. +/// +/// # Errors +/// +/// Returns `InvalidData` for oversized, malformed, conflicting, or incomplete +/// native transcripts, and for invalid terminal event ordering. +pub fn parse_transcript(adapter: &str, bytes: &[u8]) -> io::Result { + let mut result = AgentResult { + adapter: adapter.to_owned(), + provenance: PROVENANCE.to_owned(), + ..AgentResult::default() + }; + if adapter != "codex-exec" && adapter != "claude-print" { + return Ok(result); + } + if bytes.len() > MAX_TRANSCRIPT_BYTES { + return Err(invalid_data("transcript exceeds limit")); + } + + let mut terminal = false; + let mut seen = BTreeMap::>::new(); + for line in bytes.split(|byte| *byte == b'\n') { + if line.len() > MAX_LINE_BYTES { + return Err(invalid_data("malformed native event")); + } + if line.iter().all(u8::is_ascii_whitespace) { + continue; + } + let event: serde_json::Value = + serde_json::from_slice(line).map_err(|_| invalid_data("malformed native event"))?; + let Some(object) = event.as_object() else { + if event.is_null() { + return Err(invalid_data("event missing type")); + } + return Err(invalid_data("malformed native event")); + }; + let kind = object + .get("type") + .and_then(serde_json::Value::as_str) + .unwrap_or_default(); + if kind.is_empty() { + return Err(invalid_data("event missing type")); + } + if let Some(uuid) = object + .get("uuid") + .and_then(serde_json::Value::as_str) + .filter(|value| !value.is_empty()) + { + if let Some(previous) = seen.get(uuid) { + if previous.as_slice() != line { + return Err(invalid_data("conflicting duplicate event")); + } + continue; + } + seen.insert(uuid.to_owned(), line.to_vec()); + } + if terminal { + return Err(invalid_data("event after terminal result")); + } + + if adapter == "codex-exec" { + parse_codex_event(object, kind, &mut result, &mut terminal)?; + } else { + parse_claude_event(object, kind, &mut result, &mut terminal)?; + } + if result.claims.len() > MAX_CLAIMS { + return Err(invalid_data("claim capture limit exceeded")); + } + } + if !terminal { + return Err(invalid_data("terminal result missing")); + } + Ok(result) +} + +fn parse_codex_event( + event: &serde_json::Map, + kind: &str, + result: &mut AgentResult, + terminal: &mut bool, +) -> io::Result<()> { + match kind { + "thread.started" => { + let identity = string_field(event, "thread_id"); + if identity.is_empty() || (!result.session.is_empty() && result.session != identity) { + return Err(invalid_data("invalid thread identity")); + } + identity.clone_into(&mut result.session); + } + "turn.started" | "item.started" | "item.updated" => {} + "item.completed" => { + let Some(item) = event.get("item") else { + return Err(invalid_data("invalid item")); + }; + if !item.is_null() && !item.is_object() { + return Err(invalid_data("invalid item")); + } + let item_type = item + .get("type") + .map_or(Ok(""), optional_json_string) + .map_err(|()| invalid_data("invalid item"))?; + let text = item + .get("text") + .map_or(Ok(""), optional_json_string) + .map_err(|()| invalid_data("invalid item"))?; + if item_type == "agent_message" { + result.claims.push(text.to_owned()); + } + } + "turn.completed" => { + if result.session.is_empty() { + return Err(invalid_data("completion without thread")); + } + *terminal = true; + result.completed = true; + read_usage(event.get("usage"), &mut result.usage); + } + "turn.failed" | "error" => { + *terminal = true; + result.error = event + .get("error") + .map_or_else(String::new, serde_json::Value::to_string); + if result.error.is_empty() { + "provider error".clone_into(&mut result.error); + } + } + _ => result.unknown_events += 1, + } + Ok(()) +} + +fn optional_json_string(value: &serde_json::Value) -> Result<&str, ()> { + match value { + serde_json::Value::Null => Ok(""), + serde_json::Value::String(value) => Ok(value), + _ => Err(()), + } +} + +fn parse_claude_event( + event: &serde_json::Map, + kind: &str, + result: &mut AgentResult, + terminal: &mut bool, +) -> io::Result<()> { + if let Some(identity) = event + .get("session_id") + .and_then(serde_json::Value::as_str) + .filter(|value| !value.is_empty()) + { + if !result.session.is_empty() && result.session != identity { + return Err(invalid_data("session changed")); + } + identity.clone_into(&mut result.session); + } + match kind { + "system" | "assistant" | "user" | "stream_event" | "tool_progress" | "tool_use_summary" => { + } + "result" => { + let Some(failed) = event.get("is_error").and_then(serde_json::Value::as_bool) else { + return Err(invalid_data( + "terminal result requires session and is_error boolean", + )); + }; + if result.session.is_empty() { + return Err(invalid_data( + "terminal result requires session and is_error boolean", + )); + } + *terminal = true; + result.completed = !failed; + if failed { + string_field(event, "result").clone_into(&mut result.error); + if result.error.is_empty() { + "provider failed".clone_into(&mut result.error); + } + } + let claim = string_field(event, "result"); + if !claim.is_empty() { + result.claims.push(claim.to_owned()); + } + read_usage(event.get("usage"), &mut result.usage); + result.estimated_cost_usd = event + .get("total_cost_usd") + .and_then(serde_json::Value::as_f64) + .filter(|cost| *cost >= 0.0); + } + _ => result.unknown_events += 1, + } + Ok(()) +} + +fn read_usage(value: Option<&serde_json::Value>, destination: &mut BTreeMap) { + let Some(usage) = value.and_then(serde_json::Value::as_object) else { + return; + }; + for (key, value) in usage { + if let Some(number) = value.as_i64().filter(|number| *number >= 0) { + destination.insert(key.clone(), number); + } + } +} + +fn string_field<'a>(event: &'a serde_json::Map, key: &str) -> &'a str { + event + .get(key) + .and_then(serde_json::Value::as_str) + .unwrap_or_default() +} + +fn invalid_data(message: &str) -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, message) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn codex_events_keep_claims_usage_and_session_provenance() { + let transcript = br#"{"type":"thread.started","thread_id":"t1"} +{"type":"item.completed","item":{"type":"agent_message","text":"provider says fixed"}} +{"type":"turn.completed","usage":{"input_tokens":8,"output_tokens":4,"invalid":-1,"fractional":1.5}}"#; + let result = parse_transcript("codex-exec", transcript).unwrap(); + assert!(result.completed); + assert_eq!(result.session, "t1"); + assert_eq!(result.claims, ["provider says fixed"]); + assert_eq!(result.usage.get("input_tokens"), Some(&8)); + assert_eq!(result.usage.get("output_tokens"), Some(&4)); + assert!(!result.usage.contains_key("invalid")); + assert!(!result.usage.contains_key("fractional")); + assert_eq!(result.provenance, PROVENANCE); + } + + #[test] + fn claude_terminal_result_captures_claim_cost_usage_and_failure() { + let success = br#"{"type":"result","session_id":"s1","is_error":false,"result":"provider says done","total_cost_usd":0.04,"usage":{"input_tokens":9}}"#; + let result = parse_transcript("claude-print", success).unwrap(); + assert!(result.completed); + assert_eq!(result.session, "s1"); + assert_eq!(result.claims, ["provider says done"]); + assert_eq!(result.estimated_cost_usd, Some(0.04)); + assert_eq!(result.usage.get("input_tokens"), Some(&9)); + + let failure = + br#"{"type":"result","session_id":"s2","is_error":true,"result":"provider error"}"#; + let result = parse_transcript("claude-print", failure).unwrap(); + assert!(!result.completed); + assert_eq!(result.error, "provider error"); + } + + #[test] + fn duplicate_event_identity_is_idempotent_but_conflicts_fail() { + let event = br#"{"type":"thread.started","thread_id":"thread","uuid":"event-1"}"#; + let mut transcript = event.to_vec(); + transcript.push(b'\n'); + transcript.extend_from_slice(event); + transcript.extend_from_slice(b"\n{\"type\":\"turn.completed\"}"); + let result = parse_transcript("codex-exec", &transcript).unwrap(); + assert!(result.completed); + + let conflict = br#"{"type":"thread.started","thread_id":"thread","uuid":"event-1"} +{"type":"thread.started","thread_id":"other","uuid":"event-1"}"#; + assert_eq!( + parse_transcript("codex-exec", conflict) + .unwrap_err() + .to_string(), + "conflicting duplicate event" + ); + } + + #[test] + fn nullable_codex_item_fields_match_go_zero_value_decoding() { + let transcript = br#"{"type":"thread.started","thread_id":"t"} +{"type":"item.completed","item":{"type":null,"text":null}} +{"type":"turn.completed"}"#; + let result = parse_transcript("codex-exec", transcript).unwrap(); + assert!(result.completed); + assert!(result.claims.is_empty()); + } + + #[test] + fn malformed_missing_terminal_and_event_order_fail_closed() { + for (transcript, expected) in [ + (&b"not-json"[..], "malformed native event"), + (&b"{}"[..], "event missing type"), + (&b"null"[..], "event missing type"), + ( + &br#"{"type":"turn.completed"}"#[..], + "completion without thread", + ), + ( + &br#"{"type":"result","session_id":"s","is_error":null}"#[..], + "terminal result requires session and is_error boolean", + ), + ( + &br#"{"type":"result","session_id":"s","is_error":false} +{"type":"system"}"#[..], + "event after terminal result", + ), + ( + &br#"{"type":"thread.started","thread_id":"a"} +{"type":"thread.started","thread_id":"b"}"#[..], + "invalid thread identity", + ), + ] { + let result = if expected.contains("terminal result") + || expected == "event after terminal result" + { + parse_transcript("claude-print", transcript) + } else { + parse_transcript("codex-exec", transcript) + }; + assert_eq!(result.unwrap_err().to_string(), expected, "{transcript:?}"); + } + } + + #[test] + fn size_claim_and_unknown_adapter_bounds_are_enforced() { + assert_eq!( + parse_transcript("codex-exec", &vec![b' '; MAX_TRANSCRIPT_BYTES + 1]) + .unwrap_err() + .to_string(), + "transcript exceeds limit" + ); + let mut transcript = String::from("{\"type\":\"thread.started\",\"thread_id\":\"t\"}\n"); + for _ in 0..=MAX_CLAIMS { + transcript.push_str("{\"type\":\"item.completed\",\"item\":{\"type\":\"agent_message\",\"text\":\"x\"}}\n"); + } + assert_eq!( + parse_transcript("codex-exec", transcript.as_bytes()) + .unwrap_err() + .to_string(), + "claim capture limit exceeded" + ); + let unknown = parse_transcript("generic-headless", b"not-json").unwrap(); + assert_eq!(unknown.adapter, "generic-headless"); + assert!(unknown.usage.is_empty()); + assert!(!unknown.completed); + } +} diff --git a/crates/rover-cli/Cargo.toml b/crates/rover-cli/Cargo.toml new file mode 100644 index 0000000..19e5193 --- /dev/null +++ b/crates/rover-cli/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "rover-cli" +version = "0.1.0" +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[[bin]] +name = "rover" +path = "src/main.rs" + +[dependencies] +rover-agents = { path = "../rover-agents" } +rover-core = { path = "../rover-core" } +rover-execution = { path = "../rover-execution" } +rover-store = { path = "../rover-store" } +rover-source = { path = "../rover-source" } +rover-tui = { path = "../rover-tui" } +rover-tasks = { path = "../rover-tasks" } +serde_json = { version = "=1.0.151", default-features = false, features = ["std"] } + +[dev-dependencies] +rusqlite = { version = "=0.40.2", default-features = false, features = ["bundled"] } + +[lints] +workspace = true diff --git a/crates/rover-cli/src/main.rs b/crates/rover-cli/src/main.rs new file mode 100644 index 0000000..d018715 --- /dev/null +++ b/crates/rover-cli/src/main.rs @@ -0,0 +1,2711 @@ +use std::collections::BTreeMap; +use std::env; +#[cfg(unix)] +use std::ffi::{OsStr, OsString}; +#[cfg(unix)] +use std::fs::{self, DirBuilder, OpenOptions}; +use std::io; +#[cfg(unix)] +use std::io::Write; +#[cfg(unix)] +use std::os::unix::fs::{DirBuilderExt, MetadataExt, OpenOptionsExt}; +use std::path::{Path, PathBuf}; +use std::process::{Child, Command, Stdio}; +#[cfg(unix)] +use std::sync::atomic::{AtomicU64, Ordering}; +use std::thread; +use std::time::{Duration, Instant}; + +#[cfg(unix)] +use rover_agents::{clear_session_binding, load_session_binding, SessionBindingError}; +#[cfg(unix)] +use rover_core::RepositoryIdentity; +#[cfg(unix)] +use rover_core::Sha256Digest; +#[cfg(unix)] +use rover_execution::layout::{PaneKind, Workspace}; +#[cfg(unix)] +use rover_execution::pty::PtyOptions; +#[cfg(unix)] +use rover_execution::sessions::{SessionClient, SessionKey, SessionServer}; +#[cfg(unix)] +use rover_store::files::{SafeDir, StateRoot}; +#[cfg(unix)] +use rover_store::RecordValue; +#[cfg(unix)] +use rover_tui::{ + SavedCommand, TaskCheckEvidence, TaskDiffChange, TaskDiffEvidence, TaskDraft, + TaskEvidenceFinding, TaskInvestigationEvidence, TaskPlanSummary, TaskProcessEvidence, + TaskProvider, TaskSortOrder, TaskSummary, TaskViewPreferences, MAX_SAVED_COMMANDS, + MAX_SAVED_COMMAND_LINE_BYTES, MAX_SAVED_COMMAND_NAME_BYTES, MAX_TASK_DRAFTS, + MAX_TASK_DRAFT_DEPENDENCIES_BYTES, MAX_TASK_DRAFT_GATE_BYTES, MAX_TASK_DRAFT_PATHS_BYTES, + MAX_TASK_DRAFT_PROMPT_BYTES, MAX_TASK_DRAFT_TITLE_BYTES, MAX_WORKSPACE_NOTES_BYTES, + MAX_WORKSPACE_NOTES_COUNT, MAX_WORKSPACE_NOTES_TOTAL_BYTES, +}; + +fn main() { + if let Err(error) = run() { + eprintln!("rover: {error}"); + std::process::exit(1); + } +} + +fn run() -> io::Result<()> { + let mut args = env::args_os().skip(1); + match args + .next() + .and_then(|arg| arg.into_string().ok()) + .as_deref() + { + Some("tui") => run_tui(args.collect()), + Some("agents") => run_agent_command("agents", args.collect()), + Some("agent") => run_agent_command("agent", args.collect()), + #[cfg(unix)] + Some("__session_daemon") => run_daemon(args.collect()), + _ => { + eprintln!("usage: rover tui [--state PATH] [--repo PATH] | rover agents [--json] | rover agent list | rover agent capabilities | rover agent session list --repo PATH [--state PATH] | rover agent session status --repo PATH --pane ID [--state PATH]"); + Err(io::Error::new( + io::ErrorKind::InvalidInput, + "unknown command", + )) + } + } +} + +fn run_agent_command(command: &str, args: Vec) -> io::Result<()> { + #[cfg(unix)] + if command == "agent" && args.first().and_then(|argument| argument.to_str()) == Some("session") + { + return run_agent_session_command(args); + } + let args = args + .into_iter() + .map(|arg| { + arg.into_string().map_err(|_| { + io::Error::new(io::ErrorKind::InvalidInput, "agent argument is not UTF-8") + }) + }) + .collect::>>()?; + let value = agent_command_value(command, &args)?; + println!("{}", serde_json::to_string_pretty(&value)?); + Ok(()) +} + +#[cfg(unix)] +fn run_agent_session_command(args: Vec) -> io::Result<()> { + let mut args = args.into_iter(); + let _ = args.next(); // `session` + let action = args + .next() + .and_then(|value| value.into_string().ok()) + .ok_or_else(agent_session_usage)?; + if !matches!(action.as_str(), "list" | "status" | "bind" | "clear") { + return Err(agent_session_usage()); + } + let mut options = BTreeMap::::new(); + while let Some(flag) = args.next() { + let flag = flag + .into_string() + .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "option is not UTF-8"))?; + if !matches!( + flag.as_str(), + "--repo" | "--state" | "--pane" | "--adapter" | "--id" + ) || options.contains_key(&flag) + { + return Err(agent_session_usage()); + } + let value = args.next().ok_or_else(agent_session_usage)?; + options.insert(flag, value); + } + let repository = options + .get("--repo") + .map(PathBuf::from) + .ok_or_else(agent_session_usage)?; + let repository = std::fs::canonicalize(repository)?; + if !repository.is_dir() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "repository path is not a directory", + )); + } + let pane = options + .get("--pane") + .map(|value| { + value.to_str().ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidInput, "pane identifier is not UTF-8") + }) + }) + .transpose()?; + let adapter = options + .get("--adapter") + .map(|value| value.to_str().ok_or_else(agent_session_usage)) + .transpose()?; + let session_id = options + .get("--id") + .map(|value| value.to_str().ok_or_else(agent_session_usage)) + .transpose()?; + let valid_options = match action.as_str() { + "list" => pane.is_none() && adapter.is_none() && session_id.is_none(), + "status" | "clear" => pane.is_some() && adapter.is_none() && session_id.is_none(), + "bind" => pane.is_some() && adapter.is_some() && session_id.is_some(), + _ => false, + }; + if !valid_options { + return Err(agent_session_usage()); + } + let state_path = options + .get("--state") + .map(PathBuf::from) + .map_or_else(default_state_path, Ok)?; + let state = StateRoot::open(state_path)?; + let repository_text = repository.to_str().ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "repository path is not valid UTF-8", + ) + })?; + let identity = RepositoryIdentity::resolve(repository_text)?; + let pane = if action == "bind" { + Some(resolve_agent_session_pane( + &state, + &identity, + pane.expect("validated pane"), + )?) + } else { + pane.map(str::to_owned) + }; + let value = agent_session_command_value( + state.records(), + &identity, + &action, + pane.as_deref(), + adapter, + session_id, + )?; + println!("{}", serde_json::to_string_pretty(&value)?); + Ok(()) +} + +#[cfg(unix)] +fn agent_session_command_value( + store: &rover_store::Store, + project: &RepositoryIdentity, + action: &str, + pane: Option<&str>, + adapter: Option<&str>, + session_id: Option<&str>, +) -> io::Result { + let summarize = |binding: &rover_agents::AgentSessionBinding| { + serde_json::json!({ + "pane_id": binding.pane_id(), + "adapter": binding.adapter(), + "session_id": binding.session_id(), + "source": binding.source().as_str(), + "binding_status": "bound", + "agent_state": "unknown", + "state_reason": "Rover has no live process or lifecycle observation for this binding" + }) + }; + if action == "bind" { + let binding = rover_agents::AgentSessionBinding::new( + project, + pane.ok_or_else(agent_session_usage)?, + adapter.ok_or_else(agent_session_usage)?, + session_id.ok_or_else(agent_session_usage)?, + rover_agents::AgentSessionSource::ExplicitUser, + ) + .map_err(|error| io::Error::new(io::ErrorKind::InvalidInput, error.to_string()))?; + rover_agents::save_session_binding(store, project, &binding) + .map_err(|error| io::Error::new(io::ErrorKind::InvalidInput, error.to_string()))?; + return Ok(serde_json::json!({ + "schema": rover_core::SCHEMA, + "session": summarize(&binding) + })); + } + if action == "clear" { + let pane_id = pane.ok_or_else(agent_session_usage)?; + rover_agents::clear_session_binding(store, project, pane_id) + .map_err(|error| io::Error::new(io::ErrorKind::InvalidInput, error.to_string()))?; + return Ok(serde_json::json!({ + "schema": rover_core::SCHEMA, + "pane_id": pane_id, + "binding_status": "cleared" + })); + } + let sessions = rover_agents::list_session_bindings(store, project) + .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error.to_string()))?; + match (action, pane) { + ("list", None) => Ok(serde_json::json!({ + "schema": rover_core::SCHEMA, + "sessions": sessions.iter().map(summarize).collect::>() + })), + ("status", Some(pane_id)) => { + let binding = sessions + .iter() + .find(|binding| binding.pane_id() == pane_id) + .ok_or_else(|| { + io::Error::new(io::ErrorKind::NotFound, "agent session not found") + })?; + Ok(serde_json::json!({ + "schema": rover_core::SCHEMA, + "session": summarize(binding) + })) + } + _ => Err(agent_session_usage()), + } +} + +#[cfg(unix)] +fn agent_session_usage() -> io::Error { + io::Error::new( + io::ErrorKind::InvalidInput, + "agent session list --repo PATH [--state PATH] | agent session status --repo PATH --pane ID [--state PATH] | agent session bind --repo PATH --pane ID --adapter codex-exec|claude-print --id EXACT_ID [--state PATH] | agent session clear --repo PATH --pane ID [--state PATH]", + ) +} + +#[cfg(unix)] +fn resolve_agent_session_pane( + state: &StateRoot, + project: &RepositoryIdentity, + requested_pane_id: &str, +) -> io::Result { + let namespace = format!( + "project_{}", + Sha256Digest::of(project.path().as_os_str().as_encoded_bytes()).to_hex() + ); + let layout_name = format!( + "workspace_{}.json", + Sha256Digest::of(namespace.as_bytes()).to_hex() + ); + let app = state.root_handle().ensure_subdirectory("rust-tui")?; + let layouts = app.ensure_subdirectory("layouts")?; + let workspace = Workspace::load_from(&layouts, &layout_name)?; + if workspace.namespace() != namespace { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "saved workspace belongs to another project", + )); + } + let pane_id = if requested_pane_id == "active" { + workspace.active_tab().active_pane() + } else { + requested_pane_id + }; + if find_active_pane(workspace.active_tab().root(), pane_id).is_none() { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "pane is not present in this project's saved workspace", + )); + } + Ok(pane_id.to_owned()) +} + +fn agent_command_value(command: &str, args: &[String]) -> io::Result { + let has_json = args.last().is_some_and(|arg| arg == "--json"); + let args = if has_json { + &args[..args.len() - 1] + } else { + args + }; + let string_args = args.iter().map(String::as_str).collect::>(); + match (command, string_args.as_slice()) { + ("agents", [] | ["list"]) => { + serde_json::to_value(rover_agents::profiles()).map_err(io::Error::other) + } + ("agent", [] | ["list"]) => Ok(serde_json::json!({ + "schema": rover_core::SCHEMA, + "adapters": rover_agents::profiles(), + })), + ("agent", ["capabilities", name]) => { + let descriptor = rover_agents::profile(name)?; + serde_json::to_value(descriptor).map_err(io::Error::other) + } + _ => Err(io::Error::new( + io::ErrorKind::InvalidInput, + "agents [--json] | agent list [--json] | agent capabilities [--json]", + )), + } +} + +#[cfg(unix)] +fn run_tui(args: Vec) -> io::Result<()> { + let options = parse_options(args)?; + let cwd = options.repo.unwrap_or(env::current_dir()?); + let cwd = std::fs::canonicalize(cwd)?; + if !cwd.is_dir() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "repository path is not a directory", + )); + } + let state_path = options.state.unwrap_or(default_state_path()?); + let state = StateRoot::open(state_path)?; + let tasks = load_project_tasks(&state, &cwd)?; + let view_preferences = load_project_task_view_preferences(&state, &cwd)?; + let workspace_notes = load_project_workspace_notes(&state, &cwd)?; + let saved_commands = load_project_saved_commands(&state, &cwd)?; + let app_dir = state.root_handle().ensure_subdirectory("rust-tui")?; + let layouts = app_dir.ensure_subdirectory("layouts")?; + let namespace = format!( + "project_{}", + Sha256Digest::of(cwd.as_os_str().as_encoded_bytes()).to_hex() + ); + // Keep AF_UNIX socket names below platform path limits even when the state + // root lives under a deeply nested home or temporary directory. + let owner_uid = std::fs::metadata(state.path())?.uid(); + let state_hash = Sha256Digest::of(state.path().as_os_str().as_encoded_bytes()).to_hex(); + let session_dir = PathBuf::from("/tmp").join(format!("rv-{owner_uid}-{}", &state_hash[..24])); + let key = SessionKey::new(&namespace, "shell")?; + let title = cwd + .file_name() + .and_then(|part| part.to_str()) + .unwrap_or("workspace"); + let initial = Workspace::new(&namespace, title, "shell", &cwd)?; + let layout_name = format!( + "workspace_{}.json", + Sha256Digest::of(namespace.as_bytes()).to_hex() + ); + let workspace = match Workspace::load_from(&layouts, &layout_name) { + Ok(workspace) => workspace, + Err(error) if error.kind() == io::ErrorKind::NotFound => initial, + Err(error) => return Err(error), + }; + if workspace.namespace() != namespace { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "saved workspace belongs to a different project", + )); + } + let pane_id = workspace.active_tab().active_pane().to_owned(); + let pane = find_active_pane(workspace.active_tab().root(), &pane_id) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "active pane is missing"))?; + if !matches!(pane.kind(), PaneKind::Session { name } if name == key.name()) { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "active pane does not refer to the project's shell session", + )); + } + let (client, _daemon) = connect_or_start(&session_dir, &key, &cwd)?; + let mut task_provider = TaskProvider::new( + tasks, + || load_project_tasks(&state, &cwd), + |digest| state.read_blob(digest), + |task_id| { + let now = rover_core::Timestamp::now() + .to_rfc3339() + .map_err(|error| io::Error::other(error.to_string()))?; + rover_execution::supervisor::request_cancel(state.records(), task_id, &now) + .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error.to_string())) + }, + |task| load_project_investigation(&state, &cwd, task), + |task| load_project_diff(&state, &cwd, task), + ); + task_provider.configure_view_preferences(&view_preferences, |preferences| { + save_project_task_view_preferences(&state, &cwd, preferences) + }); + task_provider.configure_workspace_notes(&workspace_notes, |notes| { + save_project_workspace_notes(&state, &cwd, notes) + }); + task_provider.configure_saved_commands(&saved_commands, |commands| { + save_project_saved_commands(&state, &cwd, commands) + }); + configure_task_drafts(&mut task_provider, &state, &cwd, &pane_id)?; + configure_task_plans(&mut task_provider, &state, &cwd)?; + task_provider.configure_file_browser( + |directory, include_hidden| rover_source::browse_directory(&cwd, directory, include_hidden), + || rover_source::quick_open_files(&cwd), + |path| rover_source::read_repository_file(&cwd, path), + ); + task_provider.configure_file_editor( + |path| rover_source::read_repository_file_for_edit(&cwd, path), + |path, expected_sha256, bytes| { + rover_source::save_repository_file(&cwd, path, expected_sha256, bytes) + }, + run_configured_editor, + ); + rover_tui::run_attached_session_persistent_with_task_provider( + workspace, + &pane_id, + client, + &layouts, + &layout_name, + task_provider, + ) +} + +#[cfg(unix)] +fn configure_saved_agent_session<'a>( + provider: &mut TaskProvider<'a>, + state: &'a StateRoot, + project: &Path, + pane_id: &str, +) -> io::Result<()> { + let repository = project.to_str().ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "repository path is not valid UTF-8", + ) + })?; + let identity = RepositoryIdentity::resolve(repository)?; + match load_session_binding(state.records(), &identity, pane_id) { + Ok(binding) => { + let repository_for_clear = repository.to_owned(); + provider.configure_agent_session(&binding, move |bound_pane| { + let identity = + RepositoryIdentity::resolve(&repository_for_clear).map_err(|error| { + io::Error::new(io::ErrorKind::InvalidData, error.to_string()) + })?; + clear_session_binding(state.records(), &identity, bound_pane) + .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error.to_string())) + })?; + } + Err(SessionBindingError::Store(rover_store::StoreError::NotFound)) => {} + Err(error) => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("could not load saved agent session: {error}"), + )); + } + } + Ok(()) +} + +#[cfg(unix)] +fn configure_task_drafts<'a>( + provider: &mut TaskProvider<'a>, + state: &'a StateRoot, + project: &'a Path, + pane_id: &str, +) -> io::Result<()> { + configure_saved_agent_session(provider, state, project, pane_id)?; + let drafts = load_project_task_drafts(state, project)?; + provider.configure_task_drafts(&drafts, |drafts| { + save_project_task_drafts(state, project, drafts) + }); + Ok(()) +} + +#[cfg(unix)] +fn configure_task_plans<'a>( + provider: &mut TaskProvider<'a>, + state: &'a StateRoot, + project: &'a Path, +) -> io::Result<()> { + let plans = load_project_task_plan_summaries(state, project)?; + provider.configure_task_plans( + &plans, + || load_project_task_plan_summaries(state, project), + |draft| { + let repository = project.to_str().ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "repository path is not valid UTF-8", + ) + })?; + let identity = rover_core::RepositoryIdentity::resolve(repository)?; + let brief = rover_core::TaskBrief { + title: draft.title.clone(), + paths: draft.paths.clone(), + dependencies: draft.dependencies.clone(), + quality_gate: draft.quality_gate.clone(), + prompt: draft.prompt.clone(), + }; + let plan = rover_tasks::TaskPlanService::new(state.records()) + .create(&identity, brief) + .map_err(|error| io::Error::new(io::ErrorKind::InvalidInput, error.to_string()))?; + let service = rover_tasks::TaskPlanService::new(state.records()); + let readiness = service + .readiness(&identity, &plan.id) + .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error.to_string()))?; + Ok(task_plan_summary(&plan, readiness)) + }, + ); + Ok(()) +} + +#[cfg(unix)] +fn load_project_task_plan_summaries( + state: &StateRoot, + project: &Path, +) -> io::Result> { + let repository = project.to_str().ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "repository path is not valid UTF-8", + ) + })?; + let identity = rover_core::RepositoryIdentity::resolve(repository)?; + let service = rover_tasks::TaskPlanService::new(state.records()); + service + .list_with_readiness(&identity) + .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error.to_string()))? + .iter() + .map(|(plan, readiness)| Ok(task_plan_summary(plan, *readiness))) + .collect() +} + +#[cfg(unix)] +fn task_plan_summary( + plan: &rover_tasks::TaskPlan, + readiness: rover_tasks::TaskReadiness, +) -> TaskPlanSummary { + let readiness = match readiness { + rover_tasks::TaskReadiness::Ready => "READY", + rover_tasks::TaskReadiness::Waiting => "WAITING", + rover_tasks::TaskReadiness::Blocked => "BLOCKED", + }; + TaskPlanSummary { + id: plan.id.to_string(), + title: plan.brief.title.clone(), + status: plan.status.as_str().to_owned(), + readiness: readiness.to_owned(), + dependency_count: plan.dependencies.len(), + attempt_count: plan.attempts.len(), + updated_at: plan.updated_at.clone(), + } +} + +#[cfg(unix)] +static EDITOR_TEMP_SEQUENCE: AtomicU64 = AtomicU64::new(0); + +#[cfg(unix)] +struct EditorTempDirectory(PathBuf); + +#[cfg(unix)] +impl Drop for EditorTempDirectory { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.0); + } +} + +/// Run the user's configured editor against a private copy, returning its +/// bytes for the TUI's normal stale-check and atomic-save path. +#[cfg(unix)] +fn run_configured_editor(relative_path: &str, original: &[u8]) -> io::Result> { + let editor = env::var_os("VISUAL") + .filter(|value| !value.is_empty()) + .or_else(|| env::var_os("EDITOR").filter(|value| !value.is_empty())) + .unwrap_or_else(|| "vi".into()); + run_editor_program(relative_path, original, &editor) +} + +#[cfg(unix)] +fn run_editor_program(relative_path: &str, original: &[u8], editor: &OsStr) -> io::Result> { + const MAX_EDIT_BYTES: usize = 8 * 1024 * 1024; + if original.len() > MAX_EDIT_BYTES { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "file exceeds the 8 MiB external-edit limit", + )); + } + let temporary = create_editor_temp_directory()?; + let safe_directory = SafeDir::open(&temporary.0)?; + let extension = Path::new(relative_path) + .extension() + .and_then(|extension| extension.to_str()) + .filter(|extension| { + !extension.is_empty() + && extension.len() <= 16 + && extension + .bytes() + .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'-')) + }) + .unwrap_or("txt"); + let file_name = format!("working-copy.{extension}"); + let file_path = temporary.0.join(&file_name); + let mut options = OpenOptions::new(); + options.write(true).create_new(true).mode(0o600); + let mut file = options.open(&file_path)?; + file.write_all(original)?; + file.sync_all()?; + drop(file); + + // VISUAL and EDITOR are executable paths, not shell command lines. This + // avoids evaluating user configuration through a shell. + let status = Command::new(editor).arg(&file_path).status()?; + if !status.success() { + return Err(io::Error::other(format!( + "external editor exited with {status}" + ))); + } + let (bytes, truncated) = safe_directory.read_regular_file_path(&file_name, MAX_EDIT_BYTES)?; + if truncated { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "external editor output exceeds the 8 MiB edit limit", + )); + } + if bytes.len() > MAX_EDIT_BYTES { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "external editor output exceeds the 8 MiB edit limit", + )); + } + if std::str::from_utf8(&bytes).is_err() { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "external editor output is not valid UTF-8", + )); + } + Ok(bytes) +} + +#[cfg(unix)] +fn create_editor_temp_directory() -> io::Result { + for _ in 0..128 { + let sequence = EDITOR_TEMP_SEQUENCE.fetch_add(1, Ordering::Relaxed); + let path = env::temp_dir().join(format!("rover-edit-{}-{sequence}", std::process::id())); + match DirBuilder::new().mode(0o700).create(&path) { + Ok(()) => return Ok(EditorTempDirectory(path)), + Err(error) if error.kind() == io::ErrorKind::AlreadyExists => {} + Err(error) => return Err(error), + } + } + Err(io::Error::new( + io::ErrorKind::AlreadyExists, + "could not allocate a private external-editor directory", + )) +} + +#[cfg(unix)] +fn project_task_view_preferences_id(project: &Path) -> String { + format!( + "tui_{}", + Sha256Digest::of(project.as_os_str().as_encoded_bytes()).to_hex() + ) +} + +#[cfg(unix)] +fn load_project_task_view_preferences( + state: &StateRoot, + project: &Path, +) -> io::Result { + let id = project_task_view_preferences_id(project); + let record = match state + .records() + .get_typed::("tui_preferences", &id) + { + Ok(record) => record, + Err(rover_store::StoreError::NotFound) => return Ok(TaskViewPreferences::default()), + Err(error) => return Err(io::Error::new(io::ErrorKind::InvalidData, error)), + }; + let invalid = || io::Error::new(io::ErrorKind::InvalidData, "invalid Rover TUI preferences"); + if record.get("schema").and_then(serde_json::Value::as_str) != Some("rover/tui-task-view/v1") + || record.get("repository").and_then(serde_json::Value::as_str) != project.to_str() + { + return Err(invalid()); + } + let query = record + .get("task_query") + .and_then(serde_json::Value::as_str) + .filter(|query| query.len() <= 128) + .ok_or_else(invalid)?; + let sort = record + .get("task_sort") + .and_then(serde_json::Value::as_str) + .and_then(TaskSortOrder::parse) + .ok_or_else(invalid)?; + Ok(TaskViewPreferences { + query: query.to_owned(), + sort, + }) +} + +#[cfg(unix)] +fn save_project_task_view_preferences( + state: &StateRoot, + project: &Path, + preferences: &TaskViewPreferences, +) -> io::Result<()> { + if preferences.query.len() > 128 { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "task search exceeds 128 UTF-8 bytes", + )); + } + let repository = project.to_str().ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidInput, "repository path is not UTF-8") + })?; + let record = serde_json::json!({ + "schema": "rover/tui-task-view/v1", + "repository": repository, + "task_query": preferences.query, + "task_sort": preferences.sort.as_str(), + }); + state + .records() + .put( + "tui_preferences", + &project_task_view_preferences_id(project), + &record, + "tui.preferences", + ) + .map_err(|error| io::Error::other(error.to_string())) +} + +#[cfg(unix)] +fn project_workspace_notes_id(project: &Path) -> String { + format!( + "tui_notes_{}", + Sha256Digest::of(project.as_os_str().as_encoded_bytes()).to_hex() + ) +} + +#[cfg(unix)] +fn load_project_workspace_notes(state: &StateRoot, project: &Path) -> io::Result> { + let record = match state + .records() + .get_typed::("tui_notes", &project_workspace_notes_id(project)) + { + Ok(record) => record, + Err(rover_store::StoreError::NotFound) => return Ok(Vec::new()), + Err(error) => return Err(io::Error::new(io::ErrorKind::InvalidData, error)), + }; + let invalid = || io::Error::new(io::ErrorKind::InvalidData, "invalid Rover repository notes"); + if record.get("repository").and_then(serde_json::Value::as_str) != project.to_str() { + return Err(invalid()); + } + let schema = record.get("schema").and_then(serde_json::Value::as_str); + let notes = match schema { + Some("rover/tui-notes/v1") => record + .get("notes") + .and_then(serde_json::Value::as_str) + .filter(|note| note.len() <= MAX_WORKSPACE_NOTES_BYTES) + .map(|note| { + if note.is_empty() { + Vec::new() + } else { + vec![note.to_owned()] + } + }) + .ok_or_else(invalid)?, + Some("rover/tui-notes/v2") => record + .get("notes") + .and_then(serde_json::Value::as_array) + .filter(|notes| notes.len() <= MAX_WORKSPACE_NOTES_COUNT) + .and_then(|notes| { + notes + .iter() + .map(serde_json::Value::as_str) + .collect::>>() + }) + .filter(|notes| { + notes + .iter() + .all(|note| note.len() <= MAX_WORKSPACE_NOTES_BYTES) + && notes.iter().map(|note| note.len()).sum::() + <= MAX_WORKSPACE_NOTES_TOTAL_BYTES + }) + .map(|notes| notes.into_iter().map(str::to_owned).collect()) + .ok_or_else(invalid)?, + _ => return Err(invalid()), + }; + Ok(notes) +} + +#[cfg(unix)] +fn save_project_workspace_notes( + state: &StateRoot, + project: &Path, + notes: &[String], +) -> io::Result<()> { + if notes.len() > MAX_WORKSPACE_NOTES_COUNT + || notes + .iter() + .any(|note| note.len() > MAX_WORKSPACE_NOTES_BYTES) + || notes.iter().map(String::len).sum::() > MAX_WORKSPACE_NOTES_TOTAL_BYTES + { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "workspace notes exceed collection limits", + )); + } + let repository = project.to_str().ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidInput, "repository path is not UTF-8") + })?; + let record = serde_json::json!({ + "schema": "rover/tui-notes/v2", + "repository": repository, + "notes": notes, + }); + state + .records() + .put( + "tui_notes", + &project_workspace_notes_id(project), + &record, + "tui.notes", + ) + .map_err(|error| io::Error::other(error.to_string())) +} + +#[cfg(unix)] +fn project_saved_commands_id(project: &Path) -> String { + format!( + "tui_commands_{}", + Sha256Digest::of(project.as_os_str().as_encoded_bytes()).to_hex() + ) +} + +#[cfg(unix)] +fn load_project_saved_commands(state: &StateRoot, project: &Path) -> io::Result> { + let record = match state + .records() + .get_typed::("tui_saved_commands", &project_saved_commands_id(project)) + { + Ok(record) => record, + Err(rover_store::StoreError::NotFound) => return Ok(Vec::new()), + Err(error) => return Err(io::Error::new(io::ErrorKind::InvalidData, error)), + }; + let invalid = || io::Error::new(io::ErrorKind::InvalidData, "invalid Rover saved commands"); + if record.get("schema").and_then(serde_json::Value::as_str) + != Some("rover/tui-saved-commands/v1") + || record.get("repository").and_then(serde_json::Value::as_str) != project.to_str() + { + return Err(invalid()); + } + let commands = record + .get("commands") + .and_then(serde_json::Value::as_array) + .filter(|commands| commands.len() <= MAX_SAVED_COMMANDS) + .and_then(|commands| { + commands + .iter() + .map(|entry| { + Some(SavedCommand { + name: entry.get("name")?.as_str()?.to_owned(), + command: entry.get("command")?.as_str()?.to_owned(), + }) + }) + .collect::>>() + }) + .ok_or_else(invalid)?; + if commands + .iter() + .any(|command| !valid_saved_command_record(command)) + || commands.iter().enumerate().any(|(index, command)| { + commands[..index] + .iter() + .any(|prior| prior.name == command.name) + }) + { + return Err(invalid()); + } + Ok(commands) +} + +#[cfg(unix)] +fn save_project_saved_commands( + state: &StateRoot, + project: &Path, + commands: &[SavedCommand], +) -> io::Result<()> { + if commands.len() > MAX_SAVED_COMMANDS + || commands + .iter() + .any(|command| !valid_saved_command_record(command)) + || commands.iter().enumerate().any(|(index, command)| { + commands[..index] + .iter() + .any(|prior| prior.name == command.name) + }) + { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "saved commands exceed limits or contain invalid/duplicate entries", + )); + } + let repository = project.to_str().ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidInput, "repository path is not UTF-8") + })?; + let record = serde_json::json!({ + "schema": "rover/tui-saved-commands/v1", + "repository": repository, + "commands": commands.iter().map(|command| { + serde_json::json!({"name": command.name, "command": command.command}) + }).collect::>(), + }); + state + .records() + .put( + "tui_saved_commands", + &project_saved_commands_id(project), + &record, + "tui.saved-command", + ) + .map_err(|error| io::Error::other(error.to_string())) +} + +#[cfg(unix)] +fn valid_saved_command_record(command: &SavedCommand) -> bool { + !command.name.trim().is_empty() + && command.name.len() <= MAX_SAVED_COMMAND_NAME_BYTES + && !command.command.trim().is_empty() + && command.command.len() <= MAX_SAVED_COMMAND_LINE_BYTES + && !command.command.contains('\n') + && !command.command.contains('\r') + && command + .name + .chars() + .chain(command.command.chars()) + .all(|character| { + !character.is_control() + && !matches!( + character, + '\u{061c}' + | '\u{200e}' + | '\u{200f}' + | '\u{202a}'..='\u{202e}' + | '\u{2066}'..='\u{2069}' + | '\u{feff}' + ) + }) +} + +#[cfg(unix)] +fn project_task_drafts_id(project: &Path) -> String { + format!( + "tui_drafts_{}", + Sha256Digest::of(project.as_os_str().as_encoded_bytes()).to_hex() + ) +} + +#[cfg(unix)] +fn load_project_task_drafts(state: &StateRoot, project: &Path) -> io::Result> { + let record = match state + .records() + .get_typed::("tui_task_drafts", &project_task_drafts_id(project)) + { + Ok(record) => record, + Err(rover_store::StoreError::NotFound) => return Ok(Vec::new()), + Err(error) => return Err(io::Error::new(io::ErrorKind::InvalidData, error)), + }; + let invalid = || io::Error::new(io::ErrorKind::InvalidData, "invalid Rover task drafts"); + let schema = record.get("schema").and_then(serde_json::Value::as_str); + let legacy_v1 = schema == Some("rover/tui-task-drafts/v1"); + if (!legacy_v1 && schema != Some("rover/tui-task-drafts/v2")) + || record.get("repository").and_then(serde_json::Value::as_str) != project.to_str() + { + return Err(invalid()); + } + let drafts = record + .get("drafts") + .and_then(serde_json::Value::as_array) + .filter(|drafts| drafts.len() <= MAX_TASK_DRAFTS) + .and_then(|drafts| { + drafts + .iter() + .map(|entry| { + Some(TaskDraft { + title: entry.get("title")?.as_str()?.to_owned(), + paths: if legacy_v1 { + String::new() + } else { + entry.get("paths")?.as_str()?.to_owned() + }, + dependencies: if legacy_v1 { + String::new() + } else { + entry.get("dependencies")?.as_str()?.to_owned() + }, + quality_gate: if legacy_v1 { + String::new() + } else { + entry.get("quality_gate")?.as_str()?.to_owned() + }, + prompt: entry.get("prompt")?.as_str()?.to_owned(), + }) + }) + .collect::>>() + }) + .ok_or_else(invalid)?; + if drafts.iter().any(|draft| !valid_task_draft_record(draft)) { + return Err(invalid()); + } + Ok(drafts) +} + +#[cfg(unix)] +fn save_project_task_drafts( + state: &StateRoot, + project: &Path, + drafts: &[TaskDraft], +) -> io::Result<()> { + if drafts.len() > MAX_TASK_DRAFTS || drafts.iter().any(|draft| !valid_task_draft_record(draft)) + { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "task drafts exceed limits or contain unsupported control text", + )); + } + let repository = project.to_str().ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidInput, "repository path is not UTF-8") + })?; + let record = serde_json::json!({ + "schema": "rover/tui-task-drafts/v2", + "repository": repository, + "drafts": drafts.iter().map(|draft| { + serde_json::json!({ + "title": draft.title, + "paths": draft.paths, + "dependencies": draft.dependencies, + "quality_gate": draft.quality_gate, + "prompt": draft.prompt, + }) + }).collect::>(), + }); + state + .records() + .put( + "tui_task_drafts", + &project_task_drafts_id(project), + &record, + "tui.task-drafts", + ) + .map_err(|error| io::Error::other(error.to_string())) +} + +#[cfg(unix)] +fn valid_task_draft_record(draft: &TaskDraft) -> bool { + draft.title.len() <= MAX_TASK_DRAFT_TITLE_BYTES + && draft.paths.len() <= MAX_TASK_DRAFT_PATHS_BYTES + && draft.dependencies.len() <= MAX_TASK_DRAFT_DEPENDENCIES_BYTES + && draft.quality_gate.len() <= MAX_TASK_DRAFT_GATE_BYTES + && draft.prompt.len() <= MAX_TASK_DRAFT_PROMPT_BYTES + && draft + .title + .chars() + .all(|character| !character.is_control() && !is_bidi_format(character)) + && [ + draft.paths.as_str(), + draft.dependencies.as_str(), + draft.quality_gate.as_str(), + draft.prompt.as_str(), + ] + .into_iter() + .all(|value| { + value.chars().all(|character| { + (character == '\n' || !character.is_control()) && !is_bidi_format(character) + }) + }) +} + +#[cfg(unix)] +fn is_bidi_format(character: char) -> bool { + matches!( + character, + '\u{061c}' + | '\u{200e}' + | '\u{200f}' + | '\u{202a}'..='\u{202e}' + | '\u{2066}'..='\u{2069}' + | '\u{feff}' + ) +} + +#[cfg(unix)] +fn load_project_tasks(state: &StateRoot, project: &Path) -> io::Result> { + let records = state + .records() + .list_all("task", 1_000) + .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?; + let mut summaries = Vec::new(); + for record in records { + let id = required_record_string(&record, &["id"])?; + let status = required_record_string(&record, &["status"])?; + let updated_at = required_record_string(&record, &["updated_at"])?; + let objective = required_record_string(&record, &["contract", "objective"])?; + let repository = required_record_string(&record, &["contract", "repository"])?; + let error = optional_record_string(&record, &["error"])?.filter(|text| !text.is_empty()); + let candidate = + optional_record_string(&record, &["candidate"])?.filter(|text| !text.is_empty()); + let investigation_id = + optional_record_string(&record, &["investigation_id"])?.filter(|text| !text.is_empty()); + let base_snapshot = + optional_record_string(&record, &["base_snapshot"])?.filter(|text| !text.is_empty()); + rover_core::Id::parse(id.to_owned()).map_err(|_| invalid_task_record("id"))?; + if let Some(investigation_id) = investigation_id { + rover_core::Id::parse(investigation_id.to_owned()) + .map_err(|_| invalid_task_record("investigation_id"))?; + } + let repository = Path::new(repository); + if !repository.is_absolute() { + return Err(invalid_task_record("contract.repository")); + } + let same_project = repository == project + || std::fs::canonicalize(repository).is_ok_and(|canonical| canonical == project); + if same_project { + let attempt_count = if record.contains_path(&["attempts"]) { + record + .array_len_at_path(&["attempts"]) + .ok_or_else(|| invalid_task_record("attempts"))? + } else { + 0 + }; + let process = if record.contains_path(&["process"]) { + Some(parse_task_process(&record)?) + } else { + None + }; + summaries.push(TaskSummary { + id: id.to_owned(), + objective: objective.to_owned(), + status: status.to_owned(), + updated_at: updated_at.to_owned(), + error: error.map(str::to_owned), + candidate: candidate.map(str::to_owned), + base_snapshot: base_snapshot.map(str::to_owned), + investigation_id: investigation_id.map(str::to_owned), + attempt_count, + process, + }); + } + } + Ok(summaries) +} + +#[cfg(unix)] +fn load_project_investigation( + state: &StateRoot, + project: &Path, + task: &TaskSummary, +) -> io::Result> { + let Some(investigation_id) = task.investigation_id.as_deref() else { + return Ok(None); + }; + let record = state + .records() + .get("investigation", investigation_id) + .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?; + let id = required_record_string(&record, &["id"])?; + if id != investigation_id { + return Err(invalid_evidence_record("id does not match task reference")); + } + let schema = required_record_string(&record, &["schema"])?; + if schema != "rover/v1alpha1" { + return Err(invalid_evidence_record("schema")); + } + let repository = required_record_string(&record, &["repository"])?; + let repository = Path::new(repository); + if !repository.is_absolute() + || !(repository == project + || std::fs::canonicalize(repository).is_ok_and(|canonical| canonical == project)) + { + return Err(invalid_evidence_record("repository does not match project")); + } + let candidate = required_record_string(&record, &["candidate"])?; + if task.candidate.as_deref() != Some(candidate) { + return Err(invalid_evidence_record("candidate does not match task")); + } + let decision = required_record_string(&record, &["decision"])?; + let decision_reason = optional_record_string(&record, &["decision_reason"])? + .unwrap_or_default() + .to_owned(); + + let check_values = record + .array_values_at_path(&["checks"]) + .ok_or_else(|| invalid_evidence_record("checks"))?; + if check_values.len() > 256 { + return Err(invalid_evidence_record("too many checks")); + } + let mut checks = Vec::with_capacity(check_values.len()); + for value in check_values { + checks.push(TaskCheckEvidence { + id: required_evidence_value_string(value, "id")?.to_owned(), + outcome: required_evidence_value_string(value, "outcome")?.to_owned(), + meaning: required_evidence_value_string(value, "meaning")?.to_owned(), + required: required_evidence_value_bool(value, "required")?, + tests: required_evidence_value_integer(value, "tests")?, + skipped: required_evidence_value_integer(value, "skipped")?, + }); + if checks + .last() + .is_some_and(|check| check.tests < 0 || check.skipped < 0) + { + return Err(invalid_evidence_record("negative check counts")); + } + } + + let unknown_values = record + .array_values_at_path(&["unknowns"]) + .ok_or_else(|| invalid_evidence_record("unknowns"))?; + if unknown_values.len() > 256 { + return Err(invalid_evidence_record("too many unknowns")); + } + let unknowns = unknown_values + .iter() + .map(|value| { + value + .as_str() + .map(str::to_owned) + .ok_or_else(|| invalid_evidence_record("unknowns item")) + }) + .collect::>>()?; + + let finding_values = record + .array_values_at_path(&["findings"]) + .ok_or_else(|| invalid_evidence_record("findings"))?; + if finding_values.len() > 256 { + return Err(invalid_evidence_record("too many findings")); + } + let mut findings = Vec::with_capacity(finding_values.len()); + for value in finding_values { + findings.push(TaskEvidenceFinding { + path: optional_evidence_value_string(value, "path")? + .unwrap_or_default() + .to_owned(), + message: required_evidence_value_string(value, "message")?.to_owned(), + severity: optional_evidence_value_string(value, "severity")?.map(str::to_owned), + }); + } + + Ok(Some(TaskInvestigationEvidence { + id: id.to_owned(), + candidate: candidate.to_owned(), + decision: decision.to_owned(), + decision_reason, + checks, + unknowns, + findings, + })) +} + +#[cfg(unix)] +fn load_project_diff( + state: &StateRoot, + project: &Path, + task: &TaskSummary, +) -> io::Result> { + let (Some(base_id), Some(candidate_id)) = + (task.base_snapshot.as_deref(), task.candidate.as_deref()) + else { + return Ok(None); + }; + rover_core::Id::parse(base_id.to_owned()).map_err(|_| invalid_task_record("base_snapshot"))?; + rover_core::Id::parse(candidate_id.to_owned()).map_err(|_| invalid_task_record("candidate"))?; + + let base = state + .records() + .get_typed::("snapshot", base_id) + .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?; + let candidate = state + .records() + .get_typed::("snapshot", candidate_id) + .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?; + if base.id != base_id || candidate.id != candidate_id { + return Err(invalid_task_record("snapshot record ID")); + } + for snapshot in [&base, &candidate] { + let repository = Path::new(&snapshot.repository); + if !repository.is_absolute() + || !(repository == project + || std::fs::canonicalize(repository).is_ok_and(|canonical| canonical == project)) + { + return Err(invalid_task_record( + "snapshot repository does not match project", + )); + } + rover_source::verify_retained_snapshot(state, &snapshot.id) + .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?; + } + let comparison = rover_source::compare_snapshots(&base, &candidate) + .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?; + if comparison.changes.len() > rover_source::MAX_FILES.saturating_mul(2) { + return Err(invalid_task_record("too many snapshot changes")); + } + Ok(Some(TaskDiffEvidence { + base_snapshot: base.id, + candidate: candidate.id, + changes: comparison + .changes + .into_iter() + .map(|change| TaskDiffChange { + path: change.path, + status: change.status, + category: change.category, + }) + .collect(), + })) +} + +#[cfg(unix)] +fn required_evidence_value_string<'a>( + value: &'a serde_json::Value, + field: &str, +) -> io::Result<&'a str> { + optional_evidence_value_string(value, field)? + .filter(|text| !text.is_empty()) + .ok_or_else(|| invalid_evidence_record(field)) +} + +#[cfg(unix)] +fn optional_evidence_value_string<'a>( + value: &'a serde_json::Value, + field: &str, +) -> io::Result> { + let object = value + .as_object() + .ok_or_else(|| invalid_evidence_record("array item must be an object"))?; + match object.get(field) { + None | Some(serde_json::Value::Null) => Ok(None), + Some(value) => value + .as_str() + .map(Some) + .ok_or_else(|| invalid_evidence_record(field)), + } +} + +#[cfg(unix)] +fn required_evidence_value_bool(value: &serde_json::Value, field: &str) -> io::Result { + value + .as_object() + .and_then(|object| object.get(field)) + .and_then(serde_json::Value::as_bool) + .ok_or_else(|| invalid_evidence_record(field)) +} + +#[cfg(unix)] +fn required_evidence_value_integer(value: &serde_json::Value, field: &str) -> io::Result { + value + .as_object() + .and_then(|object| object.get(field)) + .and_then(serde_json::Value::as_i64) + .ok_or_else(|| invalid_evidence_record(field)) +} + +#[cfg(unix)] +fn invalid_evidence_record(field: &str) -> io::Error { + io::Error::new( + io::ErrorKind::InvalidData, + format!("invalid investigation record: {field}"), + ) +} + +#[cfg(unix)] +fn parse_task_process(record: &RecordValue) -> io::Result { + let stdout_sha256 = required_record_string(record, &["process", "stdout_sha256"])?; + let stderr_sha256 = required_record_string(record, &["process", "stderr_sha256"])?; + rover_core::Sha256Digest::parse_hex(stdout_sha256) + .map_err(|_| invalid_task_record("process.stdout_sha256"))?; + rover_core::Sha256Digest::parse_hex(stderr_sha256) + .map_err(|_| invalid_task_record("process.stderr_sha256"))?; + let stdout_bytes = required_record_integer(record, &["process", "stdout_bytes"])?; + let stderr_bytes = required_record_integer(record, &["process", "stderr_bytes"])?; + if stdout_bytes < 0 || stderr_bytes < 0 { + return Err(invalid_task_record("process output byte count")); + } + Ok(TaskProcessEvidence { + exit_code: required_record_integer(record, &["process", "exit_code"])?, + error: optional_record_string(record, &["process", "error"])? + .filter(|text| !text.is_empty()) + .map(str::to_owned), + timed_out: required_record_boolean(record, &["process", "timed_out"])?, + cancelled: required_record_boolean(record, &["process", "cancelled"])?, + truncated: required_record_boolean(record, &["process", "truncated"])?, + stdout_bytes, + stderr_bytes, + stdout_sha256: stdout_sha256.to_owned(), + stderr_sha256: stderr_sha256.to_owned(), + }) +} + +#[cfg(unix)] +fn required_record_string<'a>(record: &'a RecordValue, path: &[&str]) -> io::Result<&'a str> { + record + .string_at_path(path) + .filter(|text| !text.is_empty()) + .ok_or_else(|| invalid_task_record(&path.join("."))) +} + +#[cfg(unix)] +fn optional_record_string<'a>( + record: &'a RecordValue, + path: &[&str], +) -> io::Result> { + if record.contains_path(path) { + record + .string_at_path(path) + .map(Some) + .ok_or_else(|| invalid_task_record(&path.join("."))) + } else { + Ok(None) + } +} + +#[cfg(unix)] +fn required_record_integer(record: &RecordValue, path: &[&str]) -> io::Result { + record + .integer_at_path(path) + .ok_or_else(|| invalid_task_record(&path.join("."))) +} + +#[cfg(unix)] +fn required_record_boolean(record: &RecordValue, path: &[&str]) -> io::Result { + record + .bool_at_path(path) + .ok_or_else(|| invalid_task_record(&path.join("."))) +} + +#[cfg(unix)] +fn invalid_task_record(field: &str) -> io::Error { + io::Error::new( + io::ErrorKind::InvalidData, + format!("stored Rover task has missing or invalid {field}"), + ) +} + +#[cfg(unix)] +fn find_active_pane<'a>( + node: &'a rover_execution::layout::PaneNode, + pane_id: &str, +) -> Option<&'a rover_execution::layout::Pane> { + match node { + rover_execution::layout::PaneNode::Leaf(pane) => (pane.id() == pane_id).then_some(pane), + rover_execution::layout::PaneNode::Split(split) => find_active_pane(split.first(), pane_id) + .or_else(|| find_active_pane(split.second(), pane_id)), + } +} + +#[cfg(unix)] +fn connect_or_start( + dir: &Path, + key: &SessionKey, + cwd: &Path, +) -> io::Result<(SessionClient, Option)> { + match SessionClient::connect_named(dir, key) { + Ok(client) => return Ok((client, None)), + Err(error) if error.kind() == io::ErrorKind::NotFound => {} + Err(error) => return Err(error), + } + let shell = env::var_os("SHELL") + .map(PathBuf::from) + .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "SHELL is not set"))?; + if !shell.is_absolute() || !shell.is_file() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "SHELL must name an existing absolute executable", + )); + } + let executable = env::current_exe()?; + let mut command = Command::new(executable); + command + .arg("__session_daemon") + .arg("--sessions") + .arg(dir) + .arg("--namespace") + .arg(key.namespace()) + .arg("--name") + .arg(key.name()) + .arg("--cwd") + .arg(cwd) + .arg("--shell") + .arg(&shell) + .env_clear(); + for name in ["PATH", "HOME", "USER", "LOGNAME", "TERM", "LANG", "SHELL"] { + if let Some(value) = env::var_os(name) { + command.env(name, value); + } + } + for (name, value) in env::vars_os() { + if name.to_string_lossy().starts_with("LC_") { + command.env(name, value); + } + } + let mut child = command + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn()?; + let deadline = Instant::now() + Duration::from_secs(3); + loop { + if let Ok(client) = SessionClient::connect_named(dir, key) { + return Ok((client, Some(child))); + } + if let Some(status) = child.try_wait()? { + return Err(io::Error::other(format!( + "local session owner exited during startup ({status})" + ))); + } + if Instant::now() >= deadline { + return Err(io::Error::new( + io::ErrorKind::TimedOut, + "local session owner did not become ready", + )); + } + thread::sleep(Duration::from_millis(25)); + } +} + +#[cfg(unix)] +fn run_daemon(args: Vec) -> io::Result<()> { + let values = parse_named(args)?; + let sessions = required_path(&values, "sessions")?; + let namespace = required_string(&values, "namespace")?; + let name = required_string(&values, "name")?; + let cwd = required_path(&values, "cwd")?; + let shell = required_path(&values, "shell")?; + let key = SessionKey::new(namespace, name)?; + let mut child_env = BTreeMap::new(); + for name in ["PATH", "HOME", "USER", "LOGNAME", "TERM", "LANG", "SHELL"] { + if let Some(value) = env::var_os(name).and_then(|value| value.into_string().ok()) { + child_env.insert(name.to_owned(), value); + } + } + for (name, value) in env::vars() { + if name.starts_with("LC_") { + child_env.insert(name, value); + } + } + let shell = shell + .into_os_string() + .into_string() + .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "shell path is not UTF-8"))?; + let server = SessionServer::new(sessions).map_err(|error| { + io::Error::new(error.kind(), format!("prepare session server: {error}")) + })?; + server + .spawn( + key.clone(), + &PtyOptions { + argv: vec![shell, "-l".to_owned()], + cwd, + env: child_env, + rows: 24, + cols: 80, + }, + ) + .map_err(|error| io::Error::new(error.kind(), format!("start shell PTY: {error}")))?; + while server + .list()? + .iter() + .any(|session| session.key == key && session.running) + { + thread::sleep(Duration::from_millis(100)); + } + Ok(()) +} + +#[cfg(unix)] +fn parse_options(args: Vec) -> io::Result { + let mut result = CliOptions::default(); + let mut args = args.into_iter(); + while let Some(flag) = args.next() { + let flag = flag.to_string_lossy(); + let value = args.next().ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + format!("missing value for {flag}"), + ) + })?; + match flag.as_ref() { + "--state" => result.state = Some(value.into()), + "--repo" => result.repo = Some(value.into()), + _ => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("unknown option {flag}"), + )) + } + } + } + Ok(result) +} + +#[cfg(unix)] +#[derive(Default)] +struct CliOptions { + state: Option, + repo: Option, +} + +#[cfg(unix)] +fn default_state_path() -> io::Result { + if let Some(path) = env::var_os("ROVER_RUST_HOME") { + return Ok(PathBuf::from(path)); + } + let base = env::var_os("XDG_STATE_HOME") + .map(PathBuf::from) + .or_else(|| env::var_os("HOME").map(|home| PathBuf::from(home).join(".local/state"))) + .ok_or_else(|| { + io::Error::new( + io::ErrorKind::NotFound, + "set ROVER_RUST_HOME, XDG_STATE_HOME, or HOME", + ) + })?; + Ok(base.join("rover-rust")) +} + +#[cfg(unix)] +fn parse_named(args: Vec) -> io::Result> { + let mut values = BTreeMap::new(); + let mut args = args.into_iter(); + while let Some(flag) = args.next() { + let flag = flag.to_string_lossy().into_owned(); + let flag = flag.strip_prefix("--").map(str::to_owned).ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidInput, "invalid internal option") + })?; + if !["sessions", "namespace", "name", "cwd", "shell"].contains(&flag.as_str()) + || values.contains_key(&flag) + { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "unknown or duplicate internal option", + )); + } + values.insert( + flag, + args.next().ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidInput, "missing internal option value") + })?, + ); + } + Ok(values) +} + +#[cfg(unix)] +fn required_path(values: &BTreeMap, key: &str) -> io::Result { + values + .get(key) + .map(PathBuf::from) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, format!("missing {key}"))) +} + +#[cfg(unix)] +fn required_string(values: &BTreeMap, key: &str) -> io::Result { + values + .get(key) + .and_then(|value| value.to_str()) + .map(str::to_owned) + .ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + format!("missing or invalid {key}"), + ) + }) +} + +#[cfg(not(unix))] +fn run_tui(_args: Vec) -> io::Result<()> { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "rover tui session hosting currently requires Unix", + )) +} + +#[cfg(all(test, unix))] +mod tests { + use super::*; + use std::fs; + use std::os::unix::fs::PermissionsExt; + use std::sync::atomic::{AtomicU64, Ordering}; + use std::time::{SystemTime, UNIX_EPOCH}; + + static NEXT_FIXTURE_ID: AtomicU64 = AtomicU64::new(0); + + #[test] + fn agent_cli_lists_profiles_and_fails_closed_for_unknown_capabilities() { + let listed = agent_command_value("agents", &[]).unwrap(); + let profiles = listed.as_array().unwrap(); + assert_eq!(profiles.len(), 4); + assert!(profiles.iter().any(|profile| { + profile["name"] == "claude-print" + && profile["executable"] == "claude" + && profile["permission_mediation"] == false + })); + + let descriptor = agent_command_value( + "agent", + &["capabilities".to_owned(), "codex-exec".to_owned()], + ) + .unwrap(); + assert_eq!(descriptor["name"], "codex-exec"); + assert_eq!(descriptor["structured"], true); + assert!(agent_command_value( + "agent", + &["capabilities".to_owned(), "unknown-profile".to_owned()] + ) + .is_err()); + } + + #[test] + fn agent_list_command_retains_go_schema_envelope() { + let listed = agent_command_value("agent", &["list".to_owned()]).unwrap(); + assert_eq!(listed["schema"], rover_core::SCHEMA); + assert_eq!(listed["adapters"].as_array().unwrap().len(), 4); + } + + #[test] + fn agent_session_inventory_is_scoped_and_live_status_stays_unknown() { + let root = fixture_root(); + let state_path = root.join("state"); + let project_path = root.join("project"); + let other_path = root.join("other"); + fs::create_dir_all(&state_path).unwrap(); + fs::create_dir_all(&project_path).unwrap(); + fs::create_dir_all(&other_path).unwrap(); + let state = StateRoot::open(&state_path).unwrap(); + let project = RepositoryIdentity::resolve(project_path.to_str().unwrap()).unwrap(); + let other = RepositoryIdentity::resolve(other_path.to_str().unwrap()).unwrap(); + for (identity, pane, adapter, session) in [ + (&project, "pane-2", "codex-exec", "thread-two"), + (&project, "pane-1", "claude-print", "session-one"), + (&other, "pane-x", "codex-exec", "thread-other"), + ] { + let binding = rover_agents::AgentSessionBinding::new( + identity, + pane, + adapter, + session, + rover_agents::AgentSessionSource::ExplicitUser, + ) + .unwrap(); + rover_agents::save_session_binding(state.records(), identity, &binding).unwrap(); + } + + let inventory = + agent_session_command_value(state.records(), &project, "list", None, None, None) + .unwrap(); + assert_eq!(inventory["schema"], rover_core::SCHEMA); + assert_eq!(inventory["sessions"].as_array().unwrap().len(), 2); + assert_eq!(inventory["sessions"][0]["pane_id"], "pane-1"); + assert_eq!(inventory["sessions"][0]["binding_status"], "bound"); + assert_eq!(inventory["sessions"][0]["agent_state"], "unknown"); + assert!(inventory["sessions"][0]["state_reason"] + .as_str() + .unwrap() + .contains("no live process")); + + let status = agent_session_command_value( + state.records(), + &project, + "status", + Some("pane-2"), + None, + None, + ) + .unwrap(); + assert_eq!(status["session"]["adapter"], "codex-exec"); + assert_eq!(status["session"]["session_id"], "thread-two"); + assert_eq!(status["session"]["agent_state"], "unknown"); + assert!(agent_session_command_value( + state.records(), + &project, + "status", + Some("pane-x"), + None, + None + ) + .is_err()); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn agent_session_bind_requires_a_saved_project_pane_and_clear_is_explicit() { + let root = fixture_root(); + let state_path = root.join("state"); + let project_path = root.join("project"); + fs::create_dir_all(&state_path).unwrap(); + fs::create_dir_all(&project_path).unwrap(); + let state = StateRoot::open(&state_path).unwrap(); + let project = RepositoryIdentity::resolve(project_path.to_str().unwrap()).unwrap(); + let namespace = format!( + "project_{}", + Sha256Digest::of(project.path().as_os_str().as_encoded_bytes()).to_hex() + ); + let workspace = Workspace::new(&namespace, "project", "shell", &project_path).unwrap(); + let pane = workspace.active_tab().active_pane().to_owned(); + let app = state.root_handle().ensure_subdirectory("rust-tui").unwrap(); + let layouts = app.ensure_subdirectory("layouts").unwrap(); + let layout_name = format!( + "workspace_{}.json", + Sha256Digest::of(namespace.as_bytes()).to_hex() + ); + workspace.save_to(&layouts, &layout_name).unwrap(); + assert_eq!( + resolve_agent_session_pane(&state, &project, "active").unwrap(), + pane + ); + assert!(resolve_agent_session_pane(&state, &project, "pane-foreign").is_err()); + + let bound = agent_session_command_value( + state.records(), + &project, + "bind", + Some(&pane), + Some("codex-exec"), + Some("thread-explicit"), + ) + .unwrap(); + assert_eq!(bound["session"]["source"], "user-selected"); + assert_eq!(bound["session"]["session_id"], "thread-explicit"); + + let cleared = agent_session_command_value( + state.records(), + &project, + "clear", + Some(&pane), + None, + None, + ) + .unwrap(); + assert_eq!(cleared["binding_status"], "cleared"); + let empty = + agent_session_command_value(state.records(), &project, "list", None, None, None) + .unwrap(); + assert!(empty["sessions"].as_array().unwrap().is_empty()); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn external_editor_uses_private_temp_directory_and_rooted_readback() { + let temporary = create_editor_temp_directory().unwrap(); + let metadata = fs::metadata(&temporary.0).unwrap(); + assert_eq!(metadata.mode() & 0o777, 0o700); + let path = temporary.0.clone(); + drop(temporary); + assert!(!path.exists()); + + let source = b"fn main() {}\n"; + let edited = + run_editor_program("src/main.rs", source, OsStr::new("/usr/bin/true")).unwrap(); + assert_eq!(edited, source); + let editor_script_dir = create_editor_temp_directory().unwrap(); + let editor_script = editor_script_dir.0.join("editor"); + fs::write( + &editor_script, + b"#!/bin/sh\nprintf 'fn main() { edited(); }\\n' > \"$1\"\n", + ) + .unwrap(); + fs::set_permissions(&editor_script, fs::Permissions::from_mode(0o700)).unwrap(); + assert_eq!( + run_editor_program("src/main.rs", source, editor_script.as_os_str()).unwrap(), + b"fn main() { edited(); }\n" + ); + fs::write( + &editor_script, + b"#!/bin/sh\nrm -f \"$1\"\nln -s /etc/passwd \"$1\"\n", + ) + .unwrap(); + assert!(run_editor_program("src/main.rs", source, editor_script.as_os_str()).is_err()); + assert_eq!( + run_editor_program("src/main.rs", source, OsStr::new("/usr/bin/false")) + .unwrap_err() + .kind(), + io::ErrorKind::Other + ); + assert!(run_editor_program( + "src/main.rs", + &vec![b'x'; 8 * 1024 * 1024 + 1], + OsStr::new("/usr/bin/true") + ) + .is_err()); + } + + fn insert_task( + database: &Path, + id: &str, + repository: &Path, + objective: &str, + include_objective: bool, + ) { + let contract = if include_objective { + format!( + r#"{{"repository":"{}","objective":"{}"}}"#, + repository.display(), + objective + ) + } else { + format!(r#"{{"repository":"{}"}}"#, repository.display()) + }; + let stdout_hash = "a".repeat(64); + let stderr_hash = "b".repeat(64); + let payload = format!( + r#"{{"id":"{id}","contract":{contract},"status":"RUNNING","updated_at":"2026-09-26T00:00:00Z","candidate":"snapshot_a","attempts":[{{"number":1}},{{"number":2}}],"process":{{"exit_code":3,"error":"process failed","timed_out":false,"cancelled":false,"truncated":true,"stdout_sha256":"{stdout_hash}","stderr_sha256":"{stderr_hash}","stdout_bytes":44,"stderr_bytes":12}}}}"# + ); + insert_raw_record(database, id, &payload); + } + + fn insert_raw_record(database: &Path, id: &str, payload: &str) { + let connection = rusqlite::Connection::open(database).expect("open test state database"); + connection + .execute( + "INSERT INTO records(kind,id,payload,updated) VALUES('task',?1,?2,'2026-09-26T00:00:00Z')", + rusqlite::params![id, payload], + ) + .expect("insert Go-compatible task record"); + } + + fn insert_investigation_record(database: &Path, id: &str, payload: &str) { + let connection = rusqlite::Connection::open(database).expect("open test state database"); + connection + .execute( + "INSERT INTO records(kind,id,payload,updated) VALUES('investigation',?1,?2,'2026-09-26T00:00:00Z')", + rusqlite::params![id, payload], + ) + .expect("insert Go-compatible investigation record"); + } + + fn test_snapshot( + repository: &Path, + files: Vec, + ) -> rover_source::Snapshot { + let repository = repository.display().to_string(); + let files_json = serde_json::to_string(&files).expect("serialize snapshot file fixture"); + let identity = format!(r#"{{"Repository":"{repository}","Files":{files_json}}}"#); + let id = format!( + "snap_{}", + rover_core::Sha256Digest::of(identity.as_bytes()).to_hex() + ); + rover_source::Snapshot { + schema: rover_core::SCHEMA.to_owned(), + id, + repository, + source_ref: "HEAD".to_owned(), + commit: "a".repeat(40), + files, + created_at: "2026-09-26T00:00:00Z".to_owned(), + consistency: "exact Git commit blobs; no checkout filters".to_owned(), + } + } + + fn fixture_root() -> PathBuf { + let unique = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("clock after epoch") + .as_nanos(); + let sequence = NEXT_FIXTURE_ID.fetch_add(1, Ordering::Relaxed); + env::temp_dir().join(format!( + "rover-cli-board-{}-{unique}-{sequence}", + std::process::id() + )) + } + + #[test] + fn project_task_board_filters_go_records_and_fails_closed_on_malformed_rows() { + let root = fixture_root(); + let state_path = root.join("state"); + let project_a = root.join("repo-a"); + let project_b = root.join("repo-b"); + fs::create_dir_all(&state_path).expect("create state directory"); + fs::create_dir_all(&project_a).expect("create project A"); + fs::create_dir_all(&project_b).expect("create project B"); + let state = StateRoot::open(&state_path).expect("admit state root"); + let database = state_path.join("rover.db"); + insert_task(&database, "task_a", &project_a, "Inspect A", true); + insert_task(&database, "task_b", &project_b, "Inspect B", true); + + let tasks = load_project_tasks(&state, &project_a).expect("load project task summaries"); + assert_eq!(tasks.len(), 1); + assert_eq!(tasks[0].id, "task_a"); + assert_eq!(tasks[0].objective, "Inspect A"); + assert_eq!(tasks[0].status, "RUNNING"); + assert_eq!(tasks[0].candidate.as_deref(), Some("snapshot_a")); + assert_eq!(tasks[0].attempt_count, 2); + let process = tasks[0].process.as_ref().expect("process evidence"); + assert_eq!(process.exit_code, 3); + assert_eq!(process.stdout_bytes, 44); + assert_eq!(process.stderr_bytes, 12); + assert!(process.truncated); + assert_eq!(process.error.as_deref(), Some("process failed")); + + insert_task(&database, "task_bad", &project_a, "", false); + assert_eq!( + load_project_tasks(&state, &project_a) + .expect_err("malformed task row must fail closed") + .kind(), + io::ErrorKind::InvalidData + ); + state + .records() + .delete("task", "task_bad", "test.cleanup") + .expect("remove malformed task row"); + insert_raw_record( + &database, + "task_bad_process", + &format!( + r#"{{"id":"task_bad_process","contract":{{"repository":"{}","objective":"bad process"}},"status":"FAILED","updated_at":"2026-09-26T00:00:00Z","process":{{"exit_code":"3"}}}}"#, + project_a.display() + ), + ); + assert_eq!( + load_project_tasks(&state, &project_a) + .expect_err("malformed process result must fail closed") + .kind(), + io::ErrorKind::InvalidData + ); + drop(state); + fs::remove_dir_all(root).expect("remove test state"); + } + + #[test] + fn task_view_preferences_round_trip_and_fail_closed_on_invalid_records() { + let root = fixture_root(); + let state_path = root.join("state"); + let project = root.join("repo"); + fs::create_dir_all(&state_path).expect("create state directory"); + fs::create_dir_all(&project).expect("create project"); + let project = fs::canonicalize(project).expect("canonicalize project"); + let state = StateRoot::open(&state_path).expect("admit state root"); + let expected = TaskViewPreferences { + query: "backend fix".to_owned(), + sort: TaskSortOrder::Objective, + }; + + assert_eq!( + load_project_task_view_preferences(&state, &project).unwrap(), + TaskViewPreferences::default() + ); + save_project_task_view_preferences(&state, &project, &expected).unwrap(); + assert_eq!( + load_project_task_view_preferences(&state, &project).unwrap(), + expected + ); + + let id = project_task_view_preferences_id(&project); + let malformed = serde_json::json!({ + "schema": "rover/tui-task-view/v1", + "repository": project.to_str().unwrap(), + "task_query": "x".repeat(129), + "task_sort": "updated", + }); + state + .records() + .put("tui_preferences", &id, &malformed, "test.malformed") + .unwrap(); + assert_eq!( + load_project_task_view_preferences(&state, &project) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidData + ); + drop(state); + fs::remove_dir_all(root).expect("remove test state"); + } + + #[test] + fn workspace_notes_round_trip_is_project_bound_and_byte_limited() { + let root = fixture_root(); + let state_path = root.join("state"); + let project = root.join("repo"); + fs::create_dir_all(&state_path).expect("create state directory"); + fs::create_dir_all(&project).expect("create project"); + let project = fs::canonicalize(project).expect("canonicalize project"); + let state = StateRoot::open(&state_path).expect("admit state root"); + + assert_eq!( + load_project_workspace_notes(&state, &project).unwrap(), + Vec::::new() + ); + let expected = vec![ + "review prompt\ncheck CI".to_owned(), + "release checklist".to_owned(), + ]; + save_project_workspace_notes(&state, &project, &expected).unwrap(); + assert_eq!( + load_project_workspace_notes(&state, &project).unwrap(), + expected + ); + assert_eq!( + save_project_workspace_notes( + &state, + &project, + &["x".repeat(MAX_WORKSPACE_NOTES_BYTES + 1)] + ) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidInput + ); + assert_eq!( + save_project_workspace_notes( + &state, + &project, + &vec![String::new(); MAX_WORKSPACE_NOTES_COUNT + 1] + ) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidInput + ); + assert_eq!( + save_project_workspace_notes( + &state, + &project, + &vec!["x".repeat(MAX_WORKSPACE_NOTES_BYTES); 5] + ) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidInput + ); + drop(state); + fs::remove_dir_all(root).expect("remove test state"); + } + + #[test] + fn workspace_notes_v1_migration_and_repository_binding() { + let root = fixture_root(); + let state_path = root.join("state"); + let project = root.join("repo"); + fs::create_dir_all(&state_path).expect("create state directory"); + fs::create_dir_all(&project).expect("create project"); + let project = fs::canonicalize(project).expect("canonicalize project"); + let state = StateRoot::open(&state_path).expect("admit state root"); + let id = project_workspace_notes_id(&project); + let legacy = serde_json::json!({ + "schema": "rover/tui-notes/v1", + "repository": project.to_str().unwrap(), + "notes": "legacy prompt", + }); + state + .records() + .put("tui_notes", &id, &legacy, "test.legacy") + .unwrap(); + assert_eq!( + load_project_workspace_notes(&state, &project).unwrap(), + ["legacy prompt"] + ); + save_project_workspace_notes( + &state, + &project, + &load_project_workspace_notes(&state, &project).unwrap(), + ) + .unwrap(); + assert_eq!( + state + .records() + .get_typed::("tui_notes", &id) + .unwrap() + .get("schema") + .and_then(serde_json::Value::as_str), + Some("rover/tui-notes/v2") + ); + let invalid = serde_json::json!({ + "schema": "rover/tui-notes/v2", + "repository": root.join("other-repo").to_string_lossy(), + "notes": ["must not cross project boundary"], + }); + state + .records() + .put("tui_notes", &id, &invalid, "test.invalid") + .unwrap(); + assert_eq!( + load_project_workspace_notes(&state, &project) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidData + ); + drop(state); + fs::remove_dir_all(root).expect("remove test state"); + } + + #[test] + fn saved_commands_round_trip_and_reject_invalid_lines_and_count() { + let root = fixture_root(); + let state_path = root.join("state"); + let project = root.join("repo"); + fs::create_dir_all(&state_path).expect("create state directory"); + fs::create_dir_all(&project).expect("create project"); + let project = fs::canonicalize(project).expect("canonicalize project"); + let state = StateRoot::open(&state_path).expect("admit state root"); + let expected = vec![ + SavedCommand { + name: "Check Rust".to_owned(), + command: "cargo test --workspace".to_owned(), + }, + SavedCommand { + name: "List files".to_owned(), + command: "git status --short".to_owned(), + }, + ]; + + assert!(load_project_saved_commands(&state, &project) + .unwrap() + .is_empty()); + save_project_saved_commands(&state, &project, &expected).unwrap(); + assert_eq!( + load_project_saved_commands(&state, &project).unwrap(), + expected + ); + assert_eq!( + save_project_saved_commands( + &state, + &project, + &[SavedCommand { + name: "multiline".to_owned(), + command: "echo safe\nrm -rf /".to_owned(), + }] + ) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidInput + ); + assert_eq!( + save_project_saved_commands( + &state, + &project, + &vec![ + SavedCommand { + name: "command".to_owned(), + command: "true".to_owned(), + }; + MAX_SAVED_COMMANDS + 1 + ] + ) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidInput + ); + drop(state); + fs::remove_dir_all(root).expect("remove test state"); + } + + #[test] + fn saved_commands_fail_closed_on_cross_project_or_duplicate_records() { + let root = fixture_root(); + let state_path = root.join("state"); + let project = root.join("repo"); + fs::create_dir_all(&state_path).expect("create state directory"); + fs::create_dir_all(&project).expect("create project"); + let project = fs::canonicalize(project).expect("canonicalize project"); + let state = StateRoot::open(&state_path).expect("admit state root"); + let id = project_saved_commands_id(&project); + let invalid = serde_json::json!({ + "schema": "rover/tui-saved-commands/v1", + "repository": root.join("other-repo").to_string_lossy(), + "commands": [ + {"name": "same", "command": "echo 1"}, + {"name": "same", "command": "echo 2"} + ], + }); + state + .records() + .put("tui_saved_commands", &id, &invalid, "test.invalid") + .unwrap(); + assert_eq!( + load_project_saved_commands(&state, &project) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidData + ); + let duplicate = serde_json::json!({ + "schema": "rover/tui-saved-commands/v1", + "repository": project.to_str().unwrap(), + "commands": [ + {"name": "same", "command": "echo 1"}, + {"name": "same", "command": "echo 2"} + ], + }); + state + .records() + .put("tui_saved_commands", &id, &duplicate, "test.duplicate") + .unwrap(); + assert_eq!( + load_project_saved_commands(&state, &project) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidData + ); + drop(state); + fs::remove_dir_all(root).expect("remove test state"); + } + + #[test] + fn task_briefing_drafts_round_trip_with_limits() { + let root = fixture_root(); + let state_path = root.join("state"); + let project = root.join("repo"); + fs::create_dir_all(&state_path).expect("create state directory"); + fs::create_dir_all(&project).expect("create project"); + let project = fs::canonicalize(project).expect("canonicalize project"); + let state = StateRoot::open(&state_path).expect("admit state root"); + let expected = vec![ + TaskDraft { + title: "Audit plan".to_owned(), + paths: "src/**".to_owned(), + dependencies: "task_base".to_owned(), + quality_gate: "cargo test".to_owned(), + prompt: "Compare paths\nRecord evidence".to_owned(), + }, + TaskDraft { + title: String::new(), + paths: String::new(), + dependencies: String::new(), + quality_gate: String::new(), + prompt: String::new(), + }, + ]; + assert!(load_project_task_drafts(&state, &project) + .unwrap() + .is_empty()); + save_project_task_drafts(&state, &project, &expected).unwrap(); + assert_eq!( + load_project_task_drafts(&state, &project).unwrap(), + expected + ); + let legacy = serde_json::json!({ + "schema": "rover/tui-task-drafts/v1", + "repository": project.to_str().unwrap(), + "drafts": [{"title": "legacy", "prompt": "old briefing"}], + }); + let drafts_id = project_task_drafts_id(&project); + state + .records() + .put("tui_task_drafts", &drafts_id, &legacy, "test.legacy") + .unwrap(); + let migrated = load_project_task_drafts(&state, &project).unwrap(); + assert_eq!(migrated[0].title, "legacy"); + assert!(migrated[0].paths.is_empty()); + assert!(migrated[0].dependencies.is_empty()); + assert!(migrated[0].quality_gate.is_empty()); + save_project_task_drafts(&state, &project, &migrated).unwrap(); + assert_eq!( + state + .records() + .get_typed::("tui_task_drafts", &drafts_id) + .unwrap() + .get("schema") + .and_then(serde_json::Value::as_str), + Some("rover/tui-task-drafts/v2") + ); + assert_eq!( + save_project_task_drafts( + &state, + &project, + &[TaskDraft { + title: "x".repeat(MAX_TASK_DRAFT_TITLE_BYTES + 1), + paths: String::new(), + dependencies: String::new(), + quality_gate: String::new(), + prompt: String::new() + }] + ) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidInput + ); + assert_eq!( + save_project_task_drafts( + &state, + &project, + &[TaskDraft { + title: String::new(), + paths: String::new(), + dependencies: String::new(), + quality_gate: String::new(), + prompt: "x".repeat(MAX_TASK_DRAFT_PROMPT_BYTES + 1) + }] + ) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidInput + ); + assert_eq!( + save_project_task_drafts( + &state, + &project, + &vec![ + TaskDraft { + title: String::new(), + paths: String::new(), + dependencies: String::new(), + quality_gate: String::new(), + prompt: String::new() + }; + MAX_TASK_DRAFTS + 1 + ] + ) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidInput + ); + let mut over_paths = TaskDraft { + title: String::new(), + paths: String::new(), + dependencies: String::new(), + quality_gate: String::new(), + prompt: String::new(), + }; + over_paths.paths = "x".repeat(MAX_TASK_DRAFT_PATHS_BYTES + 1); + let mut over_dependencies = over_paths.clone(); + over_dependencies.paths.clear(); + over_dependencies.dependencies = "x".repeat(MAX_TASK_DRAFT_DEPENDENCIES_BYTES + 1); + let mut over_gate = over_paths.clone(); + over_gate.paths.clear(); + over_gate.quality_gate = "x".repeat(MAX_TASK_DRAFT_GATE_BYTES + 1); + for oversized in [over_paths, over_dependencies, over_gate] { + assert_eq!( + save_project_task_drafts(&state, &project, &[oversized]) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidInput + ); + } + drop(state); + fs::remove_dir_all(root).expect("remove test state"); + } + + #[test] + fn task_plan_lists_are_project_scoped_and_show_transitive_readiness() { + let root = fixture_root(); + let state_path = root.join("state"); + let project = root.join("repo"); + let other_project = root.join("other-repo"); + fs::create_dir_all(&state_path).expect("create state directory"); + fs::create_dir_all(&project).expect("create project"); + fs::create_dir_all(&other_project).expect("create other project"); + let project = fs::canonicalize(project).expect("canonicalize project"); + let other_project = fs::canonicalize(other_project).expect("canonicalize other project"); + let state = StateRoot::open(&state_path).expect("admit state root"); + let identity = rover_core::RepositoryIdentity::resolve(project.to_str().unwrap()).unwrap(); + let service = rover_tasks::TaskPlanService::new(state.records()); + let parent = service + .create( + &identity, + rover_core::TaskBrief { + title: "Parent".into(), + paths: String::new(), + dependencies: String::new(), + quality_gate: String::new(), + prompt: "Complete parent".into(), + }, + ) + .unwrap(); + let child = service + .create( + &identity, + rover_core::TaskBrief { + title: "Child".into(), + paths: String::new(), + dependencies: parent.id.to_string(), + quality_gate: String::new(), + prompt: "Complete child".into(), + }, + ) + .unwrap(); + service.start_manual(&identity, &parent.id).unwrap(); + service + .finish_manual( + &identity, + &parent.id, + rover_tasks::TaskPlanStatus::Cancelled, + "Cancelled by user", + ) + .unwrap(); + + let summaries = load_project_task_plan_summaries(&state, &project).unwrap(); + assert_eq!(summaries.len(), 2); + let child_summary = summaries + .iter() + .find(|summary| summary.id == child.id.as_str()) + .unwrap(); + assert_eq!(child_summary.status, "WAITING"); + assert_eq!(child_summary.readiness, "BLOCKED"); + assert!(load_project_task_plan_summaries(&state, &other_project) + .unwrap() + .is_empty()); + drop(state); + fs::remove_dir_all(root).expect("remove test state"); + } + + #[test] + fn task_briefing_drafts_reject_cross_project_and_terminal_controls() { + let root = fixture_root(); + let state_path = root.join("state"); + let project = root.join("repo"); + fs::create_dir_all(&state_path).expect("create state directory"); + fs::create_dir_all(&project).expect("create project"); + let project = fs::canonicalize(project).expect("canonicalize project"); + let state = StateRoot::open(&state_path).expect("admit state root"); + let id = project_task_drafts_id(&project); + let invalid = serde_json::json!({ + "schema": "rover/tui-task-drafts/v1", + "repository": root.join("other-repo").to_string_lossy(), + "drafts": [{"title": "bad", "prompt": "wrong project"}], + }); + state + .records() + .put("tui_task_drafts", &id, &invalid, "test.invalid") + .unwrap(); + assert_eq!( + load_project_task_drafts(&state, &project) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidData + ); + assert_eq!( + save_project_task_drafts( + &state, + &project, + &[TaskDraft { + title: "bad\u{202e}".to_owned(), + paths: String::new(), + dependencies: String::new(), + quality_gate: String::new(), + prompt: String::new() + }] + ) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidInput + ); + drop(state); + fs::remove_dir_all(root).expect("remove test state"); + } + + #[test] + fn task_evidence_load_is_project_and_candidate_bound() { + let root = fixture_root(); + let state_path = root.join("state"); + let project = root.join("repo"); + let other_project = root.join("other-repo"); + fs::create_dir_all(&state_path).expect("create state directory"); + fs::create_dir_all(&project).expect("create project"); + fs::create_dir_all(&other_project).expect("create other project"); + let state = StateRoot::open(&state_path).expect("admit state root"); + let database = state_path.join("rover.db"); + let task_id = "task_evidence"; + let investigation_id = "inv_evidence"; + insert_raw_record( + &database, + task_id, + &format!( + r#"{{"id":"{task_id}","contract":{{"repository":"{}","objective":"evidence fixture"}},"status":"CANDIDATE_READY","updated_at":"2026-09-26T00:00:00Z","candidate":"snapshot_a","investigation_id":"{investigation_id}"}}"#, + project.display() + ), + ); + let evidence = |repository: &Path, candidate: &str| { + format!( + r#"{{"schema":"rover/v1alpha1","id":"{investigation_id}","repository":"{}","candidate":"{candidate}","decision":"BLOCKED","decision_reason":"required review is pending","checks":[{{"id":"unit","outcome":"PASS","meaning":"3 recorded checks passed","required":true,"tests":3,"skipped":0}}],"findings":[{{"path":"src/lib.rs","message":"review this finding","severity":"warning"}}],"unknowns":["External effects are not rolled back."]}}"#, + repository.display() + ) + }; + insert_investigation_record( + &database, + investigation_id, + &evidence(&project, "snapshot_a"), + ); + + let tasks = load_project_tasks(&state, &project).expect("load project task"); + assert_eq!(tasks.len(), 1); + let summary = load_project_investigation(&state, &project, &tasks[0]) + .expect("load linked investigation") + .expect("investigation is present"); + assert_eq!(summary.id, investigation_id); + assert_eq!(summary.candidate, "snapshot_a"); + assert_eq!(summary.decision, "BLOCKED"); + assert_eq!(summary.checks.len(), 1); + assert_eq!(summary.checks[0].outcome, "PASS"); + assert_eq!(summary.findings[0].path, "src/lib.rs"); + assert_eq!(summary.unknowns.len(), 1); + + state + .records() + .delete("investigation", investigation_id, "test.replace") + .expect("remove accepted investigation"); + insert_investigation_record( + &database, + investigation_id, + &evidence(&other_project, "snapshot_a"), + ); + assert_eq!( + load_project_investigation(&state, &project, &tasks[0]) + .expect_err("cross-project evidence is rejected") + .kind(), + io::ErrorKind::InvalidData + ); + + state + .records() + .delete("investigation", investigation_id, "test.replace") + .expect("remove cross-project investigation"); + insert_investigation_record( + &database, + investigation_id, + &evidence(&project, "snapshot_other"), + ); + assert_eq!( + load_project_investigation(&state, &project, &tasks[0]) + .expect_err("stale candidate evidence is rejected") + .kind(), + io::ErrorKind::InvalidData + ); + drop(state); + fs::remove_dir_all(root).expect("remove test state"); + } + + #[test] + fn task_diff_load_verifies_retained_snapshots_and_project_identity() { + let root = fixture_root(); + let state_path = root.join("state"); + let project = root.join("repo"); + let other_project = root.join("other-repo"); + fs::create_dir_all(&state_path).expect("create state directory"); + fs::create_dir_all(&project).expect("create project"); + fs::create_dir_all(&other_project).expect("create other project"); + let state = StateRoot::open(&state_path).expect("admit state root"); + let base = test_snapshot(&project, Vec::new()); + let candidate_digest = state + .put_blob(b"verified candidate bytes") + .expect("store candidate content"); + let candidate = test_snapshot( + &project, + vec![rover_source::SnapshotFile { + path: "src/main.rs".to_owned(), + mode: 0o644, + sha256: candidate_digest.to_hex(), + size: 24, + }], + ); + state + .records() + .put("snapshot", &base.id, &base, "test.snapshot") + .expect("store base snapshot"); + state + .records() + .put("snapshot", &candidate.id, &candidate, "test.snapshot") + .expect("store candidate snapshot"); + let task = TaskSummary { + id: "task_diff".to_owned(), + objective: "compare retained snapshots".to_owned(), + status: "CANDIDATE_READY".to_owned(), + updated_at: "2026-09-26T00:00:00Z".to_owned(), + error: None, + candidate: Some(candidate.id.clone()), + base_snapshot: Some(base.id.clone()), + investigation_id: None, + attempt_count: 0, + process: None, + }; + let diff = load_project_diff(&state, &project, &task) + .expect("load verified snapshot inventory") + .expect("linked snapshots should have a comparison"); + assert_eq!(diff.base_snapshot, base.id); + assert_eq!(diff.candidate, candidate.id); + assert_eq!(diff.changes.len(), 1); + assert_eq!(diff.changes[0].path, "src/main.rs"); + assert_eq!(diff.changes[0].status, "added"); + + state + .records() + .delete("snapshot", &candidate.id, "test.replace") + .expect("remove valid candidate snapshot"); + let cross_project_candidate = test_snapshot(&other_project, Vec::new()); + state + .records() + .put( + "snapshot", + &cross_project_candidate.id, + &cross_project_candidate, + "test.snapshot", + ) + .expect("store cross-project snapshot"); + let mut cross_project_task = task; + cross_project_task.candidate = Some(cross_project_candidate.id); + assert_eq!( + load_project_diff(&state, &project, &cross_project_task) + .expect_err("cross-project snapshot must be rejected") + .kind(), + io::ErrorKind::InvalidData + ); + drop(state); + fs::remove_dir_all(root).expect("remove test state"); + } +} diff --git a/crates/rover-cli/tests/session_daemon.rs b/crates/rover-cli/tests/session_daemon.rs new file mode 100644 index 0000000..4dbee5d --- /dev/null +++ b/crates/rover-cli/tests/session_daemon.rs @@ -0,0 +1,96 @@ +#![cfg(unix)] + +use std::fs; +use std::os::unix::fs::PermissionsExt; +use std::path::PathBuf; +use std::process::{Command, Stdio}; +use std::thread; +use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; + +use rover_execution::sessions::{SessionClient, SessionKey}; + +struct Scratch(PathBuf); + +impl Drop for Scratch { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.0); + } +} + +#[test] +fn cli_session_daemon_accepts_named_client_and_exits_with_shell() { + let unique = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("clock after epoch") + .as_nanos(); + let root = PathBuf::from("/tmp").join(format!("rvcli-{unique}")); + fs::create_dir(&root).expect("create scratch root"); + fs::set_permissions(&root, fs::Permissions::from_mode(0o700)).expect("private scratch root"); + let _scratch = Scratch(root.clone()); + let sessions = root.join("sessions"); + let cwd = root.join("work"); + let shell = root.join("controlled-shell"); + fs::create_dir(&cwd).expect("create work directory"); + fs::write( + &shell, + b"#!/bin/sh\nwhile IFS= read -r line; do\n [ \"$line\" = exit ] && exit 0\ndone\n", + ) + .expect("write controlled shell fixture"); + fs::set_permissions(&shell, fs::Permissions::from_mode(0o700)) + .expect("make controlled shell executable"); + let key = SessionKey::new("integration_project", "shell").expect("valid session key"); + + let mut daemon = Command::new(env!("CARGO_BIN_EXE_rover")) + .args([ + "__session_daemon", + "--sessions", + sessions.to_str().expect("UTF-8 scratch path"), + "--namespace", + key.namespace(), + "--name", + key.name(), + "--cwd", + cwd.to_str().expect("UTF-8 working path"), + "--shell", + shell.to_str().expect("UTF-8 shell path"), + ]) + .env_clear() + .env("PATH", "/usr/bin:/bin") + .env("HOME", &root) + .env("SHELL", &shell) + .env("TERM", "xterm-256color") + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::inherit()) + .spawn() + .expect("start detached session owner"); + + let deadline = Instant::now() + Duration::from_secs(3); + let mut client = loop { + match SessionClient::connect_named(&sessions, &key) { + Ok(client) => break client, + Err(_) if Instant::now() < deadline => { + if let Some(status) = daemon.try_wait().expect("check owner process") { + panic!("session owner exited before accepting a client: {status}"); + } + thread::sleep(Duration::from_millis(20)); + } + Err(error) => panic!("session owner did not accept a client: {error}"), + } + }; + client + .send_input(b"exit\n") + .expect("send controlled shell exit"); + let deadline = Instant::now() + Duration::from_secs(5); + loop { + if let Some(status) = daemon.try_wait().expect("wait for session owner") { + assert!(status.success(), "owner exits successfully after its shell"); + break; + } + if Instant::now() >= deadline { + let _ = daemon.kill(); + panic!("controlled shell did not end the session owner"); + } + thread::sleep(Duration::from_millis(20)); + } +} diff --git a/crates/rover-core/Cargo.toml b/crates/rover-core/Cargo.toml new file mode 100644 index 0000000..1dbba06 --- /dev/null +++ b/crates/rover-core/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "rover-core" +version = "0.1.0" +edition.workspace = true +license.workspace = true +rust-version.workspace = true +description = "Domain primitives for the Rover Rust implementation" + +[dependencies] +getrandom = "=0.4.3" +serde = { version = "=1.0.229", default-features = false, features = ["std", "derive"] } +sha2 = "=0.11.0" +time = { version = "=0.3.55", default-features = false, features = ["formatting"] } + +[lints] +workspace = true diff --git a/crates/rover-core/src/lib.rs b/crates/rover-core/src/lib.rs new file mode 100644 index 0000000..8599088 --- /dev/null +++ b/crates/rover-core/src/lib.rs @@ -0,0 +1,516 @@ +//! Domain primitives for Rover's Rust implementation. +//! +//! This crate currently ports identifiers, repository/project identity, +//! raw-byte SHA-256 digests, and UTC timestamps. It does not provide process +//! execution, CLI, or TUI behavior. + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use sha2::{Digest as Sha2Digest, Sha256}; +use std::fmt; +use std::str::FromStr; +use time::{format_description::well_known::Rfc3339, OffsetDateTime}; + +mod repository; +mod task; + +pub use repository::{control_audience, RepositoryIdentity}; +pub use task::{validate_task_graph, TaskBrief, TaskBriefError, TaskGraphError, TaskGraphNode}; + +/// Current serialized Rover schema marker. +pub const SCHEMA: &str = "rover/v1alpha1"; +const LOWER_HEX: &[u8; 16] = b"0123456789abcdef"; + +/// A validated Rover identifier. +/// +/// The accepted format matches the current Go model: 1–128 ASCII bytes, an +/// initial ASCII letter or digit, followed by ASCII letters, digits, `_`, `.` +/// or `-`. +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Id(String); + +impl Id { + /// Validate and construct an identifier. + /// + /// # Errors + /// + /// Returns [`InvalidId`] when the input is empty, longer than 128 bytes, + /// starts with a non-alphanumeric ASCII byte, or contains a disallowed + /// character. + pub fn parse(value: impl Into) -> Result { + let value = value.into(); + let bytes = value.as_bytes(); + let valid_start = bytes.first().is_some_and(u8::is_ascii_alphanumeric); + let valid_tail = bytes + .iter() + .skip(1) + .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'.' | b'-')); + + if !valid_start || bytes.len() > 128 || !valid_tail { + return Err(InvalidId); + } + Ok(Self(value)) + } + + /// Generate a validated identifier with 128 bits of operating-system + /// random entropy, rendered as 32 lowercase hexadecimal characters. + /// + /// Prefixes must themselves be valid Rover ID fragments, start with an + /// ASCII letter or digit, and leave room for the underscore and entropy. + /// + /// # Errors + /// + /// Returns [`IdGenerationError::InvalidPrefix`] for a prefix that cannot + /// form a valid ID, or [`IdGenerationError::Random`] if the operating + /// system's cryptographic random source fails. + pub fn generate(prefix: &str) -> Result { + let prefix_id = Self::parse(prefix).map_err(IdGenerationError::InvalidPrefix)?; + if prefix_id.as_str().len() > 95 { + return Err(IdGenerationError::InvalidPrefix(InvalidId)); + } + + let value = try_id(prefix_id.as_str()).map_err(IdGenerationError::Random)?; + Self::parse(value).map_err(IdGenerationError::InvalidPrefix) + } + + /// Return the validated identifier as a string slice. + #[must_use] + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl fmt::Display for Id { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str(self.as_str()) + } +} + +impl FromStr for Id { + type Err = InvalidId; + + fn from_str(value: &str) -> Result { + Self::parse(value) + } +} + +impl Serialize for Id { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str(self.as_str()) + } +} + +impl<'de> Deserialize<'de> for Id { + fn deserialize>(deserializer: D) -> Result { + let value = String::deserialize(deserializer)?; + Self::parse(value).map_err(serde::de::Error::custom) + } +} + +/// Error returned when an identifier does not match Rover's compatibility rule. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct InvalidId; + +impl fmt::Display for InvalidId { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str( + "identifier must be 1-128 ASCII characters, start with a letter or digit, and contain only letters, digits, '_', '.' or '-'", + ) + } +} + +impl std::error::Error for InvalidId {} + +/// Failure while generating an identifier. +#[derive(Debug)] +pub enum IdGenerationError { + /// The prefix cannot produce a valid identifier within Rover's length cap. + InvalidPrefix(InvalidId), + /// The operating system's random source failed. + Random(getrandom::Error), +} + +impl fmt::Display for IdGenerationError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidPrefix(error) => write!(formatter, "invalid ID prefix: {error}"), + Self::Random(error) => { + write!(formatter, "operating-system random source failed: {error}") + } + } + } +} + +impl std::error::Error for IdGenerationError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::InvalidPrefix(error) => Some(error), + Self::Random(error) => Some(error), + } + } +} + +/// Generate an ID string using the legacy prefix-and-random-bytes format. +/// +/// This compatibility function preserves the Go model's behavior for any +/// prefix, including prefixes that do not themselves form a valid [`Id`]. +/// Use [`Id::generate`] when the result must be validated as a typed ID. +/// +/// # Errors +/// +/// Returns the operating-system random-source error without a predictable +/// fallback. +pub fn try_id(prefix: &str) -> Result { + let mut entropy = [0u8; 16]; + getrandom::fill(&mut entropy)?; + + let mut value = String::with_capacity(prefix.len() + 33); + value.push_str(prefix); + value.push('_'); + for byte in entropy { + value.push(char::from(LOWER_HEX[usize::from(byte >> 4)])); + value.push(char::from(LOWER_HEX[usize::from(byte & 0x0f)])); + } + Ok(value) +} + +/// A SHA-256 digest with Rover's lowercase 64-character wire representation. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Sha256Digest([u8; 32]); + +impl Sha256Digest { + /// Hash raw bytes with SHA-256. + #[must_use] + pub fn of(bytes: &[u8]) -> Self { + let hash = Sha256::digest(bytes); + let mut output = [0; 32]; + output.copy_from_slice(&hash); + Self(output) + } + + /// Parse Rover's canonical lowercase hexadecimal digest representation. + /// + /// # Errors + /// + /// Returns [`InvalidDigest`] unless the value has exactly 64 lowercase + /// hexadecimal characters. + pub fn parse_hex(value: &str) -> Result { + let bytes = value.as_bytes(); + if bytes.len() != 64 + || !bytes + .iter() + .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(byte)) + { + return Err(InvalidDigest); + } + + let mut digest = [0; 32]; + for (index, pair) in bytes.chunks_exact(2).enumerate() { + digest[index] = (decode_hex(pair[0]) << 4) | decode_hex(pair[1]); + } + Ok(Self(digest)) + } + + /// Return the digest bytes. + #[must_use] + pub const fn as_bytes(&self) -> &[u8; 32] { + &self.0 + } + + /// Return the canonical lowercase hexadecimal representation. + #[must_use] + pub fn to_hex(self) -> String { + let mut output = String::with_capacity(64); + for byte in self.0 { + output.push(char::from(LOWER_HEX[usize::from(byte >> 4)])); + output.push(char::from(LOWER_HEX[usize::from(byte & 0x0f)])); + } + output + } +} + +const fn decode_hex(byte: u8) -> u8 { + match byte { + b'0'..=b'9' => byte - b'0', + b'a'..=b'f' => byte - b'a' + 10, + _ => 0, + } +} + +impl fmt::Display for Sha256Digest { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str(&self.to_hex()) + } +} + +impl FromStr for Sha256Digest { + type Err = InvalidDigest; + + fn from_str(value: &str) -> Result { + Self::parse_hex(value) + } +} + +/// Error returned when a SHA-256 digest is not in canonical Rover form. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct InvalidDigest; + +impl fmt::Display for InvalidDigest { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("SHA-256 digest must be 64 lowercase hexadecimal characters") + } +} + +impl std::error::Error for InvalidDigest {} + +/// Return the lowercase hexadecimal SHA-256 digest of raw bytes. +#[must_use] +pub fn digest(bytes: &[u8]) -> String { + Sha256Digest::of(bytes).to_string() +} + +/// A typed UTC instant formatted as `RFC3339Nano` when displayed. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Timestamp(OffsetDateTime); + +impl Timestamp { + /// Create a timestamp from an instant, canonicalized to UTC. + /// + /// # Errors + /// + /// Returns [`InvalidTimestamp`] if converting the instant to UTC exceeds + /// the supported date range. + pub fn from_instant(instant: OffsetDateTime) -> Result { + instant + .checked_to_offset(time::UtcOffset::UTC) + .map(Self) + .ok_or(InvalidTimestamp) + } + + /// Capture the current system time in UTC. + #[must_use] + pub fn now() -> Self { + Self(OffsetDateTime::now_utc()) + } + + /// Return the timestamp as RFC3339Nano-compatible UTC text. + /// + /// # Errors + /// + /// Returns a formatting error if the instant cannot be represented in + /// RFC3339. + pub fn to_rfc3339(self) -> Result { + self.0.format(&Rfc3339) + } + + /// Return the underlying canonical UTC instant. + #[must_use] + pub const fn as_instant(&self) -> OffsetDateTime { + self.0 + } +} + +/// Error returned when an instant cannot be represented as a UTC timestamp. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct InvalidTimestamp; + +impl fmt::Display for InvalidTimestamp { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("timestamp cannot be represented in UTC") + } +} + +impl std::error::Error for InvalidTimestamp {} + +impl fmt::Display for Timestamp { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.to_rfc3339() { + Ok(value) => formatter.write_str(&value), + Err(_) => Err(fmt::Error), + } + } +} + +/// Return the current time as a typed UTC timestamp. +#[must_use] +pub fn now() -> Timestamp { + Timestamp::now() +} + +#[cfg(test)] +mod tests { + use super::{ + digest, now, try_id, Id, IdGenerationError, InvalidDigest, InvalidId, Sha256Digest, + Timestamp, SCHEMA, + }; + use std::str::FromStr; + use time::OffsetDateTime; + + #[test] + fn accepts_compatibility_examples_and_preserves_spelling() { + for input in ["a", "0", "Issue_123", "task.name-2", &"x".repeat(128)] { + let parsed = Id::parse(input).expect("valid compatibility ID"); + assert_eq!(parsed.as_str(), input); + assert_eq!(parsed.to_string(), input); + } + } + + #[test] + fn rejects_empty_non_ascii_and_invalid_characters() { + for input in [ + "", + "_leading", + ".leading", + "-leading", + "has space", + "é", + "a/b", + "a\\b", + "a\nb", + ] { + assert_eq!(Id::parse(input), Err(InvalidId), "input: {input:?}"); + } + } + + #[test] + fn enforces_the_128_byte_limit() { + assert!(Id::parse("x".repeat(128)).is_ok()); + assert_eq!(Id::parse("x".repeat(129)), Err(InvalidId)); + } + + #[test] + fn from_str_uses_the_same_validation() { + assert_eq!(Id::from_str("agent-1").unwrap().as_str(), "agent-1"); + assert_eq!(Id::from_str("agent 1"), Err(InvalidId)); + } + + #[test] + fn exposes_the_current_schema_marker() { + assert_eq!(SCHEMA, "rover/v1alpha1"); + } + + #[test] + fn generates_valid_ids_with_lowercase_128_bit_entropy() { + let id = Id::generate("task").expect("operating system random source"); + let (prefix, entropy) = id.as_str().split_once('_').expect("separator"); + assert_eq!(prefix, "task"); + assert_eq!(entropy.len(), 32); + assert!(entropy + .bytes() + .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))); + assert!(Id::parse(id.as_str()).is_ok()); + } + + #[test] + fn legacy_generator_preserves_arbitrary_prefixes() { + let generated = try_id("legacy prefix").expect("operating system random source"); + let (prefix, entropy) = generated.rsplit_once('_').expect("separator"); + assert_eq!(prefix, "legacy prefix"); + assert_eq!(entropy.len(), 32); + assert!(entropy + .bytes() + .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))); + } + + #[test] + fn generation_rejects_prefixes_without_valid_id_capacity() { + for prefix in ["", "_bad", "has space", &"x".repeat(96)] { + assert!(matches!( + Id::generate(prefix), + Err(IdGenerationError::InvalidPrefix(_)) + )); + } + } + + #[test] + fn sha256_matches_known_answer_vectors() { + assert_eq!( + digest(b""), + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ); + assert_eq!( + digest(b"abc"), + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" + ); + let typed = Sha256Digest::of(b"abc"); + assert_eq!(typed.to_hex(), digest(b"abc")); + assert_eq!(Sha256Digest::parse_hex(&typed.to_string()), Ok(typed)); + assert_eq!(typed.as_bytes().len(), 32); + } + + #[test] + fn digest_validation_rejects_noncanonical_values() { + let uppercase = "A".repeat(64); + let too_short = "a".repeat(63); + let non_hex = "g".repeat(64); + for invalid in ["", uppercase.as_str(), too_short.as_str(), non_hex.as_str()] { + assert_eq!(Sha256Digest::parse_hex(invalid), Err(InvalidDigest)); + } + } + + #[test] + fn utc_format_trims_fractional_zeroes_like_rfc3339nano() { + let instant = OffsetDateTime::from_unix_timestamp(0).unwrap() + + time::Duration::nanoseconds(123_400_000); + let typed = Timestamp::from_instant(instant).unwrap(); + assert_eq!(typed.to_rfc3339().unwrap(), "1970-01-01T00:00:00.1234Z"); + assert_eq!(typed.to_string(), "1970-01-01T00:00:00.1234Z"); + assert_eq!( + Timestamp::from_instant(OffsetDateTime::from_unix_timestamp(0).unwrap()) + .unwrap() + .to_rfc3339() + .unwrap(), + "1970-01-01T00:00:00Z" + ); + + let maximum_fraction = OffsetDateTime::from_unix_timestamp(0).unwrap() + + time::Duration::nanoseconds(999_999_999); + assert_eq!( + Timestamp::from_instant(maximum_fraction) + .unwrap() + .to_rfc3339() + .unwrap(), + "1970-01-01T00:00:00.999999999Z" + ); + } + + #[test] + fn timestamp_canonicalizes_offsets_to_utc() { + let instant = OffsetDateTime::from_unix_timestamp(0) + .unwrap() + .to_offset(time::UtcOffset::from_hms(-5, 0, 0).unwrap()); + let timestamp = Timestamp::from_instant(instant).unwrap(); + assert_eq!(timestamp.as_instant().offset(), time::UtcOffset::UTC); + assert_eq!(timestamp.to_rfc3339().unwrap(), "1970-01-01T00:00:00Z"); + } + + #[test] + fn current_timestamp_has_utc_rfc3339_shape() { + let value = now() + .to_rfc3339() + .expect("system timestamp in RFC3339 range"); + let timestamp = value.strip_suffix('Z').expect("UTC suffix"); + let (date, clock) = timestamp.split_once('T').expect("date/time separator"); + assert_eq!(date.len(), 10); + assert_eq!(&date[4..5], "-"); + assert_eq!(&date[7..8], "-"); + assert!(date + .bytes() + .enumerate() + .all(|(index, byte)| matches!(index, 4 | 7) || byte.is_ascii_digit())); + let (clock, fraction) = clock + .split_once('.') + .map_or((clock, None), |(clock, fraction)| (clock, Some(fraction))); + assert_eq!(clock.len(), 8); + assert_eq!(&clock[2..3], ":"); + assert_eq!(&clock[5..6], ":"); + assert!(clock + .bytes() + .enumerate() + .all(|(index, byte)| matches!(index, 2 | 5) || byte.is_ascii_digit())); + if let Some(fraction) = fraction { + assert!((1..=9).contains(&fraction.len())); + assert!(fraction.bytes().all(|byte| byte.is_ascii_digit())); + assert!(!fraction.ends_with('0')); + } + } +} diff --git a/crates/rover-core/src/repository.rs b/crates/rover-core/src/repository.rs new file mode 100644 index 0000000..7200eb4 --- /dev/null +++ b/crates/rover-core/src/repository.rs @@ -0,0 +1,234 @@ +//! Canonical local repository identity and state/project audience binding. + +use std::fs; +use std::io; +use std::path::{Component, Path, PathBuf}; + +use crate::Sha256Digest; + +/// A repository path and the stable project digest derived from it. +/// +/// Resolution matches Rover's Go access layer: an absolute cleaned path is +/// used when full symlink resolution fails, so not-yet-created repositories +/// still have an identity. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct RepositoryIdentity { + canonical_path: PathBuf, + project_id: Sha256Digest, +} + +impl RepositoryIdentity { + /// Resolve a repository path and compute its project ID. + /// + /// Empty input retains an empty canonical path. Nonempty input is made + /// absolute and lexically clean; if the complete path exists and resolves, + /// the filesystem's canonical path is used. + /// + /// # Errors + /// + /// Returns an I/O error if the current directory cannot be obtained for a + /// relative input. + pub fn resolve(repository: &str) -> io::Result { + let canonical_path = if repository.is_empty() { + PathBuf::new() + } else { + let repository = Path::new(repository); + let absolute = if repository.is_absolute() { + Some(repository.to_path_buf()) + } else { + match std::env::current_dir() { + Ok(current) => Some(current.join(repository)), + // Go's filepath.Abs returns an error here and the Rover + // caller retains the original repository string. + Err(_) => None, + } + }; + if let Some(absolute) = absolute { + let cleaned = normalize_path(&absolute); + fs::canonicalize(&cleaned).unwrap_or(cleaned) + } else { + repository.to_path_buf() + } + }; + let project_id = Sha256Digest::of(canonical_path.as_os_str().as_encoded_bytes()); + Ok(Self { + canonical_path, + project_id, + }) + } + + /// Return the resolved repository path. + #[must_use] + pub fn path(&self) -> &Path { + &self.canonical_path + } + + /// Return this project's SHA-256 identity. + #[must_use] + pub const fn project_id(&self) -> Sha256Digest { + self.project_id + } +} + +/// Derive the control audience bound to both state root and repository. +/// +/// This preserves the Go wire format: `rover-control/v1:` followed by the +/// lowercase SHA-256 of `state_root`, one NUL byte, and the repository path. +#[must_use] +pub fn control_audience(state_root: &Path, repository: &RepositoryIdentity) -> String { + let state_root = state_root.as_os_str().as_encoded_bytes(); + let repository = repository.canonical_path.as_os_str().as_encoded_bytes(); + let mut framed = Vec::with_capacity(state_root.len() + repository.len() + 1); + framed.extend_from_slice(state_root); + framed.push(0); + framed.extend_from_slice(repository); + format!("rover-control/v1:{}", Sha256Digest::of(&framed)) +} + +fn normalize_path(path: &Path) -> PathBuf { + let mut normalized = PathBuf::new(); + for component in path.components() { + match component { + Component::Prefix(prefix) => normalized.push(prefix.as_os_str()), + Component::RootDir => normalized.push(component.as_os_str()), + Component::CurDir => {} + Component::ParentDir => { + if normalized.file_name().is_some() { + normalized.pop(); + } else if !normalized.has_root() { + normalized.push(component.as_os_str()); + } + } + Component::Normal(part) => normalized.push(part), + } + } + normalized +} + +#[cfg(test)] +mod tests { + use std::fs; + #[cfg(unix)] + use std::os::unix::fs::symlink; + use std::path::PathBuf; + use std::sync::atomic::{AtomicU64, Ordering}; + use std::time::{SystemTime, UNIX_EPOCH}; + + use super::{control_audience, normalize_path, RepositoryIdentity}; + use crate::Sha256Digest; + + static TEST_SEQUENCE: AtomicU64 = AtomicU64::new(0); + + struct TestDirectory(PathBuf); + + impl TestDirectory { + fn new() -> Self { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("clock follows epoch") + .as_nanos(); + let path = std::env::temp_dir().join(format!( + "rover-project-id-{}-{timestamp}-{}", + std::process::id(), + TEST_SEQUENCE.fetch_add(1, Ordering::Relaxed) + )); + fs::create_dir(&path).expect("create test directory"); + Self(path) + } + } + + impl Drop for TestDirectory { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.0); + } + } + + #[test] + fn empty_and_unresolved_paths_follow_go_identity_fallback() { + let empty = RepositoryIdentity::resolve("").expect("resolve empty path"); + assert_eq!(empty.path(), PathBuf::new()); + assert_eq!(empty.project_id(), Sha256Digest::of(b"")); + + let directory = TestDirectory::new(); + let unresolved = directory.0.join("new-repository/../future"); + let identity = + RepositoryIdentity::resolve(unresolved.to_str().expect("UTF-8 fixture path")) + .expect("resolve missing path"); + assert_eq!(identity.path(), normalize_path(&unresolved)); + assert_eq!( + identity.project_id(), + Sha256Digest::of(identity.path().as_os_str().as_encoded_bytes()) + ); + } + + #[test] + #[cfg(unix)] + fn existing_path_variants_and_symlink_aliases_share_project_identity() { + let directory = TestDirectory::new(); + let repository = directory.0.join("repo"); + fs::create_dir(&repository).expect("create repository"); + let alias = directory.0.join("repo-alias"); + symlink(&repository, &alias).expect("create repository symlink"); + + let path = repository.to_str().expect("UTF-8 repository"); + let trailing = format!("{path}/./"); + let direct = RepositoryIdentity::resolve(path).expect("resolve direct path"); + let variant = RepositoryIdentity::resolve(&trailing).expect("resolve path variant"); + let linked = RepositoryIdentity::resolve(alias.to_str().expect("UTF-8 alias")) + .expect("resolve symlink alias"); + assert_eq!( + direct.path(), + fs::canonicalize(&repository).expect("canonical repository") + ); + assert_eq!(direct.project_id(), variant.project_id()); + assert_eq!(direct.project_id(), linked.project_id()); + } + + #[test] + #[cfg(unix)] + fn unresolved_symlink_descendants_use_clean_absolute_fallback() { + let directory = TestDirectory::new(); + let target = directory.0.join("target"); + fs::create_dir(&target).expect("create target"); + let alias = directory.0.join("alias"); + symlink(&target, &alias).expect("create alias"); + let unresolved_alias_child = alias.join("future"); + let unresolved_target_child = target.join("future"); + let alias_identity = RepositoryIdentity::resolve( + unresolved_alias_child.to_str().expect("UTF-8 alias child"), + ) + .expect("resolve missing alias child"); + let target_identity = RepositoryIdentity::resolve( + unresolved_target_child + .to_str() + .expect("UTF-8 target child"), + ) + .expect("resolve missing target child"); + assert_eq!( + alias_identity.path(), + normalize_path(&unresolved_alias_child) + ); + assert_ne!(alias_identity.project_id(), target_identity.project_id()); + } + + #[test] + fn control_audience_binds_state_root_and_repository() { + let directory = TestDirectory::new(); + let repository_path = directory.0.join("repo"); + fs::create_dir(&repository_path).expect("create repository"); + let repository = + RepositoryIdentity::resolve(repository_path.to_str().expect("UTF-8 repository")) + .expect("resolve repository"); + let first = control_audience(PathBuf::from("/state/one").as_path(), &repository); + let same = control_audience(PathBuf::from("/state/one").as_path(), &repository); + let other_state = control_audience(PathBuf::from("/state/two").as_path(), &repository); + let other_repository = + RepositoryIdentity::resolve("/another/project").expect("resolve other project"); + let other_project = + control_audience(PathBuf::from("/state/one").as_path(), &other_repository); + assert_eq!(first, same); + assert_ne!(first, other_state); + assert_ne!(first, other_project); + assert!(first.starts_with("rover-control/v1:")); + } +} diff --git a/crates/rover-core/src/task.rs b/crates/rover-core/src/task.rs new file mode 100644 index 0000000..36852c0 --- /dev/null +++ b/crates/rover-core/src/task.rs @@ -0,0 +1,400 @@ +//! Validated, execution-neutral task briefs and dependency graphs. +//! +//! A valid brief is not evidence that its quality-gate text is executable. +//! Gate syntax and execution policy belong to a later task-service contract. + +use crate::Id; +use serde::{Deserialize, Serialize}; +use std::collections::{BTreeMap, BTreeSet}; +use std::fmt; + +const TITLE_LIMIT: usize = 120; +const PATHS_LIMIT: usize = 8 * 1024; +const DEPENDENCIES_LIMIT: usize = 4 * 1024; +const QUALITY_GATE_LIMIT: usize = 1024; +const PROMPT_LIMIT: usize = 16 * 1024; +const MAX_PATHS: usize = 128; +const MAX_DEPENDENCIES: usize = 128; +const MAX_GRAPH_TASKS: usize = 10_000; + +/// User-authored task definition fields captured by Rover's TUI. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct TaskBrief { + pub title: String, + /// Newline-separated relative path globs. Supports `*`, `**`, and `?`. + pub paths: String, + /// Newline-separated Rover task IDs that must finish successfully first. + pub dependencies: String, + /// Opaque quality-gate description. It is never executed by this type. + pub quality_gate: String, + pub prompt: String, +} + +/// One task's dependency declaration, before task records are resolved. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TaskGraphNode { + pub id: Id, + pub dependencies: Vec, +} + +/// Invalid task brief or dependency graph. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum TaskBriefError { + EmptyTitle, + EmptyPrompt, + FieldTooLarge(&'static str), + ForbiddenCharacter(&'static str), + InvalidPathGlob, + TooManyPaths, + InvalidDependencyId, + DuplicateDependency, + TooManyDependencies, +} + +impl fmt::Display for TaskBriefError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::EmptyTitle => formatter.write_str("task title must not be empty"), + Self::EmptyPrompt => formatter.write_str("task prompt must not be empty"), + Self::FieldTooLarge(field) => write!(formatter, "task {field} exceeds its byte limit"), + Self::ForbiddenCharacter(field) => { + write!( + formatter, + "task {field} contains a forbidden control or bidi character" + ) + } + Self::InvalidPathGlob => { + formatter.write_str("task path glob is invalid or unsupported") + } + Self::TooManyPaths => formatter.write_str("task has too many path globs"), + Self::InvalidDependencyId => { + formatter.write_str("task dependency is not a valid Rover ID") + } + Self::DuplicateDependency => formatter.write_str("task has a duplicate dependency"), + Self::TooManyDependencies => formatter.write_str("task has too many dependencies"), + } + } +} + +impl std::error::Error for TaskBriefError {} + +/// Invalid graph structure. Missing dependencies and cycles fail closed. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum TaskGraphError { + TooManyTasks, + DuplicateTask(Id), + DuplicateDependency { task: Id, prerequisite: Id }, + MissingDependency { task: Id, prerequisite: Id }, + SelfDependency(Id), + Cycle(Vec), +} + +impl fmt::Display for TaskGraphError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::TooManyTasks => write!(formatter, "task graph exceeds {MAX_GRAPH_TASKS} tasks"), + Self::DuplicateTask(id) => write!(formatter, "duplicate task ID {id}"), + Self::DuplicateDependency { task, prerequisite } => { + write!(formatter, "task {task} repeats dependency {prerequisite}") + } + Self::MissingDependency { task, prerequisite } => { + write!( + formatter, + "task {task} depends on missing task {prerequisite}" + ) + } + Self::SelfDependency(id) => write!(formatter, "task {id} depends on itself"), + Self::Cycle(ids) => write!( + formatter, + "task dependency cycle includes {} task(s)", + ids.len() + ), + } + } +} + +impl std::error::Error for TaskGraphError {} + +impl TaskBrief { + /// Validate user-provided fields before creating a durable task. + /// + /// Draft persistence intentionally accepts incomplete drafts; call this + /// only when the user requests task creation. + /// + /// # Errors + /// + /// Returns an error when required fields, field bounds, path globs, or + /// dependency IDs are invalid. + pub fn validate(&self) -> Result<(), TaskBriefError> { + if self.title.trim().is_empty() { + return Err(TaskBriefError::EmptyTitle); + } + if self.prompt.trim().is_empty() { + return Err(TaskBriefError::EmptyPrompt); + } + for (name, value, limit, multiline) in [ + ("title", self.title.as_str(), TITLE_LIMIT, false), + ("paths", self.paths.as_str(), PATHS_LIMIT, true), + ( + "dependencies", + self.dependencies.as_str(), + DEPENDENCIES_LIMIT, + true, + ), + ( + "quality gate", + self.quality_gate.as_str(), + QUALITY_GATE_LIMIT, + true, + ), + ("prompt", self.prompt.as_str(), PROMPT_LIMIT, true), + ] { + if value.len() > limit { + return Err(TaskBriefError::FieldTooLarge(name)); + } + if value + .chars() + .any(|ch| (ch.is_control() && !(multiline && ch == '\n')) || is_bidi_format(ch)) + { + return Err(TaskBriefError::ForbiddenCharacter(name)); + } + } + + let paths = nonempty_lines(&self.paths); + if paths.len() > MAX_PATHS { + return Err(TaskBriefError::TooManyPaths); + } + if paths.iter().any(|path| !valid_path_glob(path)) { + return Err(TaskBriefError::InvalidPathGlob); + } + + let dependencies = nonempty_lines(&self.dependencies); + if dependencies.len() > MAX_DEPENDENCIES { + return Err(TaskBriefError::TooManyDependencies); + } + let mut seen = BTreeSet::new(); + for dependency in dependencies { + let id = Id::parse(dependency.to_owned()) + .map_err(|_| TaskBriefError::InvalidDependencyId)?; + if !seen.insert(id) { + return Err(TaskBriefError::DuplicateDependency); + } + } + Ok(()) + } +} + +/// Validate task IDs and ensure dependencies exist and form a DAG. +/// +/// Returns a stable topological order; ties are ordered lexically by task ID. +/// +/// # Errors +/// +/// Returns an error for an oversized graph, duplicate task or dependency IDs, +/// missing prerequisites, self-dependencies, or a dependency cycle. +pub fn validate_task_graph(nodes: &[TaskGraphNode]) -> Result, TaskGraphError> { + if nodes.len() > MAX_GRAPH_TASKS { + return Err(TaskGraphError::TooManyTasks); + } + let mut dependencies = BTreeMap::>::new(); + for node in nodes { + if dependencies.contains_key(&node.id) { + return Err(TaskGraphError::DuplicateTask(node.id.clone())); + } + let prerequisites = node.dependencies.iter().cloned().collect::>(); + if prerequisites.len() != node.dependencies.len() { + let mut seen = BTreeSet::new(); + for prerequisite in &node.dependencies { + if !seen.insert(prerequisite.clone()) { + return Err(TaskGraphError::DuplicateDependency { + task: node.id.clone(), + prerequisite: prerequisite.clone(), + }); + } + } + } + dependencies.insert(node.id.clone(), prerequisites); + } + for (task, prerequisites) in &dependencies { + for prerequisite in prerequisites { + if task == prerequisite { + return Err(TaskGraphError::SelfDependency(task.clone())); + } + if !dependencies.contains_key(prerequisite) { + return Err(TaskGraphError::MissingDependency { + task: task.clone(), + prerequisite: prerequisite.clone(), + }); + } + } + } + + let mut dependents = BTreeMap::>::new(); + let mut ready = BTreeSet::::new(); + for (task, prerequisites) in &dependencies { + if prerequisites.is_empty() { + ready.insert(task.clone()); + } + for prerequisite in prerequisites { + dependents + .entry(prerequisite.clone()) + .or_default() + .push(task.clone()); + } + } + let mut order = Vec::with_capacity(nodes.len()); + while let Some(id) = ready.pop_first() { + order.push(id.clone()); + if let Some(children) = dependents.get(&id) { + for child in children { + let Some(prerequisites) = dependencies.get_mut(child) else { + continue; + }; + prerequisites.remove(&id); + if prerequisites.is_empty() { + ready.insert(child.clone()); + } + } + } + } + if order.len() != nodes.len() { + let completed = order.into_iter().collect::>(); + let cycle = dependencies + .into_keys() + .filter(|id| !completed.contains(id)) + .collect(); + return Err(TaskGraphError::Cycle(cycle)); + } + Ok(order) +} + +fn nonempty_lines(value: &str) -> Vec<&str> { + value + .lines() + .filter(|line| !line.trim().is_empty()) + .collect() +} + +fn valid_path_glob(path: &str) -> bool { + if path.is_empty() + || path == "." + || path.starts_with('/') + || path.contains('\\') + || path.contains('\0') + || path + .chars() + .any(|ch| matches!(ch, '[' | ']' | '{' | '}' | '\r')) + || path + .split('/') + .any(|part| part.is_empty() || part == "." || part == "..") + { + return false; + } + true +} + +fn is_bidi_format(ch: char) -> bool { + matches!( + ch, + '\u{061c}' + | '\u{200e}'..='\u{200f}' + | '\u{202a}'..='\u{202e}' + | '\u{2066}'..='\u{2069}' + | '\u{206a}'..='\u{206f}' + ) +} + +#[cfg(test)] +mod tests { + use super::{validate_task_graph, TaskBrief, TaskBriefError, TaskGraphError, TaskGraphNode}; + use crate::Id; + + fn brief() -> TaskBrief { + TaskBrief { + title: "Feature work".into(), + paths: "src/**\nCargo.toml".into(), + dependencies: "task_base".into(), + quality_gate: "cargo test -p rover-core".into(), + prompt: "Implement the requested behavior".into(), + } + } + + fn node(id: &str, dependencies: &[&str]) -> TaskGraphNode { + TaskGraphNode { + id: Id::parse(id).unwrap(), + dependencies: dependencies + .iter() + .map(|value| Id::parse(*value).unwrap()) + .collect(), + } + } + + #[test] + fn validates_brief_and_supported_relative_globs() { + assert!(brief().validate().is_ok()); + for invalid in [ + "/etc/**", + "../secret", + "src/../secret", + "src\\**", + "src/[ab]", + "src//lib.rs", + "src/", + ] { + let mut value = brief(); + value.paths = invalid.into(); + assert_eq!( + value.validate(), + Err(TaskBriefError::InvalidPathGlob), + "{invalid}" + ); + } + } + + #[test] + fn validates_dependency_ids_and_duplicates() { + let mut value = brief(); + value.dependencies = "task_base\ntask_base".into(); + assert_eq!(value.validate(), Err(TaskBriefError::DuplicateDependency)); + value.dependencies = "bad/id".into(); + assert_eq!(value.validate(), Err(TaskBriefError::InvalidDependencyId)); + } + + #[test] + fn returns_deterministic_topological_order() { + let graph = [ + node("task_z", &["task_a"]), + node("task_a", &[]), + node("task_m", &[]), + ]; + let order = validate_task_graph(&graph).unwrap(); + assert_eq!( + order.iter().map(Id::as_str).collect::>(), + ["task_a", "task_m", "task_z"] + ); + } + + #[test] + fn rejects_missing_self_duplicate_and_cyclic_dependencies() { + assert!(matches!( + validate_task_graph(&[node("task_a", &["task_missing"])]), + Err(TaskGraphError::MissingDependency { .. }) + )); + assert_eq!( + validate_task_graph(&[node("task_a", &["task_a"])]), + Err(TaskGraphError::SelfDependency(Id::parse("task_a").unwrap())) + ); + assert_eq!( + validate_task_graph(&[node("task_a", &[]), node("task_a", &[])]), + Err(TaskGraphError::DuplicateTask(Id::parse("task_a").unwrap())) + ); + assert!(matches!( + validate_task_graph(&[node("task_a", &["task_b", "task_b"]), node("task_b", &[])]), + Err(TaskGraphError::DuplicateDependency { .. }) + )); + assert!(matches!( + validate_task_graph(&[node("task_a", &["task_b"]), node("task_b", &["task_a"])]), + Err(TaskGraphError::Cycle(_)) + )); + } +} diff --git a/crates/rover-execution/Cargo.toml b/crates/rover-execution/Cargo.toml new file mode 100644 index 0000000..af1ca9d --- /dev/null +++ b/crates/rover-execution/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "rover-execution" +version = "0.1.0" +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[dependencies] +alacritty_terminal = { version = "=0.26.0", default-features = false } +base64 = { version = "=0.22.1", default-features = false, features = ["std"] } +portable-pty = { version = "=0.9.0", default-features = false } +rover-core = { path = "../rover-core" } +rover-store = { path = "../rover-store" } +serde = { version = "=1.0.229", default-features = false, features = ["std", "derive"] } +serde_json = { version = "=1.0.151", default-features = false, features = ["std"] } +time = { version = "=0.3.55", default-features = false, features = ["formatting", "parsing", "std"] } + +[target.'cfg(unix)'.dependencies] +rustix = { version = "=1.1.4", default-features = false, features = ["fs", "process", "std"] } + + +[dev-dependencies] +rusqlite = { version = "=0.40.2", default-features = false, features = ["bundled"] } + +[lints] +workspace = true diff --git a/crates/rover-execution/src/layout.rs b/crates/rover-execution/src/layout.rs new file mode 100644 index 0000000..668aa47 --- /dev/null +++ b/crates/rover-execution/src/layout.rs @@ -0,0 +1,1121 @@ +//! Serializable terminal workspace, tab, split, zoom, popup, and scratch model. +//! +//! This module owns layout state only. It does not render widgets or imply that +//! a pane's referenced session is currently running. + +use std::io; +use std::path::PathBuf; + +use rover_core::Id; +#[cfg(unix)] +use rover_store::files::SafeDir; +use serde::{Deserialize, Serialize}; + +const WORKSPACE_SCHEMA: &str = "rover/terminal-workspace/v1"; +const MAX_TITLE_BYTES: usize = 256; +const MAX_WORKSPACE_BYTES: usize = 4 * 1024 * 1024; +const MAX_JSON_DEPTH: usize = 96; +const MAX_TABS: usize = 128; +const MAX_PANES: usize = 256; +const MAX_SPLIT_DEPTH: usize = 16; +const MAX_RATIO_BASIS_POINTS: u16 = 10_000; + +/// Direction used to divide one pane into two children. +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub enum SplitAxis { + /// Place children side by side. + Horizontal, + /// Place children one above the other. + Vertical, +} + +/// What a pane displays. A session reference is metadata, not proof the child +/// process exists; callers resolve it through the session server. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub enum PaneKind { + /// A named terminal session inside this workspace's namespace. + Session { name: String }, + /// A temporary terminal owned by this workspace. + Scratch, +} + +/// A pane specification accepted when creating tabs or splitting panes. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct PaneSpec { + kind: PaneKind, + cwd: Option, +} + +impl PaneSpec { + /// Create a reference to a session in the workspace namespace. + /// + /// # Errors + /// + /// Returns `InvalidInput` when the session name or working directory is + /// invalid. + pub fn session(name: impl Into, cwd: impl Into) -> io::Result { + let name = name.into(); + validate_id(&name, "session name")?; + let cwd = cwd.into(); + validate_cwd(&cwd)?; + Ok(Self { + kind: PaneKind::Session { name }, + cwd: Some(cwd), + }) + } + + /// Create a scratch pane with no associated directory. + #[must_use] + pub const fn scratch() -> Self { + Self { + kind: PaneKind::Scratch, + cwd: None, + } + } + + /// Pane content type. + #[must_use] + pub const fn kind(&self) -> &PaneKind { + &self.kind + } + + /// Optional working directory associated with the pane. + #[must_use] + pub fn cwd(&self) -> Option<&PathBuf> { + self.cwd.as_ref() + } +} + +/// One stable terminal pane. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct Pane { + id: String, + kind: PaneKind, + cwd: Option, +} + +impl Pane { + /// Stable identifier retained across layout edits and serialization. + #[must_use] + pub fn id(&self) -> &str { + &self.id + } + + /// Pane content type. + #[must_use] + pub const fn kind(&self) -> &PaneKind { + &self.kind + } + + /// Optional working directory. + #[must_use] + pub fn cwd(&self) -> Option<&PathBuf> { + self.cwd.as_ref() + } +} + +/// Binary split node with a stable ID and first-child size ratio. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct Split { + id: String, + axis: SplitAxis, + first_basis_points: u16, + first: Box, + second: Box, +} + +impl Split { + /// Stable identifier for resizing this split later. + #[must_use] + pub fn id(&self) -> &str { + &self.id + } + + /// Split orientation. + #[must_use] + pub const fn axis(&self) -> SplitAxis { + self.axis + } + + /// Size of the first child as basis points out of 10,000. + #[must_use] + pub const fn first_basis_points(&self) -> u16 { + self.first_basis_points + } + + /// First child in visual order. + #[must_use] + pub const fn first(&self) -> &PaneNode { + &self.first + } + + /// Second child in visual order. + #[must_use] + pub const fn second(&self) -> &PaneNode { + &self.second + } +} + +/// Recursive terminal-pane layout tree. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub enum PaneNode { + /// A terminal or scratch pane. + Leaf(Pane), + /// A two-child split. + Split(Split), +} + +/// One named tab with independent focus, zoom, popup, and split tree. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct Tab { + id: String, + title: String, + root: PaneNode, + active_pane: String, + zoomed_pane: Option, + popup_pane: Option, +} + +impl Tab { + /// Stable tab identifier. + #[must_use] + pub fn id(&self) -> &str { + &self.id + } + + /// User-visible tab title. + #[must_use] + pub fn title(&self) -> &str { + &self.title + } + + /// Root layout for rendering. + #[must_use] + pub const fn root(&self) -> &PaneNode { + &self.root + } + + /// Pane currently receiving keyboard input. + #[must_use] + pub fn active_pane(&self) -> &str { + &self.active_pane + } + + /// Pane shown alone while zoom is active. + #[must_use] + pub fn zoomed_pane(&self) -> Option<&str> { + self.zoomed_pane.as_deref() + } + + /// Pane displayed in the popup overlay. + #[must_use] + pub fn popup_pane(&self) -> Option<&str> { + self.popup_pane.as_deref() + } + + /// Current render root, honoring this tab's zoom state. + #[must_use] + pub fn render_root(&self) -> &PaneNode { + self.zoomed_pane + .as_deref() + .and_then(|id| find_node(&self.root, id)) + .unwrap_or(&self.root) + } +} + +/// One project-scoped terminal workspace with stable, serializable layouts. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct Workspace { + schema: String, + id: String, + namespace: String, + title: String, + tabs: Vec, + active_tab: String, +} + +impl Workspace { + /// Create a workspace with an initial tab and named session pane. + /// + /// # Errors + /// + /// Returns an I/O error for invalid names or title, or if secure ID + /// generation fails. + pub fn new( + namespace: impl Into, + title: impl Into, + session_name: impl Into, + cwd: impl Into, + ) -> io::Result { + let namespace = namespace.into(); + validate_id(&namespace, "workspace namespace")?; + let title = validate_title(title.into(), "workspace title")?; + let pane_spec = PaneSpec::session(session_name, cwd)?; + let pane = pane_from_spec(pane_spec)?; + let tab = Tab { + id: generate_id("tab")?, + title: "main".to_owned(), + active_pane: pane.id.clone(), + root: PaneNode::Leaf(pane), + zoomed_pane: None, + popup_pane: None, + }; + let workspace = Self { + schema: WORKSPACE_SCHEMA.to_owned(), + id: generate_id("workspace")?, + namespace, + title, + active_tab: tab.id.clone(), + tabs: vec![tab], + }; + workspace.validate()?; + Ok(workspace) + } + + /// Stable workspace identifier. + #[must_use] + pub fn id(&self) -> &str { + &self.id + } + + /// Namespace used to resolve all session panes. + #[must_use] + pub fn namespace(&self) -> &str { + &self.namespace + } + + /// User-visible workspace title. + #[must_use] + pub fn title(&self) -> &str { + &self.title + } + + /// Tabs in stable ordering. + #[must_use] + pub fn tabs(&self) -> &[Tab] { + &self.tabs + } + + /// Currently selected tab. + /// + /// # Panics + /// + /// Panics only if an internal invariant is broken: every valid workspace + /// keeps its active tab in `tabs`. + #[must_use] + pub fn active_tab(&self) -> &Tab { + self.tabs + .iter() + .find(|tab| tab.id == self.active_tab) + .expect("validated workspace keeps active tab present") + } + + /// Add and select a tab containing a session or scratch pane. + /// + /// # Errors + /// + /// Returns `InvalidInput` for an invalid title or when the workspace is at + /// its 128-tab limit, or an I/O error if secure ID generation fails. + pub fn add_tab(&mut self, title: impl Into, pane: PaneSpec) -> io::Result { + if self.tabs.len() >= MAX_TABS { + return Err(invalid_input("workspace exceeds 128 tabs")); + } + let title = validate_title(title.into(), "tab title")?; + let pane = pane_from_spec(pane)?; + let tab = Tab { + id: generate_id("tab")?, + title, + active_pane: pane.id.clone(), + root: PaneNode::Leaf(pane), + zoomed_pane: None, + popup_pane: None, + }; + self.active_tab.clone_from(&tab.id); + let id = tab.id.clone(); + self.tabs.push(tab); + Ok(id) + } + + /// Select a tab by stable ID. + /// + /// # Errors + /// + /// Returns `NotFound` if the tab does not belong to this workspace. + pub fn focus_tab(&mut self, tab_id: &str) -> io::Result<()> { + if !self.tabs.iter().any(|tab| tab.id == tab_id) { + return Err(not_found("tab not found in workspace")); + } + tab_id.clone_into(&mut self.active_tab); + Ok(()) + } + + /// Close a tab and select the adjacent surviving tab when necessary. + /// + /// # Errors + /// + /// Returns `NotFound` for an unknown tab and `InvalidInput` when closing + /// the workspace's final tab. + pub fn close_tab(&mut self, tab_id: &str) -> io::Result<()> { + let Some(index) = self.tabs.iter().position(|tab| tab.id == tab_id) else { + return Err(not_found("tab not found in workspace")); + }; + if self.tabs.len() == 1 { + return Err(invalid_input("workspace must keep at least one tab")); + } + self.tabs.remove(index); + if self.active_tab == tab_id { + self.active_tab = self.tabs[index.min(self.tabs.len() - 1)].id.clone(); + } + Ok(()) + } + + /// Add a pane beside/below the focused pane and make it active. + /// + /// # Errors + /// + /// Returns `InvalidInput` for an invalid split ratio or pane limit, and an + /// I/O error if secure IDs cannot be generated. + pub fn split_active( + &mut self, + axis: SplitAxis, + first_basis_points: u16, + pane: PaneSpec, + ) -> io::Result { + if first_basis_points == 0 || first_basis_points >= MAX_RATIO_BASIS_POINTS { + return Err(invalid_input( + "split ratio must be between 1 and 9999 basis points", + )); + } + if self.pane_count() >= MAX_PANES { + return Err(invalid_input("workspace exceeds 256 panes")); + } + let new_pane = pane_from_spec(pane)?; + let new_id = new_pane.id.clone(); + let split_id = generate_id("split")?; + let tab = self.active_tab_mut(); + replace_leaf_with_split( + &mut tab.root, + &tab.active_pane, + Split { + id: split_id, + axis, + first_basis_points, + first: Box::new(PaneNode::Leaf(Pane { + id: String::new(), + kind: PaneKind::Scratch, + cwd: None, + })), + second: Box::new(PaneNode::Leaf(new_pane)), + }, + )?; + tab.active_pane.clone_from(&new_id); + Ok(new_id) + } + + /// Resize one split using its stable ID. + /// + /// # Errors + /// + /// Returns `InvalidInput` for an invalid ratio, or `NotFound` when no split + /// has that ID in this workspace. + pub fn resize_split(&mut self, split_id: &str, first_basis_points: u16) -> io::Result<()> { + if first_basis_points == 0 || first_basis_points >= MAX_RATIO_BASIS_POINTS { + return Err(invalid_input( + "split ratio must be between 1 and 9999 basis points", + )); + } + if set_split_ratio( + &mut self.active_tab_mut().root, + split_id, + first_basis_points, + ) { + Ok(()) + } else { + Err(not_found("split not found in active tab")) + } + } + + /// Focus a pane in the active tab. + /// + /// # Errors + /// + /// Returns `NotFound` if the pane is not in the active tab. + pub fn focus_pane(&mut self, pane_id: &str) -> io::Result<()> { + let tab = self.active_tab_mut(); + if find_pane(&tab.root, pane_id).is_none() { + return Err(not_found("pane not found in active tab")); + } + pane_id.clone_into(&mut tab.active_pane); + Ok(()) + } + + /// Toggle zoom for the focused pane. + pub fn toggle_zoom(&mut self) { + let tab = self.active_tab_mut(); + if tab.zoomed_pane.as_deref() == Some(tab.active_pane.as_str()) { + tab.zoomed_pane = None; + } else { + tab.zoomed_pane = Some(tab.active_pane.clone()); + } + } + + /// Open a popup overlay for the focused pane. + pub fn open_popup(&mut self) { + let tab = self.active_tab_mut(); + tab.popup_pane = Some(tab.active_pane.clone()); + } + + /// Close an open popup, if any. + pub fn close_popup(&mut self) { + self.active_tab_mut().popup_pane = None; + } + + /// Close a pane. Its sibling replaces the split and the tab keeps focus on + /// a surviving pane. The final pane in a tab must be closed with the tab. + /// + /// # Errors + /// + /// Returns `NotFound` for an unknown pane and `InvalidInput` when it is the + /// tab's final pane. + /// + /// # Panics + /// + /// Panics only if the pane-tree invariant is broken while removing a pane; + /// a validated tree with more than one pane always has a surviving sibling. + pub fn close_pane(&mut self, pane_id: &str) -> io::Result<()> { + let tab = self.active_tab_mut(); + if find_pane(&tab.root, pane_id).is_none() { + return Err(not_found("pane not found in active tab")); + } + if count_panes(&tab.root) == 1 { + return Err(invalid_input("close the tab to remove its final pane")); + } + remove_pane(&mut tab.root, pane_id); + if tab.active_pane == pane_id { + tab.active_pane = first_pane(&tab.root) + .expect("a sibling pane survives close") + .to_owned(); + } + if tab.zoomed_pane.as_deref() == Some(pane_id) { + tab.zoomed_pane = None; + } + if tab.popup_pane.as_deref() == Some(pane_id) { + tab.popup_pane = None; + } + Ok(()) + } + + /// Count all terminal and scratch panes in the workspace. + #[must_use] + pub fn pane_count(&self) -> usize { + self.tabs.iter().map(|tab| count_panes(&tab.root)).sum() + } + + /// Validate deserialized workspace state and all cross-references. + /// + /// # Errors + /// + /// Returns `InvalidData` if schema, IDs, titles, tree bounds, namespace + /// references, active focus, zoom, or popup state are invalid. + pub fn validate(&self) -> io::Result<()> { + if self.schema != WORKSPACE_SCHEMA { + return Err(invalid_data("unsupported terminal workspace schema")); + } + validate_id(&self.id, "workspace ID").map_err(|error| as_invalid_data(&error))?; + validate_id(&self.namespace, "workspace namespace") + .map_err(|error| as_invalid_data(&error))?; + validate_title(self.title.clone(), "workspace title") + .map_err(|error| as_invalid_data(&error))?; + if self.tabs.is_empty() || self.tabs.len() > MAX_TABS { + return Err(invalid_data( + "workspace tab count is outside supported bounds", + )); + } + let mut all_ids = vec![self.id.as_str()]; + let mut pane_total = 0; + for tab in &self.tabs { + validate_id(&tab.id, "tab ID").map_err(|error| as_invalid_data(&error))?; + validate_title(tab.title.clone(), "tab title") + .map_err(|error| as_invalid_data(&error))?; + all_ids.push(&tab.id); + validate_node(&tab.root, 0, &mut pane_total, &mut all_ids)?; + if find_pane(&tab.root, &tab.active_pane).is_none() { + return Err(invalid_data("active pane is missing from its tab")); + } + for optional_pane in [tab.zoomed_pane.as_deref(), tab.popup_pane.as_deref()] + .into_iter() + .flatten() + { + if find_pane(&tab.root, optional_pane).is_none() { + return Err(invalid_data("zoom or popup pane is missing from its tab")); + } + } + } + if pane_total > MAX_PANES { + return Err(invalid_data("workspace exceeds 256 panes")); + } + if !self.tabs.iter().any(|tab| tab.id == self.active_tab) { + return Err(invalid_data("active tab is missing from workspace")); + } + all_ids.sort_unstable(); + if all_ids.windows(2).any(|pair| pair[0] == pair[1]) { + return Err(invalid_data("workspace contains duplicate stable IDs")); + } + Ok(()) + } + + /// Serialize to JSON after validating all current invariants. + /// + /// # Errors + /// + /// Returns an I/O error if validation or JSON serialization fails. + pub fn to_json(&self) -> io::Result> { + self.validate()?; + let bytes = serde_json::to_vec(self).map_err(|error| invalid_data(error.to_string()))?; + if bytes.len() > MAX_WORKSPACE_BYTES { + return Err(invalid_data("terminal workspace JSON exceeds 4 MiB")); + } + Ok(bytes) + } + + /// Deserialize JSON and validate every ID and tree reference. + /// + /// # Errors + /// + /// Returns `InvalidData` for malformed JSON or invalid workspace state. + pub fn from_json(bytes: &[u8]) -> io::Result { + if bytes.len() > MAX_WORKSPACE_BYTES { + return Err(invalid_data("terminal workspace JSON exceeds 4 MiB")); + } + validate_json_depth(bytes)?; + let workspace: Self = + serde_json::from_slice(bytes).map_err(|error| invalid_data(error.to_string()))?; + workspace.validate()?; + Ok(workspace) + } + + /// Atomically persist this validated workspace as a private state file. + /// + /// The directory must already be an admitted private Rover state + /// directory. The file is written with mode `0600`; the basename is + /// validated by the descriptor-relative filesystem layer. + /// + /// # Errors + /// + /// Returns an I/O error if workspace validation, serialization, or the + /// atomic file write fails. + #[cfg(unix)] + pub fn save_to(&self, directory: &SafeDir, name: &str) -> io::Result<()> { + directory.atomic_write(name, &self.to_json()?, 0o600) + } + + /// Load a bounded, validated workspace from a private state directory. + /// + /// The method reads at most 4 MiB plus one byte so oversized layouts are + /// rejected instead of being silently truncated. + /// + /// # Errors + /// + /// Returns an I/O error for missing/unsafe files, oversized data, or + /// invalid workspace JSON. + #[cfg(unix)] + pub fn load_from(directory: &SafeDir, name: &str) -> io::Result { + let bytes = directory.bounded_read( + name, + i64::try_from(MAX_WORKSPACE_BYTES + 1) + .map_err(|_| invalid_data("workspace size limit exceeds file API"))?, + )?; + Self::from_json(&bytes) + } + + fn active_tab_mut(&mut self) -> &mut Tab { + self.tabs + .iter_mut() + .find(|tab| tab.id == self.active_tab) + .expect("validated workspace keeps active tab present") + } +} + +fn validate_json_depth(bytes: &[u8]) -> io::Result<()> { + let mut depth = 0_usize; + let mut in_string = false; + let mut escaped = false; + for byte in bytes { + if in_string { + if escaped { + escaped = false; + } else if *byte == b'\\' { + escaped = true; + } else if *byte == b'"' { + in_string = false; + } + continue; + } + match byte { + b'"' => in_string = true, + b'{' | b'[' => { + depth += 1; + if depth > MAX_JSON_DEPTH { + return Err(invalid_data( + "terminal workspace JSON exceeds nesting limit", + )); + } + } + b'}' | b']' => depth = depth.saturating_sub(1), + _ => {} + } + } + Ok(()) +} + +fn validate_node<'a>( + node: &'a PaneNode, + depth: usize, + pane_total: &mut usize, + all_ids: &mut Vec<&'a str>, +) -> io::Result<()> { + if depth > MAX_SPLIT_DEPTH { + return Err(invalid_data("workspace split tree exceeds depth limit")); + } + match node { + PaneNode::Leaf(pane) => { + *pane_total += 1; + validate_id(&pane.id, "pane ID").map_err(|error| as_invalid_data(&error))?; + all_ids.push(&pane.id); + match &pane.kind { + PaneKind::Session { name } => { + validate_id(name, "session name").map_err(|error| as_invalid_data(&error))?; + let cwd = pane + .cwd + .as_ref() + .ok_or_else(|| invalid_data("session pane has no working directory"))?; + validate_cwd(cwd).map_err(|error| as_invalid_data(&error))?; + } + PaneKind::Scratch if pane.cwd.is_none() => {} + PaneKind::Scratch => { + return Err(invalid_data( + "scratch pane cannot claim a session directory", + )); + } + } + } + PaneNode::Split(split) => { + validate_id(&split.id, "split ID").map_err(|error| as_invalid_data(&error))?; + all_ids.push(&split.id); + if split.first_basis_points == 0 || split.first_basis_points >= MAX_RATIO_BASIS_POINTS { + return Err(invalid_data("split ratio is outside supported bounds")); + } + validate_node(&split.first, depth + 1, pane_total, all_ids)?; + validate_node(&split.second, depth + 1, pane_total, all_ids)?; + } + } + Ok(()) +} + +fn pane_from_spec(spec: PaneSpec) -> io::Result { + Ok(Pane { + id: generate_id("pane")?, + kind: spec.kind, + cwd: spec.cwd, + }) +} + +fn generate_id(prefix: &str) -> io::Result { + Id::generate(prefix) + .map(|id| id.to_string()) + .map_err(|error| io::Error::other(error.to_string())) +} + +fn validate_id(value: &str, label: &str) -> io::Result<()> { + Id::parse(value.to_owned()) + .map(|_| ()) + .map_err(|_| invalid_input(format!("invalid {label}"))) +} + +fn validate_title(value: String, label: &str) -> io::Result { + if value.trim().is_empty() + || value.len() > MAX_TITLE_BYTES + || value.chars().any(char::is_control) + { + return Err(invalid_input(format!("invalid {label}"))); + } + Ok(value) +} + +fn validate_cwd(cwd: &std::path::Path) -> io::Result<()> { + if !cwd.is_absolute() || cwd.as_os_str().is_empty() { + return Err(invalid_input("pane working directory must be absolute")); + } + Ok(()) +} + +fn replace_leaf_with_split(root: &mut PaneNode, pane_id: &str, split: Split) -> io::Result<()> { + if matches!(root, PaneNode::Leaf(pane) if pane.id == pane_id) { + let PaneNode::Leaf(existing) = std::mem::replace( + root, + PaneNode::Leaf(Pane { + id: String::new(), + kind: PaneKind::Scratch, + cwd: None, + }), + ) else { + unreachable!(); + }; + *root = PaneNode::Split(Split { + first: Box::new(PaneNode::Leaf(existing)), + ..split + }); + return Ok(()); + } + match root { + PaneNode::Leaf(_) => Err(not_found("active pane not found in layout")), + PaneNode::Split(current) => { + if replace_leaf_with_split(&mut current.first, pane_id, split.clone()).is_ok() { + Ok(()) + } else { + replace_leaf_with_split(&mut current.second, pane_id, split) + } + } + } +} + +fn set_split_ratio(root: &mut PaneNode, split_id: &str, ratio: u16) -> bool { + match root { + PaneNode::Leaf(_) => false, + PaneNode::Split(split) if split.id == split_id => { + split.first_basis_points = ratio; + true + } + PaneNode::Split(split) => { + set_split_ratio(&mut split.first, split_id, ratio) + || set_split_ratio(&mut split.second, split_id, ratio) + } + } +} + +fn remove_pane(root: &mut PaneNode, pane_id: &str) -> bool { + let PaneNode::Split(split) = root else { + return false; + }; + if matches!(split.first.as_ref(), PaneNode::Leaf(pane) if pane.id == pane_id) { + *root = *split.second.clone(); + return true; + } + if matches!(split.second.as_ref(), PaneNode::Leaf(pane) if pane.id == pane_id) { + *root = *split.first.clone(); + return true; + } + remove_pane(&mut split.first, pane_id) || remove_pane(&mut split.second, pane_id) +} + +fn find_pane<'a>(root: &'a PaneNode, pane_id: &str) -> Option<&'a Pane> { + match root { + PaneNode::Leaf(pane) if pane.id == pane_id => Some(pane), + PaneNode::Leaf(_) => None, + PaneNode::Split(split) => { + find_pane(&split.first, pane_id).or_else(|| find_pane(&split.second, pane_id)) + } + } +} + +fn find_node<'a>(root: &'a PaneNode, pane_id: &str) -> Option<&'a PaneNode> { + match root { + PaneNode::Leaf(pane) if pane.id == pane_id => Some(root), + PaneNode::Leaf(_) => None, + PaneNode::Split(split) => { + find_node(&split.first, pane_id).or_else(|| find_node(&split.second, pane_id)) + } + } +} + +fn first_pane(root: &PaneNode) -> Option<&str> { + match root { + PaneNode::Leaf(pane) => Some(&pane.id), + PaneNode::Split(split) => first_pane(&split.first).or_else(|| first_pane(&split.second)), + } +} + +fn count_panes(root: &PaneNode) -> usize { + match root { + PaneNode::Leaf(_) => 1, + PaneNode::Split(split) => count_panes(&split.first) + count_panes(&split.second), + } +} + +fn invalid_input(message: impl Into) -> io::Error { + io::Error::new(io::ErrorKind::InvalidInput, message.into()) +} + +fn invalid_data(message: impl Into) -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, message.into()) +} + +fn not_found(message: impl Into) -> io::Error { + io::Error::new(io::ErrorKind::NotFound, message.into()) +} + +fn as_invalid_data(error: &io::Error) -> io::Error { + invalid_data(error.to_string()) +} + +#[cfg(test)] +mod tests { + use super::*; + #[cfg(unix)] + use std::fs; + #[cfg(unix)] + use std::os::unix::fs::{symlink, PermissionsExt}; + + fn workspace() -> Workspace { + Workspace::new("project", "Project", "main", PathBuf::from("/repo")) + .expect("create workspace") + } + + #[cfg(unix)] + fn private_fixture_dir() -> (PathBuf, SafeDir) { + let path = std::env::temp_dir().join(format!( + "rover-layout-test-{}-{}", + std::process::id(), + generate_id("test").unwrap() + )); + fs::create_dir(&path).unwrap(); + fs::set_permissions(&path, fs::Permissions::from_mode(0o700)).unwrap(); + let directory = SafeDir::open(&path).unwrap(); + (path, directory) + } + + #[test] + fn splits_focus_and_resize_preserve_stable_ids() { + let mut workspace = workspace(); + let first_id = workspace.active_tab().active_pane().to_owned(); + let second_id = workspace + .split_active(SplitAxis::Horizontal, 4500, PaneSpec::scratch()) + .expect("split focused pane"); + let PaneNode::Split(split) = workspace.active_tab().root() else { + panic!("split root expected"); + }; + let split_id = split.id().to_owned(); + assert_eq!(split.first_basis_points(), 4500); + assert_eq!(workspace.active_tab().active_pane(), second_id); + workspace.resize_split(&split_id, 6000).unwrap(); + workspace.focus_pane(&first_id).unwrap(); + assert_eq!(workspace.active_tab().active_pane(), first_id); + assert_eq!(workspace.pane_count(), 2); + } + + #[test] + fn pane_close_collapses_tree_and_clears_zoom_and_popup_refs() { + let mut workspace = workspace(); + let first = workspace.active_tab().active_pane().to_owned(); + let second = workspace + .split_active(SplitAxis::Vertical, 5000, PaneSpec::scratch()) + .unwrap(); + workspace.toggle_zoom(); + workspace.open_popup(); + workspace.close_pane(&second).unwrap(); + assert_eq!(workspace.pane_count(), 1); + assert!(matches!(workspace.active_tab().root(), PaneNode::Leaf(_))); + assert_eq!(workspace.active_tab().active_pane(), first); + assert_eq!(workspace.active_tab().zoomed_pane(), None); + assert_eq!(workspace.active_tab().popup_pane(), None); + } + + #[test] + fn nested_split_and_close_preserve_sibling_subtrees() { + let mut workspace = workspace(); + let first = workspace.active_tab().active_pane().to_owned(); + let second = workspace + .split_active(SplitAxis::Horizontal, 4000, PaneSpec::scratch()) + .unwrap(); + workspace.focus_pane(&first).unwrap(); + let third = workspace + .split_active(SplitAxis::Vertical, 6000, PaneSpec::scratch()) + .unwrap(); + + assert_eq!(workspace.pane_count(), 3); + assert_eq!(workspace.active_tab().active_pane(), third); + workspace.validate().unwrap(); + workspace.close_pane(&first).unwrap(); + assert_eq!(workspace.pane_count(), 2); + assert!(find_pane(workspace.active_tab().root(), &second).is_some()); + assert!(find_pane(workspace.active_tab().root(), &third).is_some()); + workspace.validate().unwrap(); + } + + #[test] + fn tabs_keep_independent_focus_and_closing_active_selects_neighbor() { + let mut workspace = workspace(); + let first_tab = workspace.active_tab().id().to_owned(); + let second_tab = workspace.add_tab("scratch", PaneSpec::scratch()).unwrap(); + assert_eq!(workspace.active_tab().id(), second_tab); + workspace.focus_tab(&first_tab).unwrap(); + assert_eq!(workspace.active_tab().title(), "main"); + workspace.close_tab(&first_tab).unwrap(); + assert_eq!(workspace.active_tab().id(), second_tab); + } + + #[test] + fn zoom_and_popup_are_independent_per_tab() { + let mut workspace = workspace(); + let pane = workspace.active_tab().active_pane().to_owned(); + workspace.toggle_zoom(); + workspace.open_popup(); + assert_eq!(workspace.active_tab().zoomed_pane(), Some(pane.as_str())); + assert_eq!(workspace.active_tab().popup_pane(), Some(pane.as_str())); + assert!(matches!( + workspace.active_tab().render_root(), + PaneNode::Leaf(_) + )); + workspace.close_popup(); + assert!(workspace.active_tab().popup_pane().is_none()); + workspace.toggle_zoom(); + assert!(workspace.active_tab().zoomed_pane().is_none()); + } + + #[test] + fn serialization_round_trip_preserves_ids_order_and_layout() { + let mut workspace = workspace(); + workspace + .split_active(SplitAxis::Horizontal, 3333, PaneSpec::scratch()) + .unwrap(); + let expected = workspace.clone(); + let bytes = workspace.to_json().unwrap(); + let restored = Workspace::from_json(&bytes).unwrap(); + assert_eq!(restored, expected); + } + + #[cfg(unix)] + #[test] + fn workspace_state_saves_atomically_with_private_mode_and_restores_layout() { + let (path, directory) = private_fixture_dir(); + let mut workspace = workspace(); + workspace + .split_active(SplitAxis::Horizontal, 4_500, PaneSpec::scratch()) + .unwrap(); + workspace.save_to(&directory, "workspace.json").unwrap(); + assert_eq!( + Workspace::load_from(&directory, "workspace.json").unwrap(), + workspace + ); + assert_eq!( + fs::metadata(path.join("workspace.json")) + .unwrap() + .permissions() + .mode() + & 0o777, + 0o600 + ); + + let active = workspace.active_tab().active_pane().to_owned(); + workspace.close_pane(&active).unwrap(); + workspace.save_to(&directory, "workspace.json").unwrap(); + assert_eq!( + Workspace::load_from(&directory, "workspace.json").unwrap(), + workspace + ); + drop(directory); + fs::remove_dir_all(path).unwrap(); + } + + #[cfg(unix)] + #[test] + fn workspace_state_refuses_symlinks_oversize_and_invalid_json() { + let (path, directory) = private_fixture_dir(); + symlink("missing.json", path.join("linked.json")).unwrap(); + assert!(Workspace::load_from(&directory, "linked.json").is_err()); + + fs::write(path.join("large.json"), vec![b' '; MAX_WORKSPACE_BYTES + 1]).unwrap(); + assert_eq!( + Workspace::load_from(&directory, "large.json") + .unwrap_err() + .kind(), + io::ErrorKind::InvalidData + ); + + directory.atomic_write("broken.json", b"{}", 0o600).unwrap(); + assert_eq!( + Workspace::load_from(&directory, "broken.json") + .unwrap_err() + .kind(), + io::ErrorKind::InvalidData + ); + drop(directory); + fs::remove_dir_all(path).unwrap(); + } + + #[test] + fn deserialization_rejects_corrupt_schema_ids_and_references() { + let workspace = workspace(); + let mut value: serde_json::Value = + serde_json::from_slice(&workspace.to_json().unwrap()).unwrap(); + value["schema"] = "rover/terminal-workspace/v0".into(); + assert_eq!( + Workspace::from_json(&serde_json::to_vec(&value).unwrap()) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidData + ); + + value = serde_json::from_slice(&workspace.to_json().unwrap()).unwrap(); + value["tabs"][0]["active_pane"] = "missing".into(); + assert_eq!( + Workspace::from_json(&serde_json::to_vec(&value).unwrap()) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidData + ); + } + + #[test] + fn deserialization_bounds_size_and_nesting_before_recursive_decode() { + let too_deep = format!( + "{}null{}", + "[".repeat(MAX_JSON_DEPTH + 1), + "]".repeat(MAX_JSON_DEPTH + 1) + ); + assert_eq!( + Workspace::from_json(too_deep.as_bytes()) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidData + ); + assert_eq!( + Workspace::from_json(&vec![b' '; MAX_WORKSPACE_BYTES + 1]) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidData + ); + let oversized_path = PathBuf::from(format!("/{}", "x".repeat(MAX_WORKSPACE_BYTES))); + assert!(Workspace::new("project", "Project", "main", oversized_path) + .unwrap() + .to_json() + .is_err()); + + let mut value: serde_json::Value = + serde_json::from_slice(&workspace().to_json().unwrap()).unwrap(); + value["title"] = "[".repeat(MAX_JSON_DEPTH + 1).into(); + assert!(Workspace::from_json(&serde_json::to_vec(&value).unwrap()).is_ok()); + } + + #[test] + fn invalid_titles_ratios_and_final_pane_close_are_refused() { + assert!(Workspace::new("project", "\u{1b}[31m", "main", "/repo").is_err()); + let mut workspace = workspace(); + assert!(workspace + .split_active(SplitAxis::Horizontal, 10_000, PaneSpec::scratch()) + .is_err()); + let pane = workspace.active_tab().active_pane().to_owned(); + assert!(workspace.close_pane(&pane).is_err()); + assert_eq!(workspace.pane_count(), 1); + } + + #[test] + fn stable_ids_are_unique_across_workspace_tabs_splits_and_panes() { + let mut workspace = workspace(); + workspace + .split_active(SplitAxis::Horizontal, 5000, PaneSpec::scratch()) + .unwrap(); + workspace.add_tab("other", PaneSpec::scratch()).unwrap(); + workspace.validate().unwrap(); + } +} diff --git a/crates/rover-execution/src/lib.rs b/crates/rover-execution/src/lib.rs new file mode 100644 index 0000000..8c95cfa --- /dev/null +++ b/crates/rover-execution/src/lib.rs @@ -0,0 +1,1056 @@ +//! Bounded argv-only local process execution. +//! +//! Local execution runs with the current operating-system user's authority. It +//! is not a sandbox. The crate implements process and task supervision, native +//! PTYs, bounded terminal screens, and Unix local-session brokering. It does +//! not provide Docker isolation or durable session restart restoration. + +pub mod layout; +#[cfg(any(unix, windows))] +pub mod pty; +#[cfg(unix)] +pub mod sessions; +pub mod supervisor; +pub mod terminal; + +#[cfg(unix)] +use std::collections::BTreeMap; +#[cfg(unix)] +use std::ffi::OsString; +use std::fmt; +#[cfg(unix)] +use std::fs::{self, File}; +use std::io; +#[cfg(unix)] +use std::io::{Read, Write}; +#[cfg(unix)] +use std::path::Path; +use std::path::PathBuf; +#[cfg(unix)] +use std::process::{Command, ExitStatus, Stdio}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +#[cfg(unix)] +use std::thread; +use std::time::Duration; +#[cfg(unix)] +use std::time::Instant; + +#[cfg(unix)] +use rover_core::Sha256Digest; +#[cfg(unix)] +use std::os::unix::process::{CommandExt, ExitStatusExt}; + +pub const OUTPUT_LIMIT: usize = 1 << 20; +#[cfg(unix)] +const EXECUTABLE_HASH_LIMIT: u64 = 128 << 20; +#[cfg(unix)] +const POLL_INTERVAL: Duration = Duration::from_millis(10); +#[cfg(unix)] +const PIPE_DRAIN_GRACE: Duration = Duration::from_secs(1); + +/// Inputs for one local process invocation. +#[derive(Clone, Debug)] +pub struct Options { + pub dir: PathBuf, + pub output_dir: PathBuf, + pub argv: Vec, + pub timeout: Duration, + pub pass_env: Vec, +} + +/// Cooperative cancellation handle. Dropping a handle does not cancel a run. +#[derive(Clone, Debug, Default)] +pub struct CancellationToken(Arc); + +impl CancellationToken { + /// Request cancellation of an active or future run using this token. + pub fn cancel(&self) { + self.0.store(true, Ordering::Release); + } + + /// Return whether cancellation has been requested. + #[must_use] + pub fn is_cancelled(&self) -> bool { + self.0.load(Ordering::Acquire) + } +} + +/// Serializable execution evidence fields compatible with Rover's Go model. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ProcessResult { + pub started_at: String, + pub finished_at: String, + pub exit_code: i32, + pub error: Option, + pub timed_out: bool, + pub cancelled: bool, + pub truncated: bool, + pub stdout_sha256: String, + pub stderr_sha256: String, + pub stdout_bytes: u64, + pub stderr_bytes: u64, +} + +/// Process output and executable identity retained for downstream evidence. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ResultRecord { + pub process: ProcessResult, + pub stdout: Vec, + pub stderr: Vec, + pub executable: PathBuf, + pub executable_sha256: Option, +} + +/// Validation, setup, persistence, or process I/O failure. +#[derive(Debug)] +pub struct RunError(io::Error); + +impl fmt::Display for RunError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(formatter) + } +} + +impl std::error::Error for RunError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + Some(&self.0) + } +} + +impl From for RunError { + fn from(error: io::Error) -> Self { + Self(error) + } +} + +#[cfg(unix)] +struct Capture { + file: File, + bytes: Vec, + total: u64, + truncated: bool, +} + +#[cfg(unix)] +impl Capture { + fn new(file: File) -> Self { + Self { + file, + bytes: Vec::with_capacity(4096), + total: 0, + truncated: false, + } + } + + fn append(&mut self, bytes: &[u8]) -> io::Result<()> { + self.total = self.total.saturating_add(bytes.len() as u64); + let remaining = OUTPUT_LIMIT.saturating_sub(self.bytes.len()); + let kept = remaining.min(bytes.len()); + self.bytes.extend_from_slice(&bytes[..kept]); + self.file.write_all(&bytes[..kept])?; + self.truncated |= kept != bytes.len(); + Ok(()) + } + + fn complete(self) -> io::Result { + self.file.sync_all()?; + Ok(CapturedOutput { + bytes: self.bytes, + total: self.total, + truncated: self.truncated, + }) + } +} + +#[cfg(unix)] +struct CapturedOutput { + bytes: Vec, + total: u64, + truncated: bool, +} + +#[cfg(unix)] +struct RunArtifacts { + stdout_log: File, + stderr_log: File, + executable: PathBuf, + executable_sha256: Option, +} + +/// Start one argv-only local command, retain bounded stdout/stderr, and persist +/// the retained bytes as exclusive private log files under `output_dir`. +/// +/// # Errors +/// +/// Returns an error for invalid argv/environment/timeout, unsafe private output +/// paths, failure to create logs, or pipe read/persistence failures. A command +/// that cannot be spawned is represented in `ProcessResult.error`, matching +/// Rover's existing evidence behavior. +/// +/// # Panics +/// +/// Panics only if `std::process::Command` fails to provide a pipe that was +/// explicitly requested as piped. +#[cfg(unix)] +pub fn run(options: &Options, cancellation: &CancellationToken) -> Result { + validate(options)?; + let output = rover_store::files::prepare_private_directory(&options.output_dir)?; + let stdout_log = output.create_new_file_writer("stdout.log", 0o600)?; + let stderr_log = output.create_new_file_writer("stderr.log", 0o600)?; + let timeout_deadline = Instant::now() + .checked_add(options.timeout) + .unwrap_or_else(Instant::now); + let (environment, host_path) = clean_environment(options)?; + + let (executable, executable_not_found) = + resolve_executable(&options.argv[0], &options.dir, &host_path); + let executable_sha256 = hash_executable(&executable); + let started_at = rover_core::now().to_string(); + + let mut process = ProcessResult { + started_at, + finished_at: String::new(), + exit_code: -1, + error: None, + timed_out: false, + cancelled: false, + truncated: false, + stdout_sha256: String::new(), + stderr_sha256: String::new(), + stdout_bytes: 0, + stderr_bytes: 0, + }; + + if executable_not_found { + process.error = Some(format!( + "exec: \"{}\": executable file not found in $PATH", + options.argv[0] + )); + process.finished_at = rover_core::now().to_string(); + return finish_record( + process, + Capture::new(stdout_log), + Capture::new(stderr_log), + executable, + executable_sha256, + ); + } + + if cancellation.is_cancelled() { + process.cancelled = true; + process.error = Some("context canceled".into()); + process.finished_at = rover_core::now().to_string(); + return finish_record( + process, + Capture::new(stdout_log), + Capture::new(stderr_log), + executable, + executable_sha256, + ); + } + + let mut command = Command::new(&executable); + command + .args(&options.argv[1..]) + .current_dir(&options.dir) + .env_clear() + .envs(&environment) + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + command.process_group(0); + + run_child( + command, + process, + cancellation, + timeout_deadline, + RunArtifacts { + stdout_log, + stderr_log, + executable, + executable_sha256, + }, + ) +} + +#[cfg(unix)] +fn run_child( + mut command: Command, + mut process: ProcessResult, + cancellation: &CancellationToken, + deadline: Instant, + artifacts: RunArtifacts, +) -> Result { + let RunArtifacts { + stdout_log, + stderr_log, + executable, + executable_sha256, + } = artifacts; + if cancellation.is_cancelled() || Instant::now() >= deadline { + process.cancelled = cancellation.is_cancelled(); + process.timed_out = !process.cancelled; + process.error = Some(if process.cancelled { + "context canceled".into() + } else { + "context deadline exceeded".into() + }); + process.finished_at = rover_core::now().to_string(); + return finish_record( + process, + Capture::new(stdout_log), + Capture::new(stderr_log), + executable, + executable_sha256, + ); + } + + let mut child = match command.spawn() { + Ok(child) => child, + Err(error) => { + process.error = Some(format!("fork/exec {}: {error}", executable.display())); + process.finished_at = rover_core::now().to_string(); + return finish_record( + process, + Capture::new(stdout_log), + Capture::new(stderr_log), + executable, + executable_sha256, + ); + } + }; + let process_id = rustix::process::Pid::from_child(&child); + let stdout = child.stdout.take().expect("stdout pipe was requested"); + let stderr = child.stderr.take().expect("stderr pipe was requested"); + let finished = Arc::new(AtomicBool::new(false)); + let stdout_finished = Arc::clone(&finished); + let stderr_finished = Arc::clone(&finished); + let stdout_reader = thread::spawn(move || read_capture(stdout, &stdout_finished, stdout_log)); + let stderr_reader = thread::spawn(move || read_capture(stderr, &stderr_finished, stderr_log)); + let mut captures = CaptureThreads { + finished, + stdout: Some(stdout_reader), + stderr: Some(stderr_reader), + }; + let outcome = wait_for_child( + &mut child, + process_id, + cancellation, + deadline, + &mut captures, + )?; + process.cancelled = outcome.cancelled; + process.timed_out = outcome.timed_out; + set_exit_status(&mut process, outcome.status); + let (stdout, stderr) = captures.join()?; + process.finished_at = rover_core::now().to_string(); + finish_record(process, stdout, stderr, executable, executable_sha256) +} + +#[cfg(unix)] +struct CaptureThreads { + finished: Arc, + stdout: Option>>, + stderr: Option>>, +} + +#[cfg(unix)] +impl CaptureThreads { + fn join(&mut self) -> io::Result<(Capture, Capture)> { + self.finished.store(true, Ordering::Release); + let stdout = self + .stdout + .take() + .expect("stdout capture thread exists") + .join() + .map_err(|_| io::Error::other("stdout capture thread panicked"))??; + let stderr = self + .stderr + .take() + .expect("stderr capture thread exists") + .join() + .map_err(|_| io::Error::other("stderr capture thread panicked"))??; + Ok((stdout, stderr)) + } + + fn stop_after_wait_error( + &mut self, + child: &mut std::process::Child, + error: io::Error, + ) -> RunError { + kill_group(rustix::process::Pid::from_child(child), child); + let _ = child.wait(); + self.finished.store(true, Ordering::Release); + if let Some(reader) = self.stdout.take() { + let _ = reader.join(); + } + if let Some(reader) = self.stderr.take() { + let _ = reader.join(); + } + RunError(error) + } +} + +#[cfg(unix)] +struct WaitOutcome { + status: std::process::ExitStatus, + cancelled: bool, + timed_out: bool, +} + +#[cfg(unix)] +fn wait_for_child( + child: &mut std::process::Child, + process_id: rustix::process::Pid, + cancellation: &CancellationToken, + deadline: Instant, + captures: &mut CaptureThreads, +) -> Result { + let mut cancelled = false; + let mut timed_out = false; + let status = loop { + match child.try_wait() { + Ok(Some(status)) => break status, + Ok(None) => {} + Err(error) => return Err(captures.stop_after_wait_error(child, error)), + } + if cancellation.is_cancelled() { + cancelled = true; + kill_group(process_id, child); + match child.wait() { + Ok(status) => break status, + Err(error) => return Err(captures.stop_after_wait_error(child, error)), + } + } + if Instant::now() >= deadline { + timed_out = true; + kill_group(process_id, child); + match child.wait() { + Ok(status) => break status, + Err(error) => return Err(captures.stop_after_wait_error(child, error)), + } + } + thread::sleep(POLL_INTERVAL); + }; + Ok(WaitOutcome { + status, + cancelled, + timed_out, + }) +} + +#[cfg(unix)] +fn clean_environment(options: &Options) -> io::Result<(BTreeMap, OsString)> { + let environment_root = options.output_dir.join("environment"); + let environment_dir = rover_store::files::prepare_private_directory(&environment_root)?; + let mut environment = BTreeMap::::new(); + let host_path = std::env::var_os("PATH").unwrap_or_default(); + environment.insert("PATH".into(), host_path.clone()); + for (name, path) in [ + ("HOME", environment_root.join("home")), + ("TMPDIR", environment_root.join("tmp")), + ("GOCACHE", environment_root.join("cache")), + ("GOPATH", environment_root.join("gopath")), + ] { + environment_dir + .ensure_subdirectory(name.to_ascii_lowercase().as_str())? + .set_permissions(0o700)?; + environment.insert(name.into(), path.into_os_string()); + } + for (name, value) in [ + ("GOTOOLCHAIN", "local"), + ("GOPROXY", "off"), + ("LANG", "C.UTF-8"), + ("LC_ALL", "C.UTF-8"), + ("GIT_CONFIG_NOSYSTEM", "1"), + ("GIT_CONFIG_GLOBAL", "/dev/null"), + ("GIT_TERMINAL_PROMPT", "0"), + ] { + environment.insert(name.into(), value.into()); + } + for name in &options.pass_env { + if let Some(value) = std::env::var_os(name) { + environment.insert(name.into(), value); + } + } + Ok((environment, host_path)) +} + +/// Run is not yet supported on non-Unix targets because safe process-tree +/// cancellation and descriptor-rooted private capture are not implemented. +#[cfg(not(unix))] +pub fn run( + _options: &Options, + _cancellation: &CancellationToken, +) -> Result { + Err(RunError(io::Error::new( + io::ErrorKind::Unsupported, + "process execution is not supported on this target", + ))) +} + +#[cfg(unix)] +fn validate(options: &Options) -> io::Result<()> { + if options.argv.is_empty() || options.argv.len() > 129 || options.argv[0].trim().is_empty() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "argv must contain an executable and at most 128 arguments", + )); + } + if options + .argv + .iter() + .any(|argument| argument.contains('\0') || argument.len() > 65_536) + { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "argv contains NUL or oversized argument", + )); + } + if options.timeout.is_zero() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "positive timeout required", + )); + } + for name in &options.pass_env { + if !valid_env_name(name) { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("invalid environment variable name {name:?}"), + )); + } + } + Ok(()) +} + +#[cfg(unix)] +fn valid_env_name(name: &str) -> bool { + let mut bytes = name.bytes(); + matches!(bytes.next(), Some(b'A'..=b'Z' | b'a'..=b'z' | b'_')) + && bytes.all(|byte| byte.is_ascii_alphanumeric() || byte == b'_') +} + +#[cfg(unix)] +fn read_capture( + mut reader: R, + finished: &AtomicBool, + log: File, +) -> io::Result { + use rustix::fs::{fcntl_getfl, fcntl_setfl, OFlags}; + let flags = fcntl_getfl(&reader)?; + fcntl_setfl(&reader, flags | OFlags::NONBLOCK)?; + let mut capture = Capture::new(log); + let mut buffer = [0_u8; 16 * 1024]; + let mut drain_until = None; + loop { + match reader.read(&mut buffer) { + Ok(0) => return Ok(capture), + Ok(count) => capture.append(&buffer[..count])?, + Err(error) if error.kind() == io::ErrorKind::Interrupted => {} + Err(error) if error.kind() == io::ErrorKind::WouldBlock => { + if finished.load(Ordering::Acquire) { + let limit = + drain_until.get_or_insert_with(|| Instant::now() + PIPE_DRAIN_GRACE); + if Instant::now() >= *limit { + return Ok(capture); + } + } + thread::sleep(POLL_INTERVAL); + } + Err(error) => return Err(error), + } + } +} + +#[cfg(unix)] +fn kill_group(pid: rustix::process::Pid, child: &mut std::process::Child) { + let _ = rustix::process::kill_process_group(pid, rustix::process::Signal::KILL); + let _ = child.kill(); +} + +#[cfg(unix)] +fn set_exit_status(process: &mut ProcessResult, status: ExitStatus) { + process.exit_code = status.code().unwrap_or(-1); + if !status.success() { + process.error = Some(match status.code() { + Some(code) => format!("exit status {code}"), + None => format!( + "signal: {}", + unix_signal_name(status.signal().unwrap_or_default()) + ), + }); + } +} + +#[cfg(unix)] +fn unix_signal_name(signal: i32) -> String { + #[cfg(target_os = "macos")] + let name = match signal { + 1 => Some("hangup"), + 2 => Some("interrupt"), + 3 => Some("quit"), + 4 => Some("illegal instruction"), + 5 => Some("trace/BPT trap"), + 6 => Some("abort trap"), + 7 => Some("EMT trap"), + 8 => Some("floating point exception"), + 9 => Some("killed"), + 10 => Some("bus error"), + 11 => Some("segmentation fault"), + 12 => Some("bad system call"), + 13 => Some("broken pipe"), + 14 => Some("alarm clock"), + 15 => Some("terminated"), + 16 => Some("urgent I/O condition"), + 17 => Some("suspended (signal)"), + 18 => Some("suspended"), + 19 => Some("continued"), + 20 => Some("child exited"), + 21 => Some("stopped (tty input)"), + 22 => Some("stopped (tty output)"), + 23 => Some("I/O possible"), + 24 => Some("cputime limit exceeded"), + 25 => Some("filesize limit exceeded"), + 26 => Some("virtual timer expired"), + 27 => Some("profiling timer expired"), + 28 => Some("window size changes"), + _ => None, + }; + #[cfg(target_os = "linux")] + let name = match signal { + 1 => Some("hangup"), + 2 => Some("interrupt"), + 3 => Some("quit"), + 4 => Some("illegal instruction"), + 5 => Some("trace/breakpoint trap"), + 6 => Some("aborted"), + 7 => Some("bus error"), + 8 => Some("floating point exception"), + 9 => Some("killed"), + 10 => Some("user defined signal 1"), + 11 => Some("segmentation fault"), + 12 => Some("user defined signal 2"), + 13 => Some("broken pipe"), + 14 => Some("alarm clock"), + 15 => Some("terminated"), + 16 => Some("stack fault"), + 17 => Some("child exited"), + 18 => Some("continued"), + 19 => Some("stopped (signal)"), + 20 => Some("stopped"), + 21 => Some("stopped (tty input)"), + 22 => Some("stopped (tty output)"), + 23 => Some("urgent I/O condition"), + 24 => Some("CPU time limit exceeded"), + 25 => Some("file size limit exceeded"), + 26 => Some("virtual timer expired"), + 27 => Some("profiling timer expired"), + 28 => Some("window changed"), + 29 => Some("I/O possible"), + 30 => Some("power failure"), + 31 => Some("bad system call"), + _ => None, + }; + #[cfg(not(any(target_os = "macos", target_os = "linux")))] + let name: Option<&str> = None; + + name.map_or_else(|| format!("signal {signal}"), str::to_owned) +} + +#[cfg(unix)] +fn finish_record( + mut process: ProcessResult, + stdout: Capture, + stderr: Capture, + executable: PathBuf, + executable_sha256: Option, +) -> Result { + let stdout = stdout.complete()?; + let stderr = stderr.complete()?; + process.truncated = stdout.truncated || stderr.truncated; + process.stdout_sha256 = Sha256Digest::of(&stdout.bytes).to_hex(); + process.stderr_sha256 = Sha256Digest::of(&stderr.bytes).to_hex(); + process.stdout_bytes = stdout.total; + process.stderr_bytes = stderr.total; + Ok(ResultRecord { + process, + stdout: stdout.bytes, + stderr: stderr.bytes, + executable, + executable_sha256, + }) +} + +#[cfg(unix)] +fn resolve_executable(program: &str, dir: &Path, path: &std::ffi::OsStr) -> (PathBuf, bool) { + let program_path = Path::new(program); + if program_path.is_absolute() { + return (program_path.to_path_buf(), false); + } + if program.contains(std::path::MAIN_SEPARATOR) { + let working_directory = if dir.is_absolute() { + dir.to_path_buf() + } else { + std::env::current_dir() + .unwrap_or_else(|_| PathBuf::from(".")) + .join(dir) + }; + return (working_directory.join(program_path), false); + } + let current_directory = std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); + for directory in std::env::split_paths(path) { + let candidate = if directory.as_os_str().is_empty() { + current_directory.join(program) + } else if directory.is_absolute() { + directory.join(program) + } else { + current_directory.join(directory).join(program) + }; + if candidate.is_file() && is_executable(&candidate) { + return (candidate, false); + } + } + (program_path.to_path_buf(), true) +} + +#[cfg(unix)] +fn is_executable(path: &Path) -> bool { + use std::os::unix::fs::PermissionsExt; + fs::metadata(path).is_ok_and(|metadata| metadata.permissions().mode() & 0o111 != 0) +} + +#[cfg(unix)] +fn hash_executable(path: &Path) -> Option { + hash_executable_prefix(path, EXECUTABLE_HASH_LIMIT) +} + +#[cfg(unix)] +fn hash_executable_prefix(path: &Path, limit: u64) -> Option { + let metadata = fs::metadata(path).ok()?; + let mut bytes = Vec::with_capacity(usize::try_from(metadata.len().min(limit)).ok()?); + File::open(path) + .ok()? + .take(limit) + .read_to_end(&mut bytes) + .ok()?; + Some(Sha256Digest::of(&bytes).to_hex()) +} + +#[cfg(all(test, unix))] +mod tests { + use super::{ + hash_executable_prefix, resolve_executable, run, validate, CancellationToken, Options, + OUTPUT_LIMIT, + }; + use std::ffi::OsStr; + use std::fs; + use std::os::unix::fs::PermissionsExt; + use std::path::PathBuf; + use std::time::Duration; + + struct Fixture { + root: PathBuf, + work: PathBuf, + output: PathBuf, + } + + impl Fixture { + fn new() -> Self { + let root = std::env::temp_dir().join(format!( + "rover-execution-test-{}-{}", + std::process::id(), + rover_core::Id::generate("test").unwrap() + )); + fs::create_dir(&root).unwrap(); + fs::set_permissions(&root, fs::Permissions::from_mode(0o700)).unwrap(); + let work = root.join("work"); + fs::create_dir(&work).unwrap(); + Self { + output: root.join("output"), + root, + work, + } + } + + fn options(&self, script: &str) -> Options { + Options { + dir: self.work.clone(), + output_dir: self.output.clone(), + argv: vec!["/bin/sh".into(), "-c".into(), script.into()], + timeout: Duration::from_secs(3), + pass_env: Vec::new(), + } + } + } + + impl Drop for Fixture { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.root); + } + } + + #[test] + fn captures_process_result_and_persists_private_logs() { + let fixture = Fixture::new(); + let result = run( + &fixture.options("printf hello; printf error >&2; exit 7"), + &CancellationToken::default(), + ) + .unwrap(); + assert_eq!(result.process.exit_code, 7); + assert_eq!(result.process.error.as_deref(), Some("exit status 7")); + assert_eq!(result.stdout, b"hello"); + assert_eq!(result.stderr, b"error"); + assert_eq!(result.process.stdout_bytes, 5); + assert_eq!(result.process.stderr_bytes, 5); + assert_eq!( + result.process.stdout_sha256, + rover_core::Sha256Digest::of(b"hello").to_hex() + ); + assert_eq!( + fs::read(fixture.output.join("stdout.log")).unwrap(), + b"hello" + ); + assert_eq!( + fs::metadata(fixture.output.join("stdout.log")) + .unwrap() + .permissions() + .mode() + & 0o777, + 0o600 + ); + assert!(result.executable_sha256.is_some()); + } + + #[test] + fn maps_signal_termination_to_go_compatible_negative_one_exit_code() { + let fixture = Fixture::new(); + let result = run( + &fixture.options("kill -TERM $$"), + &CancellationToken::default(), + ) + .unwrap(); + assert_eq!(result.process.exit_code, -1); + assert_eq!(result.process.error.as_deref(), Some("signal: terminated")); + } + + #[test] + fn clears_environment_and_inherits_only_named_values() { + let fixture = Fixture::new(); + std::env::set_var("ROVER_EXECUTION_TEST_SECRET", "allowed"); + let mut options = fixture.options("test -z \"$ROVER_EXECUTION_TEST_SECRET\""); + assert_eq!( + run(&options, &CancellationToken::default()) + .unwrap() + .process + .exit_code, + 0 + ); + options.pass_env.push("ROVER_EXECUTION_TEST_SECRET".into()); + options.argv[2] = "test \"$ROVER_EXECUTION_TEST_SECRET\" = allowed".into(); + options.output_dir = fixture.root.join("output-pass-env"); + assert_eq!( + run(&options, &CancellationToken::default()) + .unwrap() + .process + .exit_code, + 0 + ); + std::env::remove_var("ROVER_EXECUTION_TEST_SECRET"); + } + + #[test] + fn missing_executable_is_recorded_and_logs_are_created() { + let fixture = Fixture::new(); + let mut options = fixture.options("ignored"); + options.argv = vec!["rover-does-not-exist".into()]; + let result = run(&options, &CancellationToken::default()).unwrap(); + assert_eq!(result.process.exit_code, -1); + assert_eq!( + result.process.error.as_deref(), + Some("exec: \"rover-does-not-exist\": executable file not found in $PATH") + ); + assert!(fixture.output.join("stdout.log").is_file()); + } + + #[test] + fn executable_hash_matches_go_limited_prefix_behavior() { + let fixture = Fixture::new(); + let file = fixture.root.join("large-executable"); + fs::write(&file, b"abcdefgh").unwrap(); + assert_eq!( + hash_executable_prefix(&file, 4), + Some(rover_core::Sha256Digest::of(b"abcd").to_hex()) + ); + } + + #[test] + fn pre_cancelled_run_records_cancellation_without_starting_the_command() { + let fixture = Fixture::new(); + let cancellation = CancellationToken::default(); + cancellation.cancel(); + let result = run(&fixture.options("touch should-not-exist"), &cancellation).unwrap(); + assert!(result.process.cancelled); + assert_eq!(result.process.exit_code, -1); + assert_eq!(result.process.error.as_deref(), Some("context canceled")); + assert!(!fixture.work.join("should-not-exist").exists()); + assert_eq!(fs::read(fixture.output.join("stdout.log")).unwrap(), b""); + } + + #[test] + fn expired_deadline_during_environment_setup_prevents_process_start() { + let fixture = Fixture::new(); + let mut options = fixture.options("touch should-not-exist"); + options.timeout = Duration::from_nanos(1); + let result = run(&options, &CancellationToken::default()).unwrap(); + assert!(result.process.timed_out); + assert_eq!( + result.process.error.as_deref(), + Some("context deadline exceeded") + ); + assert!(!fixture.work.join("should-not-exist").exists()); + } + + #[test] + fn existing_capture_log_is_never_replaced_or_executed_over() { + let fixture = Fixture::new(); + fs::create_dir(&fixture.output).unwrap(); + fs::set_permissions(&fixture.output, fs::Permissions::from_mode(0o700)).unwrap(); + fs::write(fixture.output.join("stdout.log"), b"preserve").unwrap(); + let result = run( + &fixture.options("touch should-not-exist"), + &CancellationToken::default(), + ); + assert!(result.is_err()); + assert_eq!( + fs::read(fixture.output.join("stdout.log")).unwrap(), + b"preserve" + ); + assert!(!fixture.work.join("should-not-exist").exists()); + } + + #[test] + fn output_capture_is_capped_but_counts_all_bytes() { + let fixture = Fixture::new(); + let result = run( + &fixture.options("head -c 1200000 /dev/zero; head -c 1300000 /dev/zero >&2"), + &CancellationToken::default(), + ) + .unwrap(); + assert!(result.process.truncated); + assert_eq!(result.stdout.len(), OUTPUT_LIMIT); + assert_eq!(result.process.stdout_bytes, 1_200_000); + assert_eq!(result.stderr.len(), OUTPUT_LIMIT); + assert_eq!(result.process.stderr_bytes, 1_300_000); + } + + #[test] + fn output_logs_are_written_while_the_process_runs() { + let fixture = Fixture::new(); + let mut options = fixture.options("printf live; sleep 0.3; printf done"); + options.timeout = Duration::from_secs(2); + let worker = + std::thread::spawn(move || run(&options, &CancellationToken::default()).unwrap()); + let log = fixture.output.join("stdout.log"); + let deadline = std::time::Instant::now() + Duration::from_secs(2); + let mut observed_live_output = false; + while std::time::Instant::now() < deadline { + if fs::read(&log).is_ok_and(|bytes| bytes == b"live") { + observed_live_output = true; + break; + } + std::thread::sleep(Duration::from_millis(5)); + } + let result = worker.join().unwrap(); + assert!( + observed_live_output, + "stdout log should update before process completion" + ); + assert_eq!(result.stdout, b"livedone"); + } + + #[test] + fn exact_output_limit_is_not_marked_truncated() { + let fixture = Fixture::new(); + let result = run( + &fixture.options("head -c 1048576 /dev/zero"), + &CancellationToken::default(), + ) + .unwrap(); + assert!(!result.process.truncated); + assert_eq!(result.stdout.len(), OUTPUT_LIMIT); + } + + #[test] + fn timeout_and_cancellation_terminate_the_process_group() { + let fixture = Fixture::new(); + let mut options = fixture.options("(sleep 0.3; echo leaked > descendant.txt) & wait"); + options.timeout = Duration::from_millis(80); + let result = run(&options, &CancellationToken::default()).unwrap(); + assert!(result.process.timed_out); + assert_eq!(result.process.error.as_deref(), Some("signal: killed")); + std::thread::sleep(Duration::from_millis(450)); + assert!(!fixture.work.join("descendant.txt").exists()); + let cancellation = CancellationToken::default(); + let request = cancellation.clone(); + let mut worker_options = fixture.options("sleep 20 & wait"); + worker_options.output_dir = fixture.root.join("output-cancel"); + let worker = std::thread::spawn(move || run(&worker_options, &request).unwrap()); + std::thread::sleep(Duration::from_millis(80)); + cancellation.cancel(); + let cancelled = worker.join().unwrap().process; + assert!(cancelled.cancelled); + assert_eq!(cancelled.error.as_deref(), Some("signal: killed")); + } + + #[test] + fn rejects_invalid_argv_timeout_and_environment_names() { + let fixture = Fixture::new(); + let mut options = fixture.options("true"); + options.argv[0].clear(); + assert!(run(&options, &CancellationToken::default()).is_err()); + options = fixture.options("true"); + options.timeout = Duration::ZERO; + assert!(run(&options, &CancellationToken::default()).is_err()); + options = fixture.options("true"); + options.pass_env.push("bad-name".into()); + assert!(run(&options, &CancellationToken::default()).is_err()); + + options = fixture.options("true"); + options.argv = vec!["true".into(); 129]; + assert!( + validate(&options).is_ok(), + "128 arguments after argv[0] are allowed" + ); + options.argv.push("extra".into()); + assert!(validate(&options).is_err()); + options.argv = vec!["true".into(), "bad\0arg".into()]; + assert!(validate(&options).is_err()); + options.argv = vec!["true".into(), "x".repeat(65_537)]; + assert!(validate(&options).is_err()); + } + + #[test] + fn executable_lookup_uses_the_parent_path_and_preserves_explicit_paths() { + let fixture = Fixture::new(); + let path_dir = fixture.root.join("bin"); + fs::create_dir(&path_dir).unwrap(); + let candidate = path_dir.join("fixture-tool"); + fs::write(&candidate, b"#!/bin/sh\nexit 0\n").unwrap(); + fs::set_permissions(&candidate, fs::Permissions::from_mode(0o700)).unwrap(); + let (resolved, missing) = resolve_executable( + "fixture-tool", + &fixture.work, + OsStr::new(path_dir.to_str().unwrap()), + ); + assert!(!missing); + assert_eq!(resolved, candidate); + let (explicit, missing) = + resolve_executable("./fixture-tool", &fixture.work, OsStr::new("")); + assert!(!missing); + assert_eq!(explicit, fixture.work.join("fixture-tool")); + } +} diff --git a/crates/rover-execution/src/pty.rs b/crates/rover-execution/src/pty.rs new file mode 100644 index 0000000..b8e1d75 --- /dev/null +++ b/crates/rover-execution/src/pty.rs @@ -0,0 +1,834 @@ +//! Local pseudo-terminal sessions on Unix and Windows `ConPTY`. +//! +//! This module owns one child process and a native PTY pair. It does not +//! implement scrollback, terminal emulation, persistence, or client attach. + +use std::collections::BTreeMap; +use std::io::{self, Read, Write}; +use std::path::PathBuf; +#[cfg(target_os = "macos")] +use std::process::{Command, Stdio}; + +use portable_pty::{native_pty_system, Child, CommandBuilder, ExitStatus, MasterPty, PtySize}; +use serde::{Deserialize, Serialize}; + +const MAX_ARGV: usize = 129; +const MAX_ARGUMENT_BYTES: usize = 65_536; +const MAX_DIMENSION: u16 = 1000; +#[cfg(target_os = "linux")] +const MAX_PROC_SCAN: usize = 4096; +#[cfg(target_os = "macos")] +const MAX_PROCESS_LIST_BYTES: usize = 262_144; +#[cfg(any(target_os = "linux", target_os = "macos"))] +const MAX_FOREGROUND_GROUP_MEMBERS: usize = 64; +#[cfg(target_os = "linux")] +const MAX_PROCESS_ARG_BYTES: usize = 4096; +#[cfg(any(target_os = "linux", target_os = "macos"))] +const MAX_GROUP_ARG_BYTES: usize = 8192; +#[cfg(target_os = "linux")] +const MAX_PROCESS_ARG_COUNT: usize = 64; + +/// Bounded identity and argv evidence for one process in an owned PTY group. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ForegroundProcessDetails { + /// Validated executable basename from the platform process table. + pub name: String, + /// Process argument vector, when available and within the sampler bounds. + pub argv: Option>, +} +#[cfg(target_os = "macos")] +const MAX_PROCESS_NAME_BYTES: usize = 256; + +/// Process, environment, working directory, and initial terminal dimensions. +#[derive(Clone, Debug)] +pub struct PtyOptions { + /// Absolute executable and arguments. Rover does not insert a shell. + pub argv: Vec, + /// Existing absolute directory used as the child's working directory. + pub cwd: PathBuf, + /// Complete child environment. No parent variables are inherited. + pub env: BTreeMap, + /// Initial terminal rows. + pub rows: u16, + /// Initial terminal columns. + pub cols: u16, +} + +/// One owned native PTY session and its directly spawned child. +pub struct PtySession { + master: Option>, + reader: Option>, + writer: Option>, + child: Option>, +} + +impl PtySession { + /// Create a native PTY and spawn the exact argv with a cleared environment. + /// + /// # Errors + /// + /// Returns `InvalidInput` for invalid argv, environment, directory, or + /// dimensions, and an I/O error when the native PTY or process cannot be + /// created. + pub fn spawn(options: &PtyOptions) -> io::Result { + validate_options(options)?; + + let system = native_pty_system(); + let pair = system + .openpty(PtySize { + rows: options.rows, + cols: options.cols, + pixel_width: 0, + pixel_height: 0, + }) + .map_err(backend_error)?; + + let reader = pair.master.try_clone_reader().map_err(backend_error)?; + let writer = pair.master.take_writer().map_err(backend_error)?; + let mut command = CommandBuilder::new(&options.argv[0]); + command.args(options.argv[1..].iter()); + command.env_clear(); + for (name, value) in &options.env { + command.env(name, value); + } + command.cwd(&options.cwd); + let child = pair.slave.spawn_command(command).map_err(backend_error)?; + drop(pair.slave); + + Ok(Self { + master: Some(pair.master), + reader: Some(reader), + writer: Some(writer), + child: Some(child), + }) + } + + /// Read bytes from the PTY child output. + /// + /// This is a blocking read and returns the native PTY's EOF/error when + /// the child or session closes. + /// + /// # Errors + /// + /// Returns an I/O error if the session is closed or reading fails. + pub fn read_output(&mut self, buffer: &mut [u8]) -> io::Result { + self.reader + .as_mut() + .ok_or_else(session_closed)? + .read(buffer) + } + + /// Move the PTY output reader to a dedicated broker thread. + /// + /// This is used by the session server so blocking output reads do not + /// prevent client input, resizing, or process shutdown. A reader can be + /// taken only once; subsequent reads through this session report closed. + /// + /// # Errors + /// + /// Returns `BrokenPipe` if the reader was already moved or the session was + /// closed. + pub fn take_output_reader(&mut self) -> io::Result> { + self.reader.take().ok_or_else(session_closed) + } + + /// Write input bytes to the PTY child. + /// + /// # Errors + /// + /// Returns an I/O error if the session is closed or writing fails. + pub fn write_input(&mut self, bytes: &[u8]) -> io::Result<()> { + self.writer + .as_mut() + .ok_or_else(session_closed)? + .write_all(bytes) + } + + /// Change the terminal size and notify the attached child. + /// + /// # Errors + /// + /// Returns `InvalidInput` for zero or oversized dimensions, or an I/O + /// error if the native terminal resize fails. + pub fn resize(&mut self, rows: u16, cols: u16) -> io::Result<()> { + validate_dimensions(rows, cols)?; + self.master + .as_ref() + .ok_or_else(session_closed)? + .resize(PtySize { + rows, + cols, + pixel_width: 0, + pixel_height: 0, + }) + .map_err(backend_error) + } + + /// Return the directly spawned child PID where the platform exposes it. + #[must_use] + pub fn child_id(&self) -> Option { + self.child.as_ref().and_then(|child| child.process_id()) + } + + /// Sample the executable name of the PTY's current foreground process-group leader. + /// + /// A command pipeline reports its group leader only; Rover does not search + /// child processes. A missing process or unsupported platform returns + /// `None` so callers can retain an unknown state. + #[cfg(unix)] + #[must_use] + pub fn foreground_process_name(&self) -> Option { + let pid = self.master.as_ref()?.process_group_leader()?; + foreground_process_name(pid) + } + + /// Sample executable basenames in the PTY's foreground process group. + /// + /// Sampling is bounded and platform owned. Unsupported systems and + /// incomplete or unstable scans return `None` rather than a partial list. + #[cfg(unix)] + #[must_use] + pub fn foreground_process_group_names(&self) -> Option> { + let pgid = self.master.as_ref()?.process_group_leader()?; + let details = Self::foreground_process_group_details_for(pgid)?; + let mut names = details + .into_iter() + .map(|process| process.name) + .collect::>(); + names.sort(); + names.dedup(); + Some(names) + } + + /// Sample process names and bounded argv vectors from the PTY foreground + /// process group. The list is never accepted from a client. + #[cfg(unix)] + #[must_use] + pub fn foreground_process_group_details(&self) -> Option> { + let pgid = self.master.as_ref()?.process_group_leader()?; + Self::foreground_process_group_details_for(pgid) + } + + #[cfg(unix)] + fn foreground_process_group_details_for(pgid: i32) -> Option> { + #[cfg(target_os = "linux")] + { + linux_foreground_process_group_details(pgid) + } + #[cfg(target_os = "macos")] + { + macos_foreground_process_group_details(pgid) + } + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + { + let _ = pgid; + None + } + } + + /// Check whether the direct child has exited without blocking. + /// + /// # Errors + /// + /// Returns `BrokenPipe` if the child handle is no longer owned, or an I/O + /// error if the platform backend cannot inspect it. + pub fn try_wait(&mut self) -> io::Result> { + self.child + .as_mut() + .ok_or_else(session_closed)? + .try_wait() + .map_err(backend_error) + } + + /// Stop and reap the child, then close all PTY handles. Repeated calls are + /// harmless. + /// + /// # Errors + /// + /// Returns an I/O error if querying, stopping, or reaping the child fails. + pub fn close(&mut self) -> io::Result<()> { + let result = if let Some(child) = self.child.as_mut() { + if child.try_wait()?.is_some() { + Ok(()) + } else { + child.kill()?; + child.wait().map(|_| ()) + } + } else { + Ok(()) + }; + self.child.take(); + self.writer.take(); + self.reader.take(); + self.master.take(); + result + } +} + +#[cfg(target_os = "linux")] +fn linux_foreground_process_group_details(pgid: i32) -> Option> { + if pgid <= 0 { + return None; + } + let entries = std::fs::read_dir("/proc").ok()?; + let mut processes = Vec::new(); + let mut argv_bytes = 0usize; + let skip_argv = + std::env::var_os("WSL_DISTRO_NAME").is_some() || std::env::var_os("WSL_INTEROP").is_some(); + let mut scanned = 0; + for entry in entries { + let entry = entry.ok()?; + let Some(pid) = entry + .file_name() + .to_str() + .and_then(|name| name.parse::().ok()) + else { + continue; + }; + scanned += 1; + if scanned > MAX_PROC_SCAN { + return None; + } + let Some((group, start_time)) = linux_process_group_identity(pid) else { + continue; + }; + if group != pgid { + continue; + } + let identity = crate::supervisor::process_identity(pid)?; + if identity.rsplit_once(':').map(|(_, start)| start) != Some(start_time.as_str()) { + return None; + } + let executable = std::fs::read_link(format!("/proc/{pid}/exe")).ok()?; + if crate::supervisor::process_identity(pid).as_deref() != Some(identity.as_str()) + || linux_process_group_identity(pid).map(|sample| sample.0) != Some(pgid) + { + return None; + } + let Some(name) = executable.file_name().and_then(|name| name.to_str()) else { + return None; + }; + let name = valid_executable_name(name)?; + if processes.len() == MAX_FOREGROUND_GROUP_MEMBERS { + return None; + } + let argv = if skip_argv { + None + } else { + linux_process_argv(pid) + }; + let argv = argv.filter(|argv| { + let bytes = argv.iter().map(String::len).sum::(); + if argv_bytes.saturating_add(bytes) > MAX_GROUP_ARG_BYTES { + false + } else { + argv_bytes += bytes; + true + } + }); + if crate::supervisor::process_identity(pid).as_deref() != Some(identity.as_str()) + || linux_process_group_identity(pid).map(|sample| sample.0) != Some(pgid) + { + return None; + } + processes.push(ForegroundProcessDetails { name, argv }); + } + processes.sort_by(|left, right| left.name.cmp(&right.name)); + Some(processes) +} + +#[cfg(target_os = "linux")] +fn linux_process_argv(pid: i32) -> Option> { + let file = std::fs::File::open(format!("/proc/{pid}/cmdline")).ok()?; + let mut bytes = Vec::new(); + file.take((MAX_PROCESS_ARG_BYTES + 1) as u64) + .read_to_end(&mut bytes) + .ok()?; + parse_nul_terminated_argv(&bytes) +} + +// Only the Linux /proc reader produces a real argv vector; the macOS backend +// deliberately reports unknown argv rather than parse a lossy `ps` command +// line, so this helper and its bounds are Linux-only. +#[cfg(target_os = "linux")] +fn parse_nul_terminated_argv(bytes: &[u8]) -> Option> { + if bytes.is_empty() || bytes.len() > MAX_PROCESS_ARG_BYTES { + return None; + } + let mut argv = bytes + .split(|byte| *byte == 0) + .filter(|argument| !argument.is_empty()) + .map(|argument| { + let value = std::str::from_utf8(argument).ok()?; + (!value.chars().any(char::is_control)).then(|| value.to_owned()) + }) + .collect::>>()?; + if argv.is_empty() || argv.len() > MAX_PROCESS_ARG_COUNT { + return None; + } + Some(std::mem::take(&mut argv)) +} + +#[cfg(target_os = "linux")] +fn linux_process_group_identity(pid: i32) -> Option<(i32, String)> { + let stat = std::fs::read_to_string(format!("/proc/{pid}/stat")).ok()?; + let fields = stat.get(stat.rfind(')')?.checked_add(1)?..)?; + let fields = fields.split_ascii_whitespace().collect::>(); + // The first field after the command is state (field 3); pgrp is field 5 + // and starttime is field 22 in procfs' stat format. + Some((fields.get(2)?.parse().ok()?, fields.get(19)?.to_string())) +} + +#[cfg(target_os = "macos")] +fn macos_foreground_process_group_details(pgid: i32) -> Option> { + if pgid <= 0 { + return None; + } + let mut child = Command::new("/bin/ps") + .args(["-A", "-o", "pid=", "-o", "pgid=", "-o", "comm="]) + .env_clear() + .env("LC_ALL", "C") + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .spawn() + .ok()?; + let stdout = child.stdout.take()?; + let mut output = Vec::new(); + if stdout + .take((MAX_PROCESS_LIST_BYTES + 1) as u64) + .read_to_end(&mut output) + .is_err() + || output.len() > MAX_PROCESS_LIST_BYTES + { + let _ = child.kill(); + let _ = child.wait(); + return None; + } + if !child.wait().ok()?.success() { + return None; + } + let output = std::str::from_utf8(&output).ok()?; + let mut processes = Vec::new(); + let mut argv_bytes = 0usize; + for line in output.lines() { + let mut fields = line.split_ascii_whitespace(); + let Some(process_id) = fields + .next() + .and_then(|process_id| process_id.parse::().ok()) + else { + continue; + }; + let Some(group) = fields.next().and_then(|group| group.parse::().ok()) else { + continue; + }; + if group != pgid { + continue; + } + let first = macos_process_sample(process_id)?; + if first.1 != pgid { + return None; + } + let name = valid_executable_name(PathBuf::from(&first.2).file_name()?.to_str()?)?; + if processes.len() == MAX_FOREGROUND_GROUP_MEMBERS { + return None; + } + // `ps` output is untrusted input here; a negative pid must be rejected + // rather than wrapped into a valid-looking u32. + let argv = u32::try_from(process_id).ok().and_then(macos_process_argv); + let second = macos_process_sample(process_id)?; + if first != second { + return None; + } + let argv = argv.filter(|argv| { + let bytes = argv.iter().map(String::len).sum::(); + if argv_bytes.saturating_add(bytes) > MAX_GROUP_ARG_BYTES { + false + } else { + argv_bytes += bytes; + true + } + }); + processes.push(ForegroundProcessDetails { name, argv }); + } + processes.sort_by(|left, right| left.name.cmp(&right.name)); + Some(processes) +} + +#[cfg(target_os = "macos")] +fn macos_process_argv(_pid: u32) -> Option> { + // `ps` exposes a display command line, not argv boundaries. The workspace + // forbids unsafe code, so we preserve unknown wrapper identity here rather + // than parse a lossy command string as an argument vector. + None +} + +#[cfg(target_os = "linux")] +fn foreground_process_name(pid: i32) -> Option { + let before = crate::supervisor::process_identity(pid)?; + let executable = std::fs::read_link(format!("/proc/{pid}/exe")).ok()?; + let after = crate::supervisor::process_identity(pid)?; + if before != after { + return None; + } + valid_executable_name(executable.file_name()?.to_str()?) +} + +#[cfg(target_os = "macos")] +fn foreground_process_name(pid: i32) -> Option { + if pid <= 0 { + return None; + } + let first = macos_process_sample(pid)?; + let second = macos_process_sample(pid)?; + if first != second || first.1 != pid { + return None; + } + valid_executable_name(PathBuf::from(first.2).file_name()?.to_str()?) +} + +#[cfg(target_os = "macos")] +fn macos_process_sample(pid: i32) -> Option<(String, i32, String)> { + let mut child = Command::new("/bin/ps") + .args([ + "-p", + &pid.to_string(), + "-o", + "lstart=", + "-o", + "pgid=", + "-o", + "comm=", + ]) + .env_clear() + .env("LC_ALL", "C") + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .spawn() + .ok()?; + let mut output = Vec::new(); + let stdout = child.stdout.take()?; + if stdout + .take((MAX_PROCESS_NAME_BYTES + 1) as u64) + .read_to_end(&mut output) + .is_err() + { + let _ = child.kill(); + let _ = child.wait(); + return None; + } + if output.len() > MAX_PROCESS_NAME_BYTES { + let _ = child.kill(); + let _ = child.wait(); + return None; + } + if !child.wait().ok()?.success() { + return None; + } + let process = std::str::from_utf8(&output).ok()?.trim(); + let fields = process.split_ascii_whitespace().collect::>(); + if fields.len() < 7 { + return None; + } + let start = fields[..5].join(" "); + let group = fields[5].parse().ok()?; + let command = fields[6..].join(" "); + (!start.is_empty() && !command.is_empty()).then_some((start, group, command)) +} + +#[cfg(all(unix, not(any(target_os = "linux", target_os = "macos"))))] +fn foreground_process_name(_pid: i32) -> Option { + None +} + +#[cfg(unix)] +fn valid_executable_name(name: &str) -> Option { + if name.is_empty() + || name.len() > 128 + || !name + .bytes() + .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'-' | b'.' | b'+')) + { + return None; + } + Some(name.to_owned()) +} + +impl Drop for PtySession { + fn drop(&mut self) { + let _ = self.close(); + } +} + +fn validate_options(options: &PtyOptions) -> io::Result<()> { + if options.argv.is_empty() || options.argv.len() > MAX_ARGV { + return Err(invalid_input( + "argv must contain an executable and at most 128 arguments", + )); + } + if options.argv[0].trim().is_empty() + || !PathBuf::from(&options.argv[0]).is_absolute() + || options + .argv + .iter() + .any(|argument| argument.contains('\0') || argument.len() > MAX_ARGUMENT_BYTES) + { + return Err(invalid_input( + "PTY executable must be absolute and argv must not contain NUL or oversized arguments", + )); + } + validate_dimensions(options.rows, options.cols)?; + if !options.cwd.is_absolute() || !options.cwd.is_dir() { + return Err(invalid_input( + "PTY working directory must be an existing absolute directory", + )); + } + for (name, value) in &options.env { + if !valid_env_name(name) || value.contains('\0') { + return Err(invalid_input( + "PTY environment contains an invalid name or value", + )); + } + } + Ok(()) +} + +fn validate_dimensions(rows: u16, cols: u16) -> io::Result<()> { + if rows == 0 || cols == 0 || rows > MAX_DIMENSION || cols > MAX_DIMENSION { + return Err(invalid_input("PTY dimensions must be between 1 and 1000")); + } + Ok(()) +} + +fn valid_env_name(name: &str) -> bool { + let mut bytes = name.bytes(); + matches!(bytes.next(), Some(b'A'..=b'Z' | b'a'..=b'z' | b'_')) + && bytes.all(|byte| byte.is_ascii_alphanumeric() || byte == b'_') +} + +fn invalid_input(message: &str) -> io::Error { + io::Error::new(io::ErrorKind::InvalidInput, message) +} + +fn session_closed() -> io::Error { + io::Error::new(io::ErrorKind::BrokenPipe, "PTY session is closed") +} + +fn backend_error(error: impl std::fmt::Display) -> io::Error { + io::Error::other(error.to_string()) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn options(argv: &[&str]) -> PtyOptions { + PtyOptions { + argv: argv.iter().map(|value| (*value).to_owned()).collect(), + cwd: std::env::current_dir().expect("current directory"), + env: BTreeMap::new(), + rows: 24, + cols: 80, + } + } + + #[cfg(target_os = "linux")] + #[test] + fn argv_parser_preserves_argument_boundaries_and_fails_closed_on_controls() { + assert_eq!( + parse_nul_terminated_argv( + b"/usr/bin/node\0node_modules/@letta-ai/letta-code/letta\0--backend\0local\0" + ), + Some(vec![ + "/usr/bin/node".to_owned(), + "node_modules/@letta-ai/letta-code/letta".to_owned(), + "--backend".to_owned(), + "local".to_owned(), + ]) + ); + assert_eq!(parse_nul_terminated_argv(b"node\0bad\x1bvalue\0"), None); + assert_eq!( + parse_nul_terminated_argv(&vec![b'x'; MAX_PROCESS_ARG_BYTES + 1]), + None + ); + } + + #[cfg(unix)] + #[test] + fn pty_round_trip_resize_and_close() { + let options = options(&["/usr/bin/printf", "rover-pty-ok"]); + let mut session = PtySession::spawn(&options).expect("spawn PTY child"); + assert!(session.child_id().is_some()); + session.resize(35, 110).expect("resize PTY"); + let size = session + .master + .as_ref() + .expect("open master") + .get_size() + .expect("read PTY size"); + assert_eq!((size.rows, size.cols), (35, 110)); + + let mut output = [0; 64]; + let count = session.read_output(&mut output).expect("read child output"); + assert_eq!(&output[..count], b"rover-pty-ok"); + session.close().expect("close and reap child"); + assert!(session.write_input(b"after-close").is_err()); + } + + #[cfg(unix)] + #[test] + fn closing_a_live_pty_stops_and_reaps_its_child() { + let options = options(&["/bin/sleep", "30"]); + let mut session = PtySession::spawn(&options).expect("spawn long-running PTY child"); + session.close().expect("close running PTY session"); + assert!(session.child_id().is_none()); + } + + #[cfg(unix)] + #[test] + fn foreground_process_sampler_reads_the_owned_pty_job() { + let options = options(&["/bin/sleep", "5"]); + let mut session = PtySession::spawn(&options).expect("spawn PTY process"); + let deadline = std::time::Instant::now() + std::time::Duration::from_secs(1); + let sample = loop { + if let Some(name) = session.foreground_process_name() { + break Some(name); + } + if std::time::Instant::now() >= deadline { + break None; + } + std::thread::sleep(std::time::Duration::from_millis(10)); + }; + #[cfg(target_os = "linux")] + assert_eq!(sample.as_deref(), Some("sleep")); + #[cfg(target_os = "macos")] + assert_eq!(sample.as_deref(), Some("sleep")); + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + assert!(sample.is_none()); + session.close().expect("reap sampled PTY process"); + } + + #[cfg(any(target_os = "linux", target_os = "macos"))] + #[test] + fn foreground_process_group_sampler_includes_owned_group_members() { + let options = options(&["/bin/sh", "-c", "sleep 5 & wait"]); + let mut session = PtySession::spawn(&options).expect("spawn PTY process group"); + let deadline = std::time::Instant::now() + std::time::Duration::from_secs(1); + let sample = loop { + if let Some(names) = session.foreground_process_group_names() { + if names.iter().any(|name| name == "sleep") { + break Some(names); + } + } + if std::time::Instant::now() >= deadline { + break None; + } + std::thread::sleep(std::time::Duration::from_millis(10)); + }; + let names = sample.expect("foreground sampler includes the child process"); + assert!(names.contains(&"sleep".to_owned())); + let details = session + .foreground_process_group_details() + .expect("bounded process details are sampled"); + #[cfg(target_os = "linux")] + assert!(details.iter().any(|process| { + process.name == "sleep" + && process + .argv + .as_ref() + .is_some_and(|argv| argv.iter().any(|argument| argument == "5")) + })); + #[cfg(target_os = "macos")] + assert!(details.iter().any(|process| process.name == "sleep")); + session.close().expect("reap sampled PTY process group"); + } + + #[cfg(unix)] + #[test] + fn pty_writes_input_to_the_child() { + let options = options(&[ + "/bin/sh", + "-c", + "IFS= read -r line; printf 'CHILD:%s' \"$line\"", + ]); + let mut session = PtySession::spawn(&options).expect("spawn PTY shell"); + session + .write_input(b"rover-pty-input\n") + .expect("write PTY input"); + let mut reader = session.reader.take().expect("PTY reader"); + let (sent, received) = std::sync::mpsc::channel(); + let reader_thread = std::thread::spawn(move || { + let mut output = Vec::new(); + let mut buffer = [0; 128]; + loop { + let count = reader.read(&mut buffer)?; + if count == 0 { + break; + } + output.extend_from_slice(&buffer[..count]); + if output + .windows(b"CHILD:rover-pty-input".len()) + .any(|window| window == b"CHILD:rover-pty-input") + { + break; + } + } + let _ = sent.send(output); + Ok::<(), io::Error>(()) + }); + let output = received + .recv_timeout(std::time::Duration::from_secs(2)) + .expect("child must echo input within two seconds"); + assert!(output + .windows(b"CHILD:rover-pty-input".len()) + .any(|window| window == b"CHILD:rover-pty-input")); + session.close().expect("close PTY child"); + reader_thread + .join() + .expect("reader thread") + .expect("read PTY"); + } + + #[test] + fn invalid_argv_environment_directory_and_dimensions_are_rejected() { + let mut invalid = options(&["/bin/echo", "ok"]); + invalid.rows = 0; + assert_eq!( + PtySession::spawn(&invalid) + .err() + .expect("invalid dimensions rejected") + .kind(), + io::ErrorKind::InvalidInput + ); + + let mut invalid = options(&["/bin/echo", "ok"]); + invalid.env.insert("BAD=NAME".into(), "value".into()); + assert_eq!( + PtySession::spawn(&invalid) + .err() + .expect("invalid environment rejected") + .kind(), + io::ErrorKind::InvalidInput + ); + + let mut invalid = options(&["/bin/echo", "ok"]); + invalid.cwd = PathBuf::from("relative"); + assert_eq!( + PtySession::spawn(&invalid) + .err() + .expect("invalid working directory rejected") + .kind(), + io::ErrorKind::InvalidInput + ); + + let invalid = options(&["echo", "ok"]); + assert_eq!( + PtySession::spawn(&invalid) + .err() + .expect("relative executable rejected") + .kind(), + io::ErrorKind::InvalidInput + ); + } +} diff --git a/crates/rover-execution/src/sessions.rs b/crates/rover-execution/src/sessions.rs new file mode 100644 index 0000000..12351bd --- /dev/null +++ b/crates/rover-execution/src/sessions.rs @@ -0,0 +1,1239 @@ +//! Named local terminal sessions with reconnectable Unix-socket clients. +//! +//! The server process owns each PTY child. A client disconnect only releases +//! the writer slot; stopping the server terminates its children. + +use std::collections::{BTreeMap, VecDeque}; +use std::fs; +use std::io::{self, BufRead, BufReader, Read, Write}; +use std::os::unix::fs::PermissionsExt; +use std::os::unix::net::{UnixListener, UnixStream}; +use std::path::{Path, PathBuf}; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::sync::{Arc, Mutex}; +use std::thread::{self, JoinHandle}; +use std::time::Duration; + +use rover_core::{Id, Sha256Digest}; +use rover_store::files::prepare_private_directory; +use serde::{Deserialize, Serialize}; + +use crate::pty::{PtyOptions, PtySession}; + +const HISTORY_LIMIT: usize = 32 * 1024; +const OUTPUT_CHUNK: usize = 16 * 1024; +const INPUT_LIMIT: usize = 16 * 1024; +const FRAME_LIMIT: usize = 64 * 1024; +const SOCKET_PATH_LIMIT: usize = 103; +const ACCEPT_POLL: Duration = Duration::from_millis(10); +const WRITE_TIMEOUT: Duration = Duration::from_millis(500); + +/// One validated namespace and session name. +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct SessionKey { + namespace: String, + name: String, +} + +impl SessionKey { + /// Validate a namespace/name pair using Rover's identifier rules. + /// + /// # Errors + /// + /// Returns `InvalidInput` if either identifier is invalid. + pub fn new(namespace: impl Into, name: impl Into) -> io::Result { + let namespace = namespace.into(); + let name = name.into(); + Id::parse(namespace.clone()).map_err(|_| invalid_input("invalid session namespace"))?; + Id::parse(name.clone()).map_err(|_| invalid_input("invalid session name"))?; + Ok(Self { namespace, name }) + } + + /// Namespace that scopes this session name. + #[must_use] + pub fn namespace(&self) -> &str { + &self.namespace + } + + /// Name unique within its namespace. + #[must_use] + pub fn name(&self) -> &str { + &self.name + } +} + +/// Public, reconnectable description of one session. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct SessionInfo { + /// Validated namespace and name. + pub key: SessionKey, + /// Private local socket used by clients to attach. + pub socket_path: PathBuf, + /// Whether the PTY output stream is still open. + pub running: bool, +} + +/// JSON line exchanged by Rover session clients and the local server. +#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)] +pub struct SessionFrame { + /// `output`, `input`, `resize`, `sample_foreground`, `foreground_sample`, + /// `detach`, `exit`, or `error`. + #[serde(rename = "type")] + pub kind: String, + /// Input or output bytes, encoded as JSON base64 for Go protocol compatibility. + #[serde(default, skip_serializing_if = "Vec::is_empty", with = "base64_bytes")] + pub data: Vec, + /// Validated executable basenames sampled from the owned PTY foreground + /// process group. Empty when the platform cannot provide a stable sample. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub processes: Vec, + /// Per-process bounded argv evidence from the same owned group sample. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub process_details: Vec, + /// Terminal rows for a resize request. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub rows: Option, + /// Terminal columns for a resize request. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cols: Option, + /// Bounded server error text. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, +} + +mod base64_bytes { + use base64::engine::general_purpose::STANDARD; + use base64::Engine; + use serde::{Deserialize, Deserializer, Serializer}; + + pub fn serialize(bytes: &[u8], serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&STANDARD.encode(bytes)) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let encoded = String::deserialize(deserializer)?; + STANDARD.decode(encoded).map_err(serde::de::Error::custom) + } +} + +impl SessionFrame { + fn new(kind: &str) -> Self { + Self { + kind: kind.to_owned(), + data: Vec::new(), + processes: Vec::new(), + process_details: Vec::new(), + rows: None, + cols: None, + error: None, + } + } +} + +/// In-process registry for a long-lived terminal server process. +/// +/// Names are unique within namespaces. Client detach preserves PTYs and a +/// bounded output replay ring. Dropping/stopping the server closes every PTY +/// child. Session metadata currently lives for the server process lifetime; +/// crash/restart metadata restoration belongs to P-047. +pub struct SessionServer { + sessions: Mutex>>, + socket_dir: PathBuf, +} + +impl SessionServer { + /// Create a registry whose socket files live in a private state directory. + /// Relative paths are resolved against the server process's working + /// directory and retained in that form to support Unix socket path limits. + /// + /// # Errors + /// + /// Returns an I/O error if the path is empty or cannot be safely prepared + /// as a private directory. + pub fn new(socket_dir: impl AsRef) -> io::Result { + let path = socket_dir.as_ref(); + if path.as_os_str().is_empty() { + return Err(invalid_input("session socket directory must not be empty")); + } + prepare_private_directory(path)?; + Ok(Self { + sessions: Mutex::new(BTreeMap::new()), + socket_dir: path.to_owned(), + }) + } + + /// Spawn a named PTY session owned by this server process. + /// + /// Existing names are not replaced, and stale socket files are not + /// removed automatically. Callers must inspect and explicitly resolve + /// stale state before retrying. + /// + /// # Errors + /// + /// Returns an I/O error if the name already exists, the socket path is too + /// long or occupied, PTY creation fails, or a worker thread cannot start. + pub fn spawn(&self, key: SessionKey, options: &PtyOptions) -> io::Result { + let mut sessions = self.sessions.lock().map_err(|_| poisoned())?; + if sessions.contains_key(&key) { + return Err(io::Error::new( + io::ErrorKind::AlreadyExists, + "session name already exists in this namespace", + )); + } + let socket_path = socket_path_for(&self.socket_dir, &key); + if socket_path.as_os_str().as_encoded_bytes().len() > SOCKET_PATH_LIMIT { + return Err(invalid_input("session socket path exceeds Unix limit")); + } + let broker = Arc::new(SessionBroker::start( + key.clone(), + socket_path.clone(), + options, + )?); + let info = broker.info(); + sessions.insert(key, broker); + Ok(info) + } + + /// List sessions in deterministic namespace/name order. + /// + /// # Errors + /// + /// Returns an error if the session registry lock is poisoned. + pub fn list(&self) -> io::Result> { + let sessions = self.sessions.lock().map_err(|_| poisoned())?; + Ok(sessions.values().map(|session| session.info()).collect()) + } + + /// Stop one session and terminate/reap its PTY child. + /// + /// # Errors + /// + /// Returns `NotFound` for an unknown session or an I/O error during stop. + pub fn stop(&self, key: &SessionKey) -> io::Result<()> { + let session = self + .sessions + .lock() + .map_err(|_| poisoned())? + .remove(key) + .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "session not found"))?; + session.shutdown() + } + + /// Connect a client to a session by its validated name. + /// + /// # Errors + /// + /// Returns `NotFound` for an unknown session, or an I/O error if the local + /// socket cannot be opened. + pub fn connect(&self, key: &SessionKey) -> io::Result { + let path = self + .sessions + .lock() + .map_err(|_| poisoned())? + .get(key) + .map(|session| session.socket_path.clone()) + .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "session not found"))?; + SessionClient::connect(path) + } +} + +impl Drop for SessionServer { + fn drop(&mut self) { + if let Ok(sessions) = self.sessions.get_mut() { + for (_, session) in std::mem::take(sessions) { + let _ = session.shutdown(); + } + } + } +} + +/// One connected local session client. Dropping it detaches without stopping +/// the server-owned PTY process. +pub struct SessionClient { + reader: BufReader, + writer: UnixStream, +} + +/// Read half of a connected session client, movable to a blocking reader +/// thread while the writer half stays with the interactive event loop. +pub struct SessionReader { + reader: BufReader, +} + +impl SessionReader { + /// Read the next output/error/exit frame. + /// + /// # Errors + /// + /// Returns an I/O error for a closed socket, oversized frame, or invalid + /// JSON payload. + pub fn read_frame(&mut self) -> io::Result { + read_frame(&mut self.reader) + } +} + +/// Input and resize half of a connected session client. +pub struct SessionWriter { + writer: UnixStream, +} + +impl SessionWriter { + /// Ask the server to sample the executable owning the PTY foreground group. + /// The reply is a `foreground_sample` frame with a UTF-8 basename, or an + /// empty payload when the platform cannot establish one. + /// + /// # Errors + /// + /// Returns an I/O error if the request cannot be sent. + pub fn request_foreground_sample(&mut self) -> io::Result<()> { + self.send(&SessionFrame::new("sample_foreground")) + } + + /// Send bytes to the PTY child. + /// + /// # Errors + /// + /// Returns `InvalidInput` above 16 KiB, or an I/O error if the socket + /// write fails. + pub fn send_input(&mut self, bytes: &[u8]) -> io::Result<()> { + validate_input(bytes)?; + self.send(&SessionFrame { + kind: "input".to_owned(), + data: bytes.to_vec(), + processes: Vec::new(), + process_details: Vec::new(), + rows: None, + cols: None, + error: None, + }) + } + + /// Resize the session PTY. + /// + /// # Errors + /// + /// Returns `InvalidInput` for unsupported dimensions or an I/O error if + /// the socket write fails. + pub fn resize(&mut self, rows: u16, cols: u16) -> io::Result<()> { + if rows == 0 || cols == 0 || rows > 1000 || cols > 1000 { + return Err(invalid_input( + "terminal dimensions must be between 1 and 1000", + )); + } + self.send(&SessionFrame { + kind: "resize".to_owned(), + data: Vec::new(), + processes: Vec::new(), + process_details: Vec::new(), + rows: Some(rows), + cols: Some(cols), + error: None, + }) + } + + /// Release the writer slot while leaving the PTY process alive. + /// + /// # Errors + /// + /// Returns an I/O error if the detach frame cannot be sent. + pub fn detach(&mut self) -> io::Result<()> { + self.send(&SessionFrame::new("detach"))?; + self.writer.shutdown(std::net::Shutdown::Both) + } + + fn send(&mut self, frame: &SessionFrame) -> io::Result<()> { + write_frame(&mut self.writer, frame) + } +} + +impl SessionClient { + /// Connect and claim the session's single-writer slot. + /// + /// # Errors + /// + /// Returns an I/O error if the socket cannot be opened or the server + /// rejects the connection. + pub fn connect(socket_path: impl AsRef) -> io::Result { + let stream = UnixStream::connect(socket_path)?; + let reader = BufReader::new(stream.try_clone()?); + Ok(Self { + reader, + writer: stream, + }) + } + + /// Connect by namespace and session name without an in-memory server + /// handle. The session owner must use the same private socket directory. + /// + /// Socket basenames are derived from the validated key, not from user + /// names. A stale socket is never removed or taken over automatically. + /// + /// # Errors + /// + /// Returns an I/O error if the session socket is absent, stale, or refuses + /// a connection. + pub fn connect_named(socket_dir: impl AsRef, key: &SessionKey) -> io::Result { + Self::connect(socket_path_for(socket_dir.as_ref(), key)) + } + + /// Split this single connection into a reader and writer for separate + /// threads without claiming a second client slot. + #[must_use] + pub fn into_parts(self) -> (SessionReader, SessionWriter) { + ( + SessionReader { + reader: self.reader, + }, + SessionWriter { + writer: self.writer, + }, + ) + } + + /// Read the next output/error/exit frame. + /// + /// # Errors + /// + /// Returns an I/O error for a closed socket, oversized frame, or invalid + /// JSON payload. + pub fn read_frame(&mut self) -> io::Result { + read_frame(&mut self.reader) + } + + /// Send bytes to the PTY child. + /// + /// # Errors + /// + /// Returns `InvalidInput` above 16 KiB, or an I/O error if the socket + /// write fails. + pub fn send_input(&mut self, bytes: &[u8]) -> io::Result<()> { + validate_input(bytes)?; + self.send(&SessionFrame { + kind: "input".to_owned(), + data: bytes.to_vec(), + processes: Vec::new(), + process_details: Vec::new(), + rows: None, + cols: None, + error: None, + }) + } + + /// Request a best-effort foreground executable sample from the owned PTY. + /// + /// # Errors + /// + /// Returns an I/O error if the request cannot be sent. + pub fn request_foreground_sample(&mut self) -> io::Result<()> { + self.send(&SessionFrame::new("sample_foreground")) + } + + /// Resize the session PTY. + /// + /// # Errors + /// + /// Returns `InvalidInput` for unsupported dimensions or an I/O error if + /// the socket write fails. + pub fn resize(&mut self, rows: u16, cols: u16) -> io::Result<()> { + if rows == 0 || cols == 0 || rows > 1000 || cols > 1000 { + return Err(invalid_input( + "terminal dimensions must be between 1 and 1000", + )); + } + self.send(&SessionFrame { + kind: "resize".to_owned(), + data: Vec::new(), + processes: Vec::new(), + process_details: Vec::new(), + rows: Some(rows), + cols: Some(cols), + error: None, + }) + } + + /// Release the writer slot while leaving the PTY process alive. + /// + /// # Errors + /// + /// Returns an I/O error if the detach frame cannot be sent. + pub fn detach(&mut self) -> io::Result<()> { + self.send(&SessionFrame::new("detach"))?; + self.writer.shutdown(std::net::Shutdown::Both) + } + + fn send(&mut self, frame: &SessionFrame) -> io::Result<()> { + write_frame(&mut self.writer, frame) + } +} + +fn socket_path_for(socket_dir: &Path, key: &SessionKey) -> PathBuf { + let mut identity = Vec::with_capacity(key.namespace.len() + key.name.len() + 1); + identity.extend_from_slice(key.namespace.as_bytes()); + identity.push(0); + identity.extend_from_slice(key.name.as_bytes()); + let digest = Sha256Digest::of(&identity).to_hex(); + socket_dir.join(format!("{}.sock", &digest[..32])) +} + +struct SessionBroker { + key: SessionKey, + socket_path: PathBuf, + core: Arc, + accept_thread: Mutex>>, + output_thread: Mutex>>, +} + +struct BrokerCore { + pty: Mutex, + state: Mutex, + stopping: AtomicBool, + running: AtomicBool, + next_client: AtomicU64, +} + +struct BrokerState { + history: VecDeque, + active_client: Option, + output_stream: Option, +} + +impl SessionBroker { + fn start(key: SessionKey, socket_path: PathBuf, options: &PtyOptions) -> io::Result { + if socket_path.exists() { + return Err(io::Error::new( + io::ErrorKind::AlreadyExists, + "session socket exists; takeover is not automatic", + )); + } + let listener = UnixListener::bind(&socket_path)?; + if let Err(error) = fs::set_permissions(&socket_path, fs::Permissions::from_mode(0o600)) { + let _ = fs::remove_file(&socket_path); + return Err(error); + } + if let Err(error) = listener.set_nonblocking(true) { + let _ = fs::remove_file(&socket_path); + return Err(error); + } + let mut pty = match PtySession::spawn(options) { + Ok(pty) => pty, + Err(error) => { + let _ = fs::remove_file(&socket_path); + return Err(error); + } + }; + let reader = match pty.take_output_reader() { + Ok(reader) => reader, + Err(error) => { + let _ = fs::remove_file(&socket_path); + return Err(error); + } + }; + let core = Arc::new(BrokerCore { + pty: Mutex::new(pty), + state: Mutex::new(BrokerState { + history: VecDeque::with_capacity(HISTORY_LIMIT), + active_client: None, + output_stream: None, + }), + stopping: AtomicBool::new(false), + running: AtomicBool::new(true), + next_client: AtomicU64::new(1), + }); + let output_core = Arc::clone(&core); + let output_thread = match thread::Builder::new() + .name(format!("rover-session-output-{}", key.name)) + .spawn(move || output_loop(&output_core, reader)) + { + Ok(thread) => thread, + Err(error) => { + let _ = core.pty.lock().map(|mut pty| pty.close()); + let _ = fs::remove_file(&socket_path); + return Err(error); + } + }; + let accept_core = Arc::clone(&core); + let accept_thread = match thread::Builder::new() + .name(format!("rover-session-accept-{}", key.name)) + .spawn(move || accept_loop(&accept_core, &listener)) + { + Ok(thread) => thread, + Err(error) => { + core.stopping.store(true, Ordering::Release); + let _ = core.pty.lock().map(|mut pty| pty.close()); + let _ = output_thread.join(); + let _ = fs::remove_file(&socket_path); + return Err(error); + } + }; + Ok(Self { + key, + socket_path, + core, + accept_thread: Mutex::new(Some(accept_thread)), + output_thread: Mutex::new(Some(output_thread)), + }) + } + + fn info(&self) -> SessionInfo { + SessionInfo { + key: self.key.clone(), + socket_path: self.socket_path.clone(), + running: self.core.running.load(Ordering::Acquire), + } + } + + fn shutdown(&self) -> io::Result<()> { + if self.core.stopping.swap(true, Ordering::AcqRel) { + return Ok(()); + } + if let Some(mut stream) = self + .core + .state + .lock() + .map_err(|_| poisoned())? + .output_stream + .take() + { + let _ = write_frame(&mut stream, &SessionFrame::new("exit")); + let _ = stream.shutdown(std::net::Shutdown::Both); + } + let close_result = self.core.pty.lock().map_err(|_| poisoned())?.close(); + join_thread(&self.accept_thread)?; + join_thread(&self.output_thread)?; + if let Err(error) = fs::remove_file(&self.socket_path) { + if error.kind() != io::ErrorKind::NotFound && close_result.is_ok() { + return Err(error); + } + } + close_result + } +} + +impl Drop for SessionBroker { + fn drop(&mut self) { + let _ = self.shutdown(); + } +} + +fn accept_loop(core: &Arc, listener: &UnixListener) { + while !core.stopping.load(Ordering::Acquire) { + match listener.accept() { + Ok((stream, _)) => { + if stream.set_nonblocking(false).is_err() { + continue; + } + let client_core = Arc::clone(core); + let _ = thread::Builder::new() + .name("rover-session-client".to_owned()) + .spawn(move || serve_client(&client_core, stream)); + } + Err(error) if error.kind() == io::ErrorKind::WouldBlock => { + thread::sleep(ACCEPT_POLL); + } + Err(_) => break, + } + } +} + +fn serve_client(core: &Arc, mut stream: UnixStream) { + let client_id = core.next_client.fetch_add(1, Ordering::Relaxed); + let Ok(writer) = stream.try_clone() else { + return; + }; + let Ok(mut input) = stream.try_clone().map(BufReader::new) else { + return; + }; + let Ok(mut state) = core.state.lock() else { + return; + }; + if state.active_client.is_some() { + let mut error = SessionFrame::new("error"); + error.error = Some("session already has an attached writer".to_owned()); + let _ = write_frame(&mut stream, &error); + return; + } + let history = state.history.iter().copied().collect::>(); + let replay = SessionFrame { + kind: "output".to_owned(), + data: history, + processes: Vec::new(), + process_details: Vec::new(), + rows: None, + cols: None, + error: None, + }; + if write_frame(&mut stream, &replay).is_err() { + return; + } + state.active_client = Some(client_id); + state.output_stream = Some(writer); + drop(state); + + loop { + if core.stopping.load(Ordering::Acquire) { + break; + } + match read_frame(&mut input) { + Ok(frame) => match frame.kind.as_str() { + "input" if frame.data.len() <= INPUT_LIMIT => { + if !core + .pty + .lock() + .is_ok_and(|mut pty| pty.write_input(&frame.data).is_ok()) + { + break; + } + } + "resize" => match (frame.rows, frame.cols) { + (Some(rows), Some(cols)) + if (1..=1000).contains(&rows) && (1..=1000).contains(&cols) => + { + if !core + .pty + .lock() + .is_ok_and(|mut pty| pty.resize(rows, cols).is_ok()) + { + break; + } + } + _ => break, + }, + "sample_foreground" if frame.data.is_empty() => { + if send_foreground_sample(core, client_id).is_err() { + break; + } + } + _ => break, + }, + Err(_) => break, + } + } + if let Ok(mut state) = core.state.lock() { + if state.active_client == Some(client_id) { + state.active_client = None; + state.output_stream = None; + } + } +} + +fn send_foreground_sample(core: &Arc, client_id: u64) -> io::Result<()> { + let (sample, process_details) = { + let pty = core.pty.lock().map_err(|_| poisoned())?; + ( + pty.foreground_process_name().unwrap_or_default(), + pty.foreground_process_group_details().unwrap_or_default(), + ) + }; + let mut processes = process_details + .iter() + .map(|process| process.name.clone()) + .collect::>(); + processes.sort(); + processes.dedup(); + let mut state = core.state.lock().map_err(|_| poisoned())?; + if state.active_client != Some(client_id) { + return Err(io::Error::new( + io::ErrorKind::PermissionDenied, + "session client no longer owns the writer slot", + )); + } + let output = state + .output_stream + .as_mut() + .ok_or_else(|| io::Error::new(io::ErrorKind::BrokenPipe, "session output is closed"))?; + write_frame( + output, + &SessionFrame { + kind: "foreground_sample".to_owned(), + data: sample.into_bytes(), + processes, + process_details, + rows: None, + cols: None, + error: None, + }, + ) +} + +fn output_loop(core: &Arc, mut reader: Box) { + let mut buffer = [0; OUTPUT_CHUNK]; + loop { + match reader.read(&mut buffer) { + Ok(0) => break, + Ok(count) => { + let Ok(mut state) = core.state.lock() else { + break; + }; + retain_history(&mut state.history, &buffer[..count]); + if let Some(stream) = state.output_stream.as_mut() { + let _ = stream.set_write_timeout(Some(WRITE_TIMEOUT)); + let frame = SessionFrame { + kind: "output".to_owned(), + data: buffer[..count].to_vec(), + processes: Vec::new(), + process_details: Vec::new(), + rows: None, + cols: None, + error: None, + }; + if write_frame(stream, &frame).is_err() { + let _ = stream.shutdown(std::net::Shutdown::Both); + state.output_stream = None; + state.active_client = None; + } + } + } + Err(error) if error.kind() == io::ErrorKind::Interrupted => {} + Err(error) if is_retryable_read_error(&error) => { + thread::sleep(ACCEPT_POLL); + } + Err(_) => break, + } + } + core.running.store(false, Ordering::Release); + if let Ok(mut state) = core.state.lock() { + if let Some(stream) = state.output_stream.as_mut() { + let _ = write_frame(stream, &SessionFrame::new("exit")); + let _ = stream.shutdown(std::net::Shutdown::Both); + } + state.output_stream = None; + state.active_client = None; + } + if let Ok(mut pty) = core.pty.lock() { + let _ = pty.close(); + } +} + +fn retain_history(history: &mut VecDeque, bytes: &[u8]) { + history.extend(bytes); + while history.len() > HISTORY_LIMIT { + history.pop_front(); + } +} + +fn validate_input(bytes: &[u8]) -> io::Result<()> { + if bytes.len() > INPUT_LIMIT { + return Err(invalid_input("session input exceeds 16 KiB")); + } + Ok(()) +} + +fn is_retryable_read_error(error: &io::Error) -> bool { + error.kind() == io::ErrorKind::WouldBlock + || error + .raw_os_error() + .is_some_and(|code| code == rustix::io::Errno::AGAIN.raw_os_error()) +} + +fn read_frame(reader: &mut impl BufRead) -> io::Result { + let mut line = Vec::with_capacity(256); + let count = reader + .take((FRAME_LIMIT + 1) as u64) + .read_until(b'\n', &mut line)?; + if count == 0 { + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "session disconnected", + )); + } + if count > FRAME_LIMIT { + return Err(invalid_input("session frame exceeds 64 KiB")); + } + serde_json::from_slice(&line).map_err(|error| invalid_data(error.to_string())) +} + +fn write_frame(writer: &mut impl Write, frame: &SessionFrame) -> io::Result<()> { + serde_json::to_writer(&mut *writer, frame).map_err(|error| invalid_data(error.to_string()))?; + writer.write_all(b"\n") +} + +fn join_thread(handle: &Mutex>>) -> io::Result<()> { + let worker = handle.lock().map_err(|_| poisoned())?.take(); + if worker.is_some_and(|worker| worker.join().is_err()) { + return Err(io::Error::other("session worker thread panicked")); + } + Ok(()) +} + +fn invalid_input(message: &str) -> io::Error { + io::Error::new(io::ErrorKind::InvalidInput, message) +} + +fn invalid_data(message: String) -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, message) +} + +fn poisoned() -> io::Error { + io::Error::other("session server lock is poisoned") +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + use std::os::unix::fs::PermissionsExt; + use std::process::Command; + use std::time::{Instant, SystemTime, UNIX_EPOCH}; + + fn fixture_dir() -> PathBuf { + let nonce = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("clock") + .as_nanos(); + PathBuf::from(format!(".rover-session-test-{nonce}")) + } + + fn pty_options(command: &str) -> PtyOptions { + pty_argv(&["/bin/sh", "-c", command]) + } + + fn pty_argv(argv: &[&str]) -> PtyOptions { + let mut env = BTreeMap::new(); + env.insert("PATH".to_owned(), "/usr/bin:/bin".to_owned()); + PtyOptions { + argv: argv.iter().map(|argument| (*argument).to_owned()).collect(), + cwd: std::env::current_dir().expect("cwd"), + env, + rows: 24, + cols: 80, + } + } + + #[test] + fn frame_bytes_match_go_json_base64_encoding() { + let frame = SessionFrame { + kind: "output".to_owned(), + data: vec![0, 1, 2], + processes: Vec::new(), + process_details: Vec::new(), + rows: None, + cols: None, + error: None, + }; + assert_eq!( + serde_json::to_string(&frame).unwrap(), + r#"{"type":"output","data":"AAEC"}"# + ); + assert_eq!( + serde_json::from_str::(r#"{"type":"input","data":"AAEC"}"#) + .unwrap() + .data, + vec![0, 1, 2] + ); + } + + #[test] + fn foreground_process_group_field_is_additive_and_round_trips() { + let frame = SessionFrame { + kind: "foreground_sample".to_owned(), + data: b"sh".to_vec(), + processes: vec!["amp".to_owned(), "sh".to_owned()], + process_details: vec![crate::pty::ForegroundProcessDetails { + name: "node".to_owned(), + argv: Some(vec![ + "/usr/bin/node".to_owned(), + "node_modules/@letta-ai/letta-code/letta".to_owned(), + ]), + }], + rows: None, + cols: None, + error: None, + }; + let encoded = serde_json::to_string(&frame).unwrap(); + assert!(encoded.contains(r#""processes":["amp","sh"]"#)); + assert!(encoded.contains(r#""process_details":[{"name":"node","argv":["/usr/bin/node","node_modules/@letta-ai/letta-code/letta"]}]"#)); + let decoded: SessionFrame = serde_json::from_str(&encoded).unwrap(); + assert_eq!(decoded, frame); + let legacy: SessionFrame = + serde_json::from_str(r#"{"type":"output","data":"AAEC"}"#).unwrap(); + assert!(legacy.processes.is_empty()); + } + + #[test] + fn replay_history_is_capped_and_preserves_the_newest_bytes() { + let mut history = VecDeque::new(); + retain_history(&mut history, &vec![b'a'; HISTORY_LIMIT]); + retain_history(&mut history, b"tail"); + assert_eq!(history.len(), HISTORY_LIMIT); + assert_eq!( + history + .iter() + .skip(HISTORY_LIMIT - 4) + .copied() + .collect::>(), + b"tail" + ); + } + + #[test] + fn input_frame_limit_is_enforced_before_writing() { + assert_eq!( + validate_input(&vec![b'x'; INPUT_LIMIT + 1]) + .unwrap_err() + .kind(), + io::ErrorKind::InvalidInput + ); + } + + fn wait_for_output(client: &mut SessionClient, needle: &[u8]) -> Vec { + let deadline = Instant::now() + Duration::from_secs(3); + let mut output = Vec::new(); + while Instant::now() < deadline { + match client.read_frame() { + Ok(frame) if frame.kind == "output" => { + output.extend(frame.data); + if output.windows(needle.len()).any(|window| window == needle) { + return output; + } + } + Ok(frame) if frame.kind == "error" => panic!("server error: {:?}", frame.error), + Ok(_) => {} + Err(error) + if matches!( + error.kind(), + io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut + ) => {} + Err(error) => panic!("session read failed while waiting for {needle:?}: {error}"), + } + } + panic!("timed out waiting for session output: {output:?}"); + } + + #[test] + fn named_sessions_are_namespace_scoped_and_duplicate_names_fail() { + let directory = fixture_dir(); + let server = SessionServer::new(&directory).expect("private session directory"); + let key_a = SessionKey::new("project-a", "shell").unwrap(); + let key_b = SessionKey::new("project-b", "shell").unwrap(); + let info_a = server + .spawn(key_a.clone(), &pty_options("sleep 30")) + .unwrap(); + let info_b = server + .spawn(key_b.clone(), &pty_options("sleep 30")) + .unwrap(); + assert_eq!(server.list().unwrap().len(), 2); + assert_eq!( + server + .spawn(key_a.clone(), &pty_options("true")) + .unwrap_err() + .kind(), + io::ErrorKind::AlreadyExists + ); + server.stop(&key_a).unwrap(); + assert_eq!(server.list().unwrap().len(), 1); + drop(server); + assert!(!info_a.socket_path.exists()); + assert!(!info_b.socket_path.exists()); + fs::remove_dir(directory).unwrap(); + } + + #[test] + fn detach_preserves_process_and_reconnect_replays_bounded_output() { + let directory = fixture_dir(); + let server = SessionServer::new(&directory).expect("private session directory"); + let key = SessionKey::new("project", "shell").unwrap(); + let info = server + .spawn(key.clone(), &pty_argv(&["/bin/sleep", "30"])) + .expect("spawn terminal"); + assert_eq!( + fs::metadata(&info.socket_path) + .unwrap() + .permissions() + .mode() + & 0o777, + 0o600 + ); + + let mut first = server.connect(&key).unwrap(); + first.send_input(b"READY\n").unwrap(); + let first_output = wait_for_output(&mut first, b"READY"); + assert!(first_output.windows(5).any(|window| window == b"READY")); + assert!(server.list().unwrap()[0].running); + first.detach().unwrap(); + drop(first); + std::thread::sleep(Duration::from_millis(40)); + assert!(server.list().unwrap()[0].running); + + let mut second = server.connect(&key).unwrap(); + let replay = second.read_frame().unwrap(); + assert_eq!(replay.kind, "output"); + assert!(replay.data.windows(5).any(|window| window == b"READY")); + second.send_input(b"again\n").unwrap(); + let second_output = wait_for_output(&mut second, b"again"); + assert!(second_output.windows(5).any(|window| window == b"again")); + assert!(server.list().unwrap()[0].running); + server.stop(&key).unwrap(); + assert!(!info.socket_path.exists()); + drop(server); + let _ = fs::remove_dir(directory); + } + + #[test] + fn attached_client_receives_only_owned_pty_foreground_samples() { + let directory = fixture_dir(); + let server = SessionServer::new(&directory).expect("private session directory"); + let key = SessionKey::new("project", "sample").unwrap(); + server + .spawn(key.clone(), &pty_argv(&["/bin/sleep", "30"])) + .expect("spawn PTY child"); + let mut client = server.connect(&key).expect("attach session client"); + assert_eq!(client.read_frame().unwrap().kind, "output"); + client.request_foreground_sample().unwrap(); + let sample = client.read_frame().expect("foreground sample reply"); + assert_eq!(sample.kind, "foreground_sample"); + assert!(sample.error.is_none()); + #[cfg(target_os = "linux")] + assert!(!sample.processes.is_empty()); + #[cfg(target_os = "linux")] + assert!(sample.process_details.iter().any(|process| { + process.name == "sleep" + && process + .argv + .as_ref() + .is_some_and(|argv| argv.iter().any(|argument| argument == "30")) + })); + #[cfg(target_os = "macos")] + assert!(sample + .process_details + .iter() + .any(|process| process.name == "sleep")); + let name = std::str::from_utf8(&sample.data).expect("UTF-8 process basename"); + #[cfg(any(target_os = "linux", target_os = "macos"))] + assert_eq!(name, "sleep"); + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + assert!(name.is_empty()); + server.stop(&key).unwrap(); + drop(client); + drop(server); + let _ = fs::remove_dir(directory); + } + + #[test] + fn split_reader_writer_halves_keep_one_session_connection_live() { + let directory = fixture_dir(); + let server = SessionServer::new(&directory).expect("private session directory"); + let key = SessionKey::new("project", "shell").unwrap(); + server + .spawn( + key.clone(), + &pty_options("IFS= read -r line; printf '<%s>' \"$line\"; sleep 1"), + ) + .expect("spawn terminal"); + let client = server.connect(&key).unwrap(); + let (mut reader, mut writer) = client.into_parts(); + writer.send_input(b"TUI-bridge\n").unwrap(); + + let deadline = Instant::now() + Duration::from_secs(3); + let mut output = Vec::new(); + while Instant::now() < deadline { + let frame = reader.read_frame().expect("read session output"); + if frame.kind == "output" { + output.extend(frame.data); + if output.windows(12).any(|window| window == b"") { + break; + } + } + } + assert!(output.windows(12).any(|window| window == b"")); + writer.detach().unwrap(); + drop(server); + let _ = fs::remove_dir(directory); + } + + #[test] + fn client_connects_by_namespace_and_name_and_replays_after_detach() { + let directory = fixture_dir(); + let server = SessionServer::new(&directory).expect("private session directory"); + let key = SessionKey::new("project", "shell").unwrap(); + let info = server + .spawn( + key.clone(), + &pty_options("IFS= read -r line; printf '<%s>' \"$line\"; sleep 1"), + ) + .expect("spawn named session"); + + let child = Command::new(std::env::current_exe().unwrap()) + .args([ + "--exact", + "sessions::tests::named_client_subprocess_fixture", + "--nocapture", + ]) + .env("ROVER_SESSION_TEST_SOCKET_DIR", &directory) + .env("ROVER_SESSION_TEST_NAMESPACE", key.namespace()) + .env("ROVER_SESSION_TEST_NAME", key.name()) + .status() + .expect("run a separate process as the named client"); + assert!(child.success(), "named client subprocess failed: {child}"); + + let mut second = SessionClient::connect_named(&directory, &key).unwrap(); + let replay = second.read_frame().unwrap(); + assert_eq!(replay.kind, "output"); + assert!(replay + .data + .windows(b"".len()) + .any(|window| window == b"")); + second.detach().unwrap(); + + server.stop(&key).unwrap(); + assert!(!info.socket_path.exists()); + match SessionClient::connect_named(&directory, &key) { + Err(error) => assert_eq!(error.kind(), io::ErrorKind::NotFound), + Ok(_) => panic!("stopped session socket must no longer be connectable"), + } + drop(server); + fs::remove_dir(directory).unwrap(); + } + + #[test] + fn named_client_subprocess_fixture() { + let (Ok(socket_dir), Ok(namespace), Ok(name)) = ( + std::env::var("ROVER_SESSION_TEST_SOCKET_DIR"), + std::env::var("ROVER_SESSION_TEST_NAMESPACE"), + std::env::var("ROVER_SESSION_TEST_NAME"), + ) else { + return; + }; + let key = SessionKey::new(namespace, name).unwrap(); + let mut client = SessionClient::connect_named(socket_dir, &key).unwrap(); + client.send_input(b"from-separate-process\n").unwrap(); + let response = wait_for_output(&mut client, b""); + assert!(response + .windows(b"".len()) + .any(|window| window == b"")); + client.detach().unwrap(); + } + + #[test] + fn only_one_client_can_hold_the_writer_slot() { + let directory = fixture_dir(); + let server = SessionServer::new(&directory).expect("private session directory"); + let key = SessionKey::new("project", "shell").unwrap(); + let info = server + .spawn(key.clone(), &pty_options("sleep 30")) + .expect("spawn terminal"); + let first = server.connect(&key).unwrap(); + std::thread::sleep(Duration::from_millis(20)); + let mut second = SessionClient::connect(&info.socket_path).unwrap(); + let response = second.read_frame().unwrap(); + let response = if response.kind == "output" { + second.read_frame().unwrap() + } else { + response + }; + assert_eq!(response.kind, "error"); + assert_eq!( + response.error.as_deref(), + Some("session already has an attached writer") + ); + drop(first); + drop(server); + assert!(!info.socket_path.exists()); + let _ = fs::remove_dir(directory); + } +} diff --git a/crates/rover-execution/src/supervisor.rs b/crates/rover-execution/src/supervisor.rs new file mode 100644 index 0000000..454465d --- /dev/null +++ b/crates/rover-execution/src/supervisor.rs @@ -0,0 +1,1507 @@ +//! Conservative process-presence checks for detached supervisor recovery. +//! +//! A stale heartbeat alone never proves a supervisor is gone. Linux process +//! identity includes the boot ID and kernel start time, so PID reuse can be +//! detected. On other Unix systems Rover only marks a process gone when the OS +//! explicitly reports that the PID does not exist. + +use rover_store::{Store, StoreError}; +use serde_json::{Map, Value}; +#[cfg(unix)] +use std::collections::BTreeSet; +use std::fmt; +#[cfg(target_os = "linux")] +use std::fs; +use std::io; +#[cfg(unix)] +use std::os::unix::process::CommandExt; +use std::path::PathBuf; +#[cfg(unix)] +use std::process::{Command, Stdio}; +use std::sync::{mpsc, Arc}; +use std::thread; +use std::time::Duration; +use time::{format_description::well_known::Rfc3339, OffsetDateTime}; + +const LOST_AFTER_SECONDS: i64 = 10; +const HEARTBEAT_INTERVAL: Duration = Duration::from_millis(250); + +/// Inputs for launching one durable Rover task worker. +#[derive(Clone, Debug)] +pub struct LaunchOptions { + /// Existing queued task record to own. + pub task_id: String, + /// Absolute executable path followed by arguments; no shell is inserted. + pub argv: Vec, + /// Existing absolute directory used as the worker's current directory. + pub dir: PathBuf, + /// Private `/tasks/` directory where `supervisor.log` is created exclusively. + pub output_dir: PathBuf, + /// Explicit host environment names to pass to the worker. + pub pass_env: Vec, +} + +/// Process ownership returned after the task dispatch event is persisted. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct LaunchReceipt { + pub pid: i32, + pub process_identity: Option, +} + +/// Failure while validating, starting, or recording a detached task worker. +#[derive(Debug)] +pub enum LaunchError { + Invalid(String), + Io(io::Error), + Store(StoreError), + StartedButUnrecorded { + task_id: String, + receipt: Box, + source: Box, + }, +} + +impl fmt::Display for LaunchError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Invalid(reason) => write!(formatter, "invalid worker launch: {reason}"), + Self::Io(error) => write!(formatter, "worker launch I/O failed: {error}"), + Self::Store(error) => write!(formatter, "worker state update failed: {error}"), + Self::StartedButUnrecorded { + task_id, + receipt, + source, + } => write!( + formatter, + "worker for task {task_id} started as PID {} but dispatch persistence failed; inspect before retry: {source}", + receipt.pid + ), + } + } +} + +impl std::error::Error for LaunchError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::Io(error) => Some(error), + Self::Store(error) => Some(error), + Self::StartedButUnrecorded { source, .. } => Some(source.as_ref()), + Self::Invalid(_) => None, + } + } +} + +/// A failure reported by a managed heartbeat loop. +#[derive(Debug)] +pub enum HeartbeatError { + Store(StoreError), + WorkerPanicked, +} + +impl fmt::Display for HeartbeatError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Store(error) => write!(formatter, "task heartbeat persistence failed: {error}"), + Self::WorkerPanicked => formatter.write_str("task heartbeat thread panicked"), + } + } +} + +impl std::error::Error for HeartbeatError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::Store(error) => Some(error), + Self::WorkerPanicked => None, + } + } +} + +/// Owner for a worker's periodic task heartbeat. +pub struct HeartbeatGuard { + stop: Option>, + thread: Option>>, +} + +impl HeartbeatGuard { + /// Stop the heartbeat thread and return any persistence failure it saw. + /// + /// # Errors + /// + /// Returns the first store error from the heartbeat loop or if the loop + /// thread panicked. + pub fn stop(mut self) -> Result<(), HeartbeatError> { + self.stop_and_join() + } + + fn stop_and_join(&mut self) -> Result<(), HeartbeatError> { + if let Some(stop) = self.stop.take() { + let _ = stop.send(()); + } + match self.thread.take().map(thread::JoinHandle::join) { + Some(Ok(result)) => result.map_err(HeartbeatError::Store), + Some(Err(_)) => Err(HeartbeatError::WorkerPanicked), + None => Ok(()), + } + } +} + +impl Drop for HeartbeatGuard { + fn drop(&mut self) { + let _ = self.stop_and_join(); + } +} + +/// Start a managed 250 ms heartbeat loop for a claimed task. +/// +/// A persisted cancellation request cancels the supplied process token. A +/// storage failure also cancels the token and is returned by +/// [`HeartbeatGuard::stop`]. Dropping the guard stops and joins the loop. +/// +/// # Errors +/// +/// Returns an I/O error if the heartbeat thread cannot be created. +pub fn start_heartbeat( + store: Arc, + task_id: impl Into, + cancellation: super::CancellationToken, +) -> io::Result { + start_heartbeat_at_interval(store, task_id.into(), cancellation, HEARTBEAT_INTERVAL) +} + +fn start_heartbeat_at_interval( + store: Arc, + task_id: String, + cancellation: super::CancellationToken, + interval: Duration, +) -> io::Result { + let (stop, stopped) = mpsc::channel(); + let worker = thread::Builder::new() + .name(format!("rover-heartbeat-{task_id}")) + .spawn(move || loop { + match stopped.recv_timeout(interval) { + Ok(()) | Err(mpsc::RecvTimeoutError::Disconnected) => return Ok(()), + Err(mpsc::RecvTimeoutError::Timeout) => { + let timestamp = rover_core::now().to_string(); + match heartbeat(&store, &task_id, ×tamp) { + Ok(true) => cancellation.cancel(), + Ok(false) => {} + Err(error) => { + cancellation.cancel(); + return Err(error); + } + } + } + } + })?; + Ok(HeartbeatGuard { + stop: Some(stop), + thread: Some(worker), + }) +} + +/// Start a task worker independently of the caller and persist its ownership. +/// +/// The worker has null stdin, a private exclusive combined stdout/stderr log, +/// a separate process group, and only PATH, HOME, plus the named +/// `pass_env` values. This is process supervision, not a sandbox. Dropping the +/// returned receipt does not stop the worker. +/// +/// # Errors +/// +/// Returns an error for invalid launch fields, unsafe/private log setup, +/// process creation or reaper setup failure, missing task records, or dispatch +/// persistence failure. A persistence failure after spawn returns the PID and +/// explicitly warns callers to inspect state before retrying. +#[cfg(unix)] +pub fn launch_detached( + store: &Store, + options: &LaunchOptions, +) -> Result { + validate_launch(options)?; + let task = store + .get("task", &options.task_id) + .map_err(LaunchError::Store)?; + let task_status = task + .as_value() + .get("status") + .and_then(Value::as_str) + .ok_or_else(|| LaunchError::Invalid("task status is missing or invalid".into()))?; + if task_status != "QUEUED" { + return Err(LaunchError::Invalid(format!( + "task cannot be dispatched from {task_status}" + ))); + } + let task_record_id = task + .as_value() + .get("id") + .and_then(Value::as_str) + .ok_or_else(|| LaunchError::Invalid("task ID field is missing or invalid".into()))?; + if task_record_id != options.task_id { + return Err(LaunchError::Invalid( + "task record ID does not match its storage key".into(), + )); + } + optional_bool( + task.as_value() + .as_object() + .ok_or_else(|| LaunchError::Invalid("task record must be an object".into()))?, + "cancel_requested", + ) + .map_err(|error| LaunchError::Invalid(error.to_string()))?; + let workspace = task + .as_value() + .get("workspace") + .and_then(Value::as_str) + .ok_or_else(|| LaunchError::Invalid("task workspace is missing or invalid".into()))?; + let workspace = PathBuf::from(workspace); + if workspace.file_name().is_none_or(|name| name != "workspace") + || workspace.parent() != Some(options.output_dir.as_path()) + { + return Err(LaunchError::Invalid( + "worker output directory must be the task workspace parent".into(), + )); + } + + let (log, stderr_log) = create_worker_log(options).map_err(LaunchError::Io)?; + let (reaper_tx, reaper) = match start_reaper(&options.task_id) { + Ok(reaper) => reaper, + Err(error) => { + let message = format!("supervisor reaper start failed: {error}"); + record_dispatch_failure(store, &options.task_id, &message) + .map_err(LaunchError::Store)?; + return Err(LaunchError::Io(error)); + } + }; + let child = match spawn_worker(options, log, stderr_log) { + Ok(child) => child, + Err(error) => { + drop(reaper_tx); + drop(reaper); + let message = format!("supervisor start failed: {error}"); + record_dispatch_failure(store, &options.task_id, &message) + .map_err(LaunchError::Store)?; + return Err(LaunchError::Io(error)); + } + }; + let pid = match hand_off_to_reaper(child, reaper_tx, reaper) { + Ok(pid) => pid, + Err(error) => { + let message = format!("supervisor reaper handoff failed: {error}"); + record_dispatch_failure(store, &options.task_id, &message) + .map_err(LaunchError::Store)?; + return Err(LaunchError::Io(error)); + } + }; + let receipt = LaunchReceipt { + pid, + process_identity: process_identity(pid), + }; + if let Err(source) = record_dispatch(store, &options.task_id, &receipt) { + return Err(LaunchError::StartedButUnrecorded { + task_id: options.task_id.clone(), + receipt: Box::new(receipt), + source: Box::new(source), + }); + } + Ok(receipt) +} + +#[cfg(unix)] +fn create_worker_log(options: &LaunchOptions) -> io::Result<(std::fs::File, std::fs::File)> { + let output = rover_store::files::prepare_private_directory(&options.output_dir)?; + let log = output.create_new_file_writer("supervisor.log", 0o600)?; + let stderr_log = log.try_clone()?; + Ok((log, stderr_log)) +} + +#[cfg(unix)] +fn start_reaper( + task_id: &str, +) -> io::Result<(mpsc::Sender, thread::JoinHandle<()>)> { + let (reaper_tx, reaper_rx) = mpsc::channel::(); + let reaper = thread::Builder::new() + .name(format!("rover-worker-reaper-{task_id}")) + .spawn(move || { + if let Ok(mut child) = reaper_rx.recv() { + let _ = child.wait(); + } + })?; + Ok((reaper_tx, reaper)) +} + +#[cfg(unix)] +fn spawn_worker( + options: &LaunchOptions, + log: std::fs::File, + stderr_log: std::fs::File, +) -> io::Result { + let host_path = std::env::var_os("PATH").unwrap_or_default(); + let mut command = Command::new(&options.argv[0]); + command + .args(&options.argv[1..]) + .current_dir(&options.dir) + .env_clear() + .env("PATH", &host_path) + .env("HOME", std::env::var_os("HOME").unwrap_or_default()) + .stdin(Stdio::null()) + .stdout(Stdio::from(log)) + .stderr(Stdio::from(stderr_log)); + let mut seen_env = BTreeSet::from(["PATH".to_owned(), "HOME".to_owned()]); + for name in &options.pass_env { + if seen_env.insert(name.clone()) { + if let Some(value) = std::env::var_os(name) { + command.env(name, value); + } + } + } + command.process_group(0); + command.spawn() +} + +#[cfg(unix)] +fn hand_off_to_reaper( + child: std::process::Child, + reaper_tx: mpsc::Sender, + reaper: thread::JoinHandle<()>, +) -> io::Result { + let Ok(pid) = i32::try_from(child.id()) else { + let mut child = child; + let process_id = rustix::process::Pid::from_child(&child); + super::kill_group(process_id, &mut child); + let _ = child.wait(); + drop(reaper_tx); + drop(reaper); + return Err(io::Error::other( + "worker PID exceeds the supported process identifier range", + )); + }; + if let Err(error) = reaper_tx.send(child) { + let mut orphaned_child = error.0; + let process_id = rustix::process::Pid::from_child(&orphaned_child); + super::kill_group(process_id, &mut orphaned_child); + let _ = orphaned_child.wait(); + drop(reaper); + return Err(io::Error::other(format!( + "worker reaper stopped before taking ownership: {pid}" + ))); + } + drop(reaper_tx); + drop(reaper); + Ok(pid) +} + +#[cfg(unix)] +fn validate_launch(options: &LaunchOptions) -> Result<(), LaunchError> { + if options.task_id.is_empty() || rover_core::Id::parse(options.task_id.clone()).is_err() { + return Err(LaunchError::Invalid("task ID is invalid".into())); + } + if options.argv.is_empty() || options.argv.len() > 129 { + return Err(LaunchError::Invalid( + "argv must include an executable and at most 128 arguments".into(), + )); + } + if !PathBuf::from(&options.argv[0]).is_absolute() { + return Err(LaunchError::Invalid( + "worker executable path must be absolute".into(), + )); + } + if options + .argv + .iter() + .any(|arg| arg.contains('\0') || arg.len() > 65_536) + { + return Err(LaunchError::Invalid( + "argv contains NUL or an oversized argument".into(), + )); + } + if !options.dir.is_absolute() || !options.dir.is_dir() { + return Err(LaunchError::Invalid( + "worker directory must be an existing absolute directory".into(), + )); + } + for name in &options.pass_env { + if !super::valid_env_name(name) { + return Err(LaunchError::Invalid(format!( + "invalid environment variable name {name:?}" + ))); + } + } + Ok(()) +} + +#[cfg(unix)] +fn record_dispatch( + store: &Store, + task_id: &str, + receipt: &LaunchReceipt, +) -> Result<(), StoreError> { + let now = rover_core::now().to_string(); + store.mutate_with_event("task", task_id, |old| { + let mut next = old.clone(); + let object = as_object_mut(&mut next)?; + if required_string(object, "status")? == "QUEUED" { + object.insert("pid".into(), Value::from(receipt.pid)); + if let Some(identity) = &receipt.process_identity { + object.insert("process_identity".into(), Value::String(identity.clone())); + } + object.insert("heartbeat".into(), Value::String(now.clone())); + object.insert("updated_at".into(), Value::String(now.clone())); + } + Ok((next, Some("task.dispatched".into()))) + }) +} + +#[cfg(unix)] +fn record_dispatch_failure(store: &Store, task_id: &str, message: &str) -> Result<(), StoreError> { + let now = rover_core::now().to_string(); + store.mutate_with_event("task", task_id, |old| { + let mut next = old.clone(); + let object = as_object_mut(&mut next)?; + if required_string(object, "status")? == "QUEUED" { + object.insert("status".into(), Value::String("ERROR".into())); + object.insert("error".into(), Value::String(message.to_owned())); + object.insert("updated_at".into(), Value::String(now.clone())); + Ok((next, Some("task.dispatch_failed".into()))) + } else { + Ok((next, None)) + } + }) +} + +/// Return an OS-stable process identity when the platform exposes one. +/// +/// Linux identities bind boot ID, PID, and `/proc//stat` start time. +/// macOS currently returns `None`, matching Rover's Go behavior: a live or +/// reused PID remains ambiguous and must not be declared lost from heartbeat +/// age alone. +#[must_use] +pub fn process_identity(pid: i32) -> Option { + #[cfg(target_os = "linux")] + { + linux_process_identity(pid) + } + #[cfg(not(target_os = "linux"))] + { + let _ = pid; + None + } +} + +/// Return true only when the OS and available identity evidence prove that the +/// recorded process is gone or its PID now belongs to a different process. +#[must_use] +pub fn definitely_gone(pid: i32, identity: &str) -> bool { + if pid <= 0 { + return false; + } + #[cfg(unix)] + { + definitely_gone_unix(pid, identity) + } + #[cfg(not(unix))] + { + let _ = identity; + false + } +} + +#[cfg(unix)] +fn definitely_gone_unix(pid: i32, identity: &str) -> bool { + let Some(pid) = rustix::process::Pid::from_raw(pid) else { + return false; + }; + if matches!( + rustix::process::test_kill_process(pid), + Err(rustix::io::Errno::SRCH) + ) { + return true; + } + + #[cfg(target_os = "linux")] + { + if !identity.is_empty() { + if let Some(current) = linux_process_identity(pid.as_raw_nonzero().get()) { + if identity != current { + return true; + } + } + } + linux_process_is_zombie(pid.as_raw_nonzero().get()) + } + #[cfg(not(target_os = "linux"))] + { + let _ = identity; + false + } +} + +/// Result of atomically claiming a queued task for a supervisor process. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ClaimOutcome { + /// The queued task is now owned by this process. + Claimed, + /// Cancellation was already requested, so the queued task became terminal. + Cancelled, +} + +/// Atomically claim a queued Rover task and persist process ownership. +/// +/// `now` must be an RFC 3339 timestamp. A task that has already transitioned +/// cannot be claimed again, and a pre-cancelled task is made terminal without +/// launching work. +/// +/// # Errors +/// +/// Returns a store error for malformed records, invalid timestamps, missing +/// tasks, repeated claims, or database failures. +pub fn claim( + store: &Store, + task_id: &str, + pid: i32, + process_identity: Option<&str>, + now: &str, +) -> Result { + if pid <= 0 { + return Err(StoreError::MutationRejected( + "supervisor PID must be positive".into(), + )); + } + parse_time(now)?; + let mut outcome = None; + store.mutate_with_event("task", task_id, |old| { + let mut next = old.clone(); + let object = as_object_mut(&mut next)?; + let status = required_string(object, "status")?; + if status != "QUEUED" { + return Err(StoreError::MutationRejected(format!( + "task cannot be claimed from {status}; automatic re-execution is disabled" + ))); + } + let cancelled = optional_bool(object, "cancel_requested")?; + if cancelled { + object.insert("status".into(), Value::String("CANCELLED".into())); + outcome = Some(ClaimOutcome::Cancelled); + } else { + object.insert("status".into(), Value::String("PREPARING".into())); + object.insert("pid".into(), Value::from(pid)); + if let Some(identity) = process_identity.filter(|value| !value.is_empty()) { + object.insert( + "process_identity".into(), + Value::String(identity.to_owned()), + ); + } + object.insert("heartbeat".into(), Value::String(now.to_owned())); + outcome = Some(ClaimOutcome::Claimed); + } + object.insert("updated_at".into(), Value::String(now.to_owned())); + Ok((next, Some("task.claimed".into()))) + })?; + outcome.ok_or_else(|| StoreError::MutationRejected("task claim produced no outcome".into())) +} + +/// Refresh a live task heartbeat and return whether the worker should cancel. +/// Terminal records are left unchanged. Heartbeats append no event rows. +/// +/// # Errors +/// +/// Returns a store error for malformed records, invalid timestamps, missing +/// tasks, or database failures. +pub fn heartbeat(store: &Store, task_id: &str, now: &str) -> Result { + parse_time(now)?; + let mut cancel_requested = false; + store.mutate_with_event("task", task_id, |old| { + let mut next = old.clone(); + let object = as_object_mut(&mut next)?; + let status = required_string(object, "status")?; + if is_terminal(status) { + return Ok((next, None)); + } + cancel_requested = optional_bool(object, "cancel_requested")?; + object.insert("heartbeat".into(), Value::String(now.to_owned())); + object.insert("updated_at".into(), Value::String(now.to_owned())); + Ok((next, None)) + })?; + Ok(cancel_requested) +} + +/// Persist cancellation intent unless the task is already terminal. +/// +/// # Errors +/// +/// Returns a store error for malformed records, invalid timestamps, terminal +/// tasks, missing tasks, or database failures. +pub fn request_cancel(store: &Store, task_id: &str, now: &str) -> Result<(), StoreError> { + parse_time(now)?; + store.mutate_with_event("task", task_id, |old| { + let mut next = old.clone(); + let object = as_object_mut(&mut next)?; + let status = required_string(object, "status")?; + if is_terminal(status) { + return Err(StoreError::MutationRejected( + "task is already terminal".into(), + )); + } + object.insert("cancel_requested".into(), Value::Bool(true)); + object.insert("updated_at".into(), Value::String(now.to_owned())); + Ok((next, Some("task.cancel_requested".into()))) + }) +} + +/// Mark stale tasks LOST only after the process-presence check proves loss. +/// Workspaces and process records are retained; this function never restarts a +/// task or treats a stale heartbeat alone as evidence of process death. +/// +/// # Errors +/// +/// Returns a store error for malformed records, missing tasks, or database +/// failures. +pub fn reconcile(store: &Store, now: &str) -> Result { + reconcile_with_presence(store, now, definitely_gone) +} + +fn reconcile_with_presence( + store: &Store, + now: &str, + mut process_is_gone: F, +) -> Result +where + F: FnMut(i32, &str) -> bool, +{ + let now_text = now.to_owned(); + let now = parse_time(now)?; + let stale_before = now - time::Duration::seconds(LOST_AFTER_SECONDS); + let records = store.list("task", 1000)?; + let mut lost = 0; + for record in records { + let value = record.as_value(); + let Some(object) = value.as_object() else { + return Err(StoreError::MutationRejected( + "task record must be an object".into(), + )); + }; + let status = required_string(object, "status")?; + if is_terminal(status) { + // A previous reconciliation may have persisted LOST and then + // failed while releasing leases. Retrying cleanup is safe because + // resource release is owner-scoped and idempotent. + if status == "LOST" { + let task_id = object + .get("id") + .and_then(Value::as_str) + .ok_or_else(|| StoreError::MutationRejected("task record has no ID".into()))?; + store.release_resources(task_id)?; + } + continue; + } + let Some(pid) = object.get("pid").and_then(Value::as_i64) else { + continue; + }; + let Ok(pid) = i32::try_from(pid) else { + continue; + }; + if pid <= 0 { + continue; + } + let Some(heartbeat) = object.get("heartbeat").and_then(Value::as_str) else { + continue; + }; + let Ok(heartbeat_time) = parse_time(heartbeat) else { + continue; + }; + if heartbeat_time > stale_before { + continue; + } + let identity = object + .get("process_identity") + .and_then(Value::as_str) + .unwrap_or("") + .to_owned(); + if !process_is_gone(pid, &identity) { + continue; + } + let task_id = object + .get("id") + .and_then(Value::as_str) + .ok_or_else(|| StoreError::MutationRejected("task record has no ID".into()))? + .to_owned(); + let original_heartbeat = heartbeat.to_owned(); + let mut transitioned = false; + store.mutate_with_event("task", &task_id, |old| { + let mut next = old.clone(); + let object = as_object_mut(&mut next)?; + let current_status = required_string(object, "status")?; + let current_pid = object.get("pid").and_then(Value::as_i64); + let current_heartbeat = object.get("heartbeat").and_then(Value::as_str); + if is_terminal(current_status) + || current_pid != Some(i64::from(pid)) + || current_heartbeat != Some(original_heartbeat.as_str()) + { + return Ok((next, None)); + } + object.insert("status".into(), Value::String("LOST".into())); + object.insert( + "error".into(), + Value::String( + "supervisor is no longer present; workspace retained; no automatic restart" + .into(), + ), + ); + object.insert("updated_at".into(), Value::String(now_text.clone())); + transitioned = true; + Ok((next, Some("task.lost".into()))) + })?; + if transitioned { + // The process-presence check and conditional state transition + // establish that the old owner is gone. Releasing by exact task + // ID cannot free a lease belonging to a different worker. + store.release_resources(&task_id)?; + lost += 1; + } + } + Ok(lost) +} + +fn as_object_mut(value: &mut Value) -> Result<&mut Map, StoreError> { + value + .as_object_mut() + .ok_or_else(|| StoreError::MutationRejected("task record must be a JSON object".into())) +} + +fn required_string<'a>(object: &'a Map, key: &str) -> Result<&'a str, StoreError> { + object.get(key).and_then(Value::as_str).ok_or_else(|| { + StoreError::MutationRejected(format!("task record field {key} must be a string")) + }) +} + +fn optional_bool(object: &Map, key: &str) -> Result { + match object.get(key) { + None => Ok(false), + Some(Value::Bool(value)) => Ok(*value), + Some(_) => Err(StoreError::MutationRejected(format!( + "task record field {key} must be a boolean" + ))), + } +} + +fn parse_time(value: &str) -> Result { + OffsetDateTime::parse(value, &Rfc3339).map_err(|error| StoreError::Timestamp(error.to_string())) +} + +fn is_terminal(status: &str) -> bool { + matches!( + status, + "CANDIDATE_READY" + | "REVIEW_READY" + | "CHECKS_BLOCKED" + | "FAILED" + | "ERROR" + | "CANCELLED" + | "TIMED_OUT" + | "LOST" + ) +} + +#[cfg(target_os = "linux")] +fn linux_process_identity(pid: i32) -> Option { + if pid <= 0 { + return None; + } + let stat = fs::read_to_string(format!("/proc/{pid}/stat")).ok()?; + let fields = stat.get(stat.rfind(')')?.checked_add(1)?..)?; + let fields = fields.split_ascii_whitespace().collect::>(); + let start_time = fields.get(19)?; + let boot_id = fs::read_to_string("/proc/sys/kernel/random/boot_id").ok()?; + let boot_id = boot_id.trim(); + if boot_id.is_empty() || start_time.is_empty() { + return None; + } + Some(format!("{boot_id}:{pid}:{start_time}")) +} + +#[cfg(target_os = "linux")] +fn linux_process_is_zombie(pid: i32) -> bool { + let Ok(stat) = fs::read_to_string(format!("/proc/{pid}/stat")) else { + return false; + }; + let Some(fields) = stat + .rfind(')') + .and_then(|end| stat.get(end.saturating_add(1)..)) + else { + return false; + }; + fields.split_ascii_whitespace().next() == Some("Z") +} + +#[cfg(test)] +mod tests { + use super::{ + claim, definitely_gone, heartbeat, launch_detached, process_identity, + reconcile_with_presence, record_dispatch, request_cancel, start_heartbeat_at_interval, + ClaimOutcome, HeartbeatError, LaunchError, LaunchOptions, LaunchReceipt, + }; + use crate::CancellationToken; + use rover_store::{Store, StoreError}; + use rusqlite::Connection; + use serde_json::{json, Value}; + #[cfg(unix)] + use std::fs; + #[cfg(unix)] + use std::os::unix::fs::PermissionsExt; + #[cfg(unix)] + use std::path::{Path, PathBuf}; + #[cfg(unix)] + use std::process::Command; + #[cfg(unix)] + use std::sync::atomic::{AtomicU64, Ordering}; + use std::sync::Arc; + use std::thread; + use std::time::Duration; + + #[cfg(unix)] + static LAUNCH_TEMP_SEQUENCE: AtomicU64 = AtomicU64::new(0); + + #[cfg(unix)] + struct LaunchTemp(PathBuf); + + #[cfg(unix)] + impl LaunchTemp { + fn new() -> Self { + let sequence = LAUNCH_TEMP_SEQUENCE.fetch_add(1, Ordering::Relaxed); + let path = std::env::temp_dir().join(format!( + "rover-supervisor-{}-{sequence}", + std::process::id() + )); + fs::create_dir(&path).expect("create private temp root"); + fs::set_permissions(&path, fs::Permissions::from_mode(0o700)) + .expect("secure private temp root"); + Self(path) + } + + fn worker_dir(&self) -> PathBuf { + let path = self.0.join("task_launch"); + fs::create_dir(&path).expect("create worker log directory"); + fs::set_permissions(&path, fs::Permissions::from_mode(0o700)) + .expect("secure worker log directory"); + path + } + } + + #[cfg(unix)] + impl Drop for LaunchTemp { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.0); + } + } + + #[cfg(unix)] + fn launch_options(task_id: &str, dir: &Path, output_dir: &Path) -> LaunchOptions { + LaunchOptions { + task_id: task_id.into(), + argv: vec!["/bin/sleep".into(), "1".into()], + dir: dir.to_path_buf(), + output_dir: output_dir.to_path_buf(), + pass_env: Vec::new(), + } + } + + fn memory_store() -> Store { + Store::from_connection(Connection::open_in_memory().expect("open in-memory database")) + .expect("configure test store") + } + + fn queued_task(cancel_requested: bool, heartbeat: &str) -> Value { + json!({ + "schema": "rover/v1alpha1", + "id": "task_one", + "status": "QUEUED", + "workspace": "/state/tasks/task_one/workspace", + "heartbeat": heartbeat, + "pid": 0, + "cancel_requested": cancel_requested, + "updated_at": heartbeat, + }) + } + + fn put_task(store: &Store, task: &Value) { + store + .put("task", "task_one", task, "task.created") + .expect("insert task fixture"); + } + + fn task(store: &Store) -> Value { + store + .get("task", "task_one") + .expect("read task") + .as_value() + .clone() + } + + #[cfg(unix)] + fn insert_launch_task(store: &Store, output_dir: &Path) { + store + .put( + "task", + "task_launch", + &json!({ + "id": "task_launch", + "status": "QUEUED", + "workspace": output_dir.join("workspace"), + "cancel_requested": false, + }), + "task.created", + ) + .expect("insert launch task"); + } + + #[test] + fn current_process_is_not_reported_gone_without_identity() { + let pid = i32::try_from(std::process::id()).expect("test process ID fits i32"); + assert!(!definitely_gone(pid, "")); + } + + #[test] + fn invalid_process_ids_are_ambiguous() { + assert!(!definitely_gone(0, "")); + assert!(!definitely_gone(-1, "")); + } + + #[cfg(target_os = "linux")] + #[test] + fn linux_identity_binds_boot_id_pid_and_start_time() { + let pid = i32::try_from(std::process::id()).expect("test process ID fits i32"); + let identity = process_identity(pid).expect("Linux process identity is available"); + let parts = identity.split(':').collect::>(); + assert_eq!(parts.len(), 3); + assert_eq!(parts[1], pid.to_string()); + assert!(parts[0].contains('-')); + assert!(parts[2].parse::().is_ok()); + assert!(!definitely_gone(pid, &identity)); + assert!(definitely_gone(pid, "different-boot:1:1")); + } + + #[cfg(target_os = "linux")] + #[test] + fn absent_linux_process_is_definitely_gone() { + assert!(definitely_gone(i32::MAX, "")); + assert!(process_identity(i32::MAX).is_none()); + } + + #[cfg(target_os = "macos")] + #[test] + fn macos_keeps_live_pid_reuse_ambiguous() { + let pid = i32::try_from(std::process::id()).expect("test process ID fits i32"); + assert!(process_identity(pid).is_none()); + assert!(!definitely_gone(pid, "old-process-identity")); + } + + #[cfg(unix)] + #[test] + fn detached_launch_records_pid_and_uses_a_private_exclusive_log() { + let store = memory_store(); + let temp = LaunchTemp::new(); + let output_dir = temp.worker_dir(); + insert_launch_task(&store, &output_dir); + let options = launch_options("task_launch", &temp.0, &output_dir); + + let receipt = launch_detached(&store, &options).expect("launch worker"); + assert!(receipt.pid > 0); + let record = store + .get("task", "task_launch") + .expect("read dispatched task"); + assert_eq!(record["status"], "QUEUED"); + assert_eq!(record["pid"], receipt.pid); + assert_eq!( + record["process_identity"].as_str(), + receipt.process_identity.as_deref() + ); + assert!(record["heartbeat"].as_str().is_some()); + assert_eq!( + store.events("task_launch").expect("events").last().unwrap()["event"], + "task.dispatched" + ); + let log = fs::metadata(output_dir.join("supervisor.log")).expect("supervisor log"); + assert_eq!(log.permissions().mode() & 0o777, 0o600); + assert!(matches!( + launch_detached(&store, &options), + Err(LaunchError::Io(_)) + )); + assert!(!definitely_gone( + receipt.pid, + receipt.process_identity.as_deref().unwrap_or("") + )); + for _ in 0..300 { + if definitely_gone( + receipt.pid, + receipt.process_identity.as_deref().unwrap_or(""), + ) { + break; + } + thread::sleep(Duration::from_millis(10)); + } + assert!(definitely_gone( + receipt.pid, + receipt.process_identity.as_deref().unwrap_or("") + )); + assert_eq!(store.events("task_launch").expect("events").len(), 2); + } + + #[cfg(unix)] + #[test] + fn detached_worker_stdout_and_stderr_share_the_supervisor_log() { + let store = memory_store(); + let temp = LaunchTemp::new(); + let output_dir = temp.worker_dir(); + insert_launch_task(&store, &output_dir); + let mut options = launch_options("task_launch", &temp.0, &output_dir); + options.argv = vec!["/usr/bin/printf".into(), "worker-output".into()]; + + let receipt = launch_detached(&store, &options).expect("launch worker"); + for _ in 0..100 { + if definitely_gone(receipt.pid, "") { + break; + } + thread::sleep(Duration::from_millis(10)); + } + assert!(definitely_gone(receipt.pid, "")); + assert_eq!( + fs::read(output_dir.join("supervisor.log")).expect("read worker log"), + b"worker-output" + ); + } + + #[cfg(unix)] + #[test] + fn detached_worker_survives_the_launcher_process_exit() { + let temp = LaunchTemp::new(); + let helper = Command::new(std::env::current_exe().expect("test executable")) + .args([ + "--exact", + "supervisor::tests::detached_worker_parent_exit_fixture", + ]) + .env("ROVER_SUPERVISOR_PARENT_EXIT_FIXTURE", &temp.0) + .output() + .expect("run subprocess fixture"); + assert!( + helper.status.success(), + "subprocess fixture failed: {}", + String::from_utf8_lossy(&helper.stderr) + ); + let pid: i32 = fs::read_to_string(temp.0.join("worker.pid")) + .expect("subprocess writes worker PID") + .parse() + .expect("worker PID is numeric"); + assert!( + !definitely_gone(pid, ""), + "worker should survive after the launcher process exits" + ); + for _ in 0..400 { + if definitely_gone(pid, "") { + break; + } + thread::sleep(Duration::from_millis(10)); + } + assert!(definitely_gone(pid, "")); + } + + #[cfg(unix)] + #[test] + fn detached_worker_parent_exit_fixture() { + let Some(root) = std::env::var_os("ROVER_SUPERVISOR_PARENT_EXIT_FIXTURE") else { + return; + }; + let root = PathBuf::from(root); + let output_dir = root.join("task_launch"); + fs::create_dir(&output_dir).expect("create child task directory"); + fs::set_permissions(&output_dir, fs::Permissions::from_mode(0o700)) + .expect("secure child task directory"); + let store = memory_store(); + insert_launch_task(&store, &output_dir); + let mut options = launch_options("task_launch", &root, &output_dir); + options.argv = vec!["/bin/sleep".into(), "2".into()]; + let receipt = launch_detached(&store, &options).expect("launch detached child"); + fs::write(root.join("worker.pid"), receipt.pid.to_string()) + .expect("persist child PID for parent test"); + } + + #[cfg(unix)] + #[test] + fn failed_worker_spawn_is_persisted_as_error() { + let store = memory_store(); + let temp = LaunchTemp::new(); + let output_dir = temp.worker_dir(); + insert_launch_task(&store, &output_dir); + let mut options = launch_options("task_launch", &temp.0, &output_dir); + options.argv[0] = "/definitely/missing/rover-worker".into(); + + assert!(matches!( + launch_detached(&store, &options), + Err(LaunchError::Io(_)) + )); + let record = store + .get("task", "task_launch") + .expect("read failed dispatch"); + assert_eq!(record["status"], "ERROR"); + assert!(record["error"] + .as_str() + .unwrap() + .contains("supervisor start failed")); + assert_eq!( + store.events("task_launch").expect("events").last().unwrap()["event"], + "task.dispatch_failed" + ); + } + + #[cfg(unix)] + #[test] + fn invalid_or_already_logged_launch_does_not_start_a_worker() { + let store = memory_store(); + let temp = LaunchTemp::new(); + let output_dir = temp.worker_dir(); + insert_launch_task(&store, &output_dir); + let mut options = launch_options("task_launch", &temp.0, &output_dir); + options.argv[0] = "true".into(); + assert!(matches!( + launch_detached(&store, &options), + Err(LaunchError::Invalid(_)) + )); + + options.argv = vec!["/bin/sleep".into(), "1".into()]; + fs::write(output_dir.join("supervisor.log"), b"keep").expect("precreate log"); + fs::set_permissions( + output_dir.join("supervisor.log"), + fs::Permissions::from_mode(0o600), + ) + .expect("secure preexisting log"); + assert!(matches!( + launch_detached(&store, &options), + Err(LaunchError::Io(_)) + )); + assert_eq!( + fs::read(output_dir.join("supervisor.log")).unwrap(), + b"keep" + ); + assert_eq!( + store.get("task", "task_launch").unwrap()["status"], + "QUEUED" + ); + assert_eq!(store.events("task_launch").expect("events").len(), 1); + } + + #[test] + fn claim_is_one_way_and_persists_process_ownership() { + let store = memory_store(); + let now = "2026-09-25T12:00:00Z"; + put_task(&store, &queued_task(false, now)); + + assert_eq!( + claim(&store, "task_one", 1234, Some("host:1234:boot"), now).expect("claim"), + ClaimOutcome::Claimed + ); + let stored = task(&store); + assert_eq!(stored["status"], "PREPARING"); + assert_eq!(stored["pid"], 1234); + assert_eq!(stored["process_identity"], "host:1234:boot"); + assert_eq!(stored["heartbeat"], now); + assert!(matches!( + claim(&store, "task_one", 1235, None, now), + Err(StoreError::MutationRejected(_)) + )); + assert_eq!(store.events("task_one").expect("events").len(), 2); + } + + #[test] + fn claim_honors_cancellation_before_launch() { + let store = memory_store(); + let now = "2026-09-25T12:00:00Z"; + put_task(&store, &queued_task(true, now)); + + assert_eq!( + claim(&store, "task_one", 1234, None, now).expect("claim cancelled task"), + ClaimOutcome::Cancelled + ); + let stored = task(&store); + assert_eq!(stored["status"], "CANCELLED"); + assert_eq!(stored["pid"], 0); + assert_eq!(store.events("task_one").expect("events").len(), 2); + } + + #[cfg(unix)] + #[test] + fn dispatch_recording_preserves_a_worker_that_claimed_first() { + let store = memory_store(); + let temp = LaunchTemp::new(); + let output_dir = temp.worker_dir(); + insert_launch_task(&store, &output_dir); + let claim_time = "2026-09-25T12:00:00Z"; + assert_eq!( + claim( + &store, + "task_launch", + 4321, + Some("child-identity"), + claim_time, + ) + .expect("worker claim"), + ClaimOutcome::Claimed + ); + + record_dispatch( + &store, + "task_launch", + &LaunchReceipt { + pid: 1234, + process_identity: Some("parent-dispatch-identity".into()), + }, + ) + .expect("record late parent dispatch"); + let record = store + .get("task", "task_launch") + .expect("read task after racing claim"); + assert_eq!(record["status"], "PREPARING"); + assert_eq!(record["pid"], 4321); + assert_eq!(record["process_identity"], "child-identity"); + assert_eq!(record["heartbeat"], claim_time); + assert_eq!(store.events("task_launch").expect("events").len(), 3); + } + + #[test] + fn malformed_cancel_state_fails_closed() { + let store = memory_store(); + let now = "2026-09-25T12:00:00Z"; + let mut value = queued_task(false, now); + value["cancel_requested"] = Value::String("false".into()); + put_task(&store, &value); + + assert!(matches!( + claim(&store, "task_one", 1234, None, now), + Err(StoreError::MutationRejected(_)) + )); + assert_eq!(task(&store)["status"], "QUEUED"); + assert_eq!(store.events("task_one").expect("events").len(), 1); + } + + #[test] + fn heartbeat_observes_cancel_request_without_appending_events() { + let store = memory_store(); + let original = "2026-09-25T12:00:00Z"; + let now = "2026-09-25T12:00:01Z"; + let mut value = queued_task(false, original); + value["status"] = Value::String("RUNNING".into()); + value["pid"] = json!(1234); + put_task(&store, &value); + request_cancel(&store, "task_one", now).expect("request cancellation"); + let events_before_heartbeat = store.events("task_one").expect("events").len(); + + assert!(heartbeat(&store, "task_one", "2026-09-25T12:00:02Z").expect("heartbeat")); + let stored = task(&store); + assert_eq!(stored["heartbeat"], "2026-09-25T12:00:02Z"); + assert_eq!( + store.events("task_one").expect("events").len(), + events_before_heartbeat + ); + } + + #[test] + fn heartbeat_guard_refreshes_state_and_observes_cancellation() { + let store = Arc::new(memory_store()); + let initial = "2026-09-25T12:00:00Z"; + put_task(&store, &queued_task(false, initial)); + let cancellation = CancellationToken::default(); + let guard = start_heartbeat_at_interval( + Arc::clone(&store), + "task_one".into(), + cancellation.clone(), + Duration::from_millis(5), + ) + .expect("start heartbeat loop"); + + for _ in 0..100 { + if task(&store)["heartbeat"] != initial { + break; + } + thread::sleep(Duration::from_millis(2)); + } + assert_ne!(task(&store)["heartbeat"], initial); + request_cancel(&store, "task_one", "2026-09-25T12:00:01Z").expect("request cancellation"); + for _ in 0..100 { + if cancellation.is_cancelled() { + break; + } + thread::sleep(Duration::from_millis(2)); + } + assert!(cancellation.is_cancelled()); + guard.stop().expect("stop healthy heartbeat"); + } + + #[test] + fn heartbeat_persistence_failure_cancels_worker_and_is_reported() { + let store = Arc::new(memory_store()); + put_task(&store, &queued_task(false, "2026-09-25T12:00:00Z")); + let cancellation = CancellationToken::default(); + let guard = start_heartbeat_at_interval( + Arc::clone(&store), + "task_one".into(), + cancellation.clone(), + Duration::from_millis(5), + ) + .expect("start heartbeat loop"); + store + .delete("task", "task_one", "") + .expect("remove task to simulate lost state"); + for _ in 0..100 { + if cancellation.is_cancelled() { + break; + } + thread::sleep(Duration::from_millis(2)); + } + assert!(cancellation.is_cancelled()); + let error = guard + .stop() + .expect_err("heartbeat should report missing task"); + assert!(matches!(error, HeartbeatError::Store(StoreError::NotFound))); + } + + #[test] + fn reconciliation_requires_staleness_and_definite_process_loss() { + let store = memory_store(); + let now = "2026-09-25T12:00:20Z"; + let mut value = queued_task(false, "2026-09-25T12:00:11Z"); + value["status"] = Value::String("RUNNING".into()); + value["pid"] = json!(4321); + value["process_identity"] = Value::String("old-host:4321:birth".into()); + put_task(&store, &value); + + assert_eq!( + reconcile_with_presence(&store, now, |_, _| panic!("fresh task must not be checked")) + .expect("reconcile fresh task"), + 0 + ); + assert_eq!(task(&store)["status"], "RUNNING"); + + let stale_time = "2026-09-25T12:00:10Z"; + store + .mutate("task", "task_one", "", |old| { + let mut next = old.clone(); + next["heartbeat"] = Value::String(stale_time.into()); + Ok(next) + }) + .expect("age heartbeat"); + assert_eq!( + reconcile_with_presence(&store, now, |pid, identity| { + assert_eq!(pid, 4321); + assert_eq!(identity, "old-host:4321:birth"); + false + }) + .expect("ambiguous reconciliation"), + 0 + ); + assert_eq!(task(&store)["status"], "RUNNING"); + + let events_before_race = store.events("task_one").expect("events").len(); + assert_eq!( + reconcile_with_presence(&store, now, |_, _| { + store + .mutate("task", "task_one", "", |old| { + let mut next = old.clone(); + next["heartbeat"] = Value::String(now.into()); + Ok(next) + }) + .expect("concurrent heartbeat"); + true + }) + .expect("reconcile after heartbeat race"), + 0 + ); + assert_eq!(task(&store)["status"], "RUNNING"); + assert_eq!( + store.events("task_one").expect("events").len(), + events_before_race + ); + store + .mutate("task", "task_one", "", |old| { + let mut next = old.clone(); + next["heartbeat"] = Value::String(stale_time.into()); + Ok(next) + }) + .expect("restore stale heartbeat fixture"); + + assert_eq!( + reconcile_with_presence(&store, now, |_, _| true) + .expect("definite-loss reconciliation"), + 1 + ); + let stored = task(&store); + assert_eq!(stored["status"], "LOST"); + assert!(stored["workspace"].as_str().is_some()); + assert!(stored["error"] + .as_str() + .unwrap() + .contains("no automatic restart")); + assert_eq!( + store.events("task_one").expect("events").last().unwrap()["event"], + "task.lost" + ); + } + + #[test] + fn reconciliation_releases_only_a_confirmed_lost_tasks_resources() { + let store = memory_store(); + let now = "2026-09-25T12:00:20Z"; + let stale = "2026-09-25T12:00:10Z"; + let mut value = queued_task(false, stale); + value["status"] = Value::String("RUNNING".into()); + value["pid"] = json!(4321); + put_task(&store, &value); + let resources = vec!["database_one".to_owned()]; + store + .acquire_resources("task_one", &resources, 4) + .expect("reserve task resources"); + + assert_eq!( + reconcile_with_presence(&store, now, |_, _| false).expect("preserve ambiguous owner"), + 0 + ); + assert!(matches!( + store.acquire_resources("task_two", &resources, 4), + Err(rover_store::resources::ResourceError::Busy) + )); + + assert_eq!( + reconcile_with_presence(&store, now, |_, _| true) + .expect("reconcile definitely absent process"), + 1 + ); + assert_eq!(task(&store)["status"], "LOST"); + store + .acquire_resources("task_two", &resources, 4) + .expect("confirmed loss releases the old owner's reservations"); + + // A repeated pass retries owner-scoped cleanup for a LOST task. + assert_eq!( + reconcile_with_presence(&store, now, |_, _| panic!("terminal task is not probed")) + .expect("repeat lost cleanup"), + 0 + ); + assert!(matches!( + store.acquire_resources("task_one", &resources, 4), + Err(rover_store::resources::ResourceError::Busy) + )); + } + + #[test] + fn terminal_tasks_cannot_be_cancelled_or_revived_by_heartbeat() { + let store = memory_store(); + let now = "2026-09-25T12:00:00Z"; + let mut value = queued_task(false, now); + value["status"] = Value::String("FAILED".into()); + put_task(&store, &value); + + assert!(matches!( + request_cancel(&store, "task_one", now), + Err(StoreError::MutationRejected(_)) + )); + assert!(!heartbeat(&store, "task_one", "2026-09-25T12:00:01Z").expect("terminal heartbeat")); + assert_eq!(task(&store)["status"], "FAILED"); + assert_eq!(task(&store)["heartbeat"], now); + } +} diff --git a/crates/rover-execution/src/terminal.rs b/crates/rover-execution/src/terminal.rs new file mode 100644 index 0000000..788b6fe --- /dev/null +++ b/crates/rover-execution/src/terminal.rs @@ -0,0 +1,537 @@ +//! Bounded screen model for untrusted local PTY output. +//! +//! The terminal core interprets VT control sequences into cells. Rover exports +//! only filtered cell text plus a coalesced bell signal; terminal sequences +//! never reach the TUI renderer. + +use std::io; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex}; + +use alacritty_terminal::event::{Event, EventListener}; +use alacritty_terminal::grid::Dimensions; +use alacritty_terminal::index::{Column, Line, Point}; +use alacritty_terminal::term::{Config, Osc52, Term}; +use alacritty_terminal::vte::ansi::Processor; + +const MIN_COLUMNS: usize = 2; +const MAX_ROWS: usize = 1000; +const MAX_COLUMNS: usize = 1000; +const MAX_VISIBLE_CELLS: usize = 250_000; +const MAX_SCROLLBACK_LINES: usize = 10_000; +const MAX_SCROLLBACK_CELLS: usize = 1_000_000; +const MAX_OUTPUT_CHUNK: usize = 64 * 1024; +const MAX_OSC_TITLE_BYTES: usize = 4096; +const MAX_OSC_PROGRESS_BYTES: usize = 4096; + +/// One validated terminal viewport size. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct TerminalSize { + /// Number of visible rows. + pub rows: usize, + /// Number of visible columns. + pub columns: usize, +} + +impl TerminalSize { + /// Validate and construct a viewport size. + /// + /// # Errors + /// + /// Returns `InvalidInput` for dimensions outside the supported bounds or + /// for viewports exceeding the cell budget. + pub fn new(rows: usize, columns: usize) -> io::Result { + validate_size(rows, columns)?; + Ok(Self { rows, columns }) + } +} + +/// VT screen emulator with bounded viewport and scrollback storage. +pub struct TerminalScreen { + terminal: Term, + parser: Processor, + size: TerminalSize, + requested_scrollback: usize, + history_limit: usize, + bell: Arc, + osc_title: Arc>>, + osc_progress: OscProgressCapture, +} + +#[derive(Clone)] +struct BellListener { + bell: Arc, + osc_title: Arc>>, +} + +#[derive(Default)] +struct OscProgressCapture { + payload: Option>, + pending_escape: bool, + progress: Option, +} + +impl OscProgressCapture { + fn advance(&mut self, bytes: &[u8]) { + for &byte in bytes { + if self.payload.is_none() { + if self.pending_escape { + self.pending_escape = false; + if byte == b']' { + self.payload = Some(Vec::new()); + } else if byte == 0x1b { + self.pending_escape = true; + } + } else if byte == 0x1b { + self.pending_escape = true; + } + continue; + } + + if byte == 0x07 || byte == 0x9c || (self.pending_escape && byte == b'\\') { + let completed = self.payload.take().unwrap_or_default(); + self.pending_escape = false; + self.update_progress(&completed); + continue; + } + if self.pending_escape { + self.payload = if byte == b']' { Some(Vec::new()) } else { None }; + self.pending_escape = false; + self.progress = None; + continue; + } + if byte == 0x1b { + self.pending_escape = true; + continue; + } + if byte < 0x20 { + self.payload = None; + self.progress = None; + continue; + } + let Some(payload) = self.payload.as_mut() else { + continue; + }; + if payload.len() >= MAX_OSC_PROGRESS_BYTES { + self.payload = None; + self.progress = None; + continue; + } + payload.push(byte); + } + } + + fn update_progress(&mut self, payload: &[u8]) { + let Some(value) = payload.strip_prefix(b"9;") else { + return; + }; + if !value.starts_with(b"4;") { + return; + } + if !value + .iter() + .all(|byte| byte.is_ascii_digit() || matches!(byte, b';' | b'-' | b'?')) + { + self.progress = None; + return; + } + self.progress = Some(String::from_utf8_lossy(value).into_owned()); + } +} + +impl EventListener for BellListener { + fn send_event(&self, event: Event) { + match event { + Event::Bell => self.bell.store(true, Ordering::Relaxed), + Event::Title(title) if !title.is_empty() && title.len() <= MAX_OSC_TITLE_BYTES => { + *self + .osc_title + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(title); + } + Event::Title(_) | Event::ResetTitle => { + *self + .osc_title + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) = None; + } + _ => {} + } + } +} + +impl TerminalScreen { + /// Create a screen with at most `scrollback_lines` history rows. + /// + /// The effective history cap is reduced for wide terminals so the retained + /// scrollback never exceeds the configured cell budget. OSC 52 clipboard + /// requests and all outward terminal events except a coalesced bell are + /// disabled. + /// + /// # Errors + /// + /// Returns `InvalidInput` for invalid size or a history request above the + /// supported 10,000-line maximum. + pub fn new(size: TerminalSize, scrollback_lines: usize) -> io::Result { + validate_size(size.rows, size.columns)?; + if scrollback_lines > MAX_SCROLLBACK_LINES { + return Err(invalid_input("scrollback exceeds 10000 lines")); + } + let history_limit = history_capacity(scrollback_lines, size.columns); + let config = Config { + scrolling_history: history_limit, + osc52: Osc52::Disabled, + ..Config::default() + }; + let dimensions = GridSize::from(size); + let bell = Arc::new(AtomicBool::new(false)); + let osc_title = Arc::new(Mutex::new(None)); + let terminal = Term::new( + config, + &dimensions, + BellListener { + bell: Arc::clone(&bell), + osc_title: Arc::clone(&osc_title), + }, + ); + Ok(Self { + terminal, + parser: Processor::new(), + size, + requested_scrollback: scrollback_lines, + history_limit, + bell, + osc_title, + osc_progress: OscProgressCapture::default(), + }) + } + + /// Parse one bounded byte chunk from the PTY into terminal screen state. + /// + /// # Errors + /// + /// Returns `InvalidInput` if one chunk exceeds 64 KiB. + pub fn process_output(&mut self, bytes: &[u8]) -> io::Result<()> { + if bytes.len() > MAX_OUTPUT_CHUNK { + return Err(invalid_input("PTY output chunk exceeds 64 KiB")); + } + self.osc_progress.advance(bytes); + self.parser.advance(&mut self.terminal, bytes); + Ok(()) + } + + /// Resize the viewport while maintaining the bounded history budget. + /// + /// # Errors + /// + /// Returns `InvalidInput` for unsupported dimensions or a viewport above + /// the cell budget. + pub fn resize(&mut self, size: TerminalSize) -> io::Result<()> { + validate_size(size.rows, size.columns)?; + let config = Config { + scrolling_history: history_capacity(self.requested_scrollback, size.columns), + osc52: Osc52::Disabled, + ..Config::default() + }; + self.terminal.set_options(config); + self.terminal.resize(GridSize::from(size)); + self.size = size; + self.history_limit = history_capacity(self.requested_scrollback, size.columns); + Ok(()) + } + + /// Current viewport dimensions. + #[must_use] + pub const fn size(&self) -> TerminalSize { + self.size + } + + /// Number of retained history rows after applying the cell budget. + #[must_use] + pub fn history_size(&self) -> usize { + self.terminal.grid().history_size() + } + + /// Maximum history rows retained for the current width. + #[must_use] + pub const fn history_limit(&self) -> usize { + self.history_limit + } + + /// Consume one or more terminal bell events since the previous call. + /// Multiple events are coalesced to prevent a stalled UI from flooding + /// the host terminal with bell requests. + #[must_use] + pub fn take_bell(&self) -> bool { + self.bell.swap(false, Ordering::Relaxed) + } + + /// Plain, filtered text from history and the visible screen. + /// + /// This contains cell characters only; ANSI/VT sequences and hyperlink + /// targets are not returned. Control and bidirectional formatting + /// characters are replaced before callers render or export the text. + #[must_use] + pub fn text(&self) -> String { + let grid = self.terminal.grid(); + let start = Point::new(grid.topmost_line(), Column(0)); + let end = Point::new(grid.bottommost_line(), Column(grid.columns())); + self.terminal + .bounds_to_string(start, end) + .chars() + .map(safe_text_character) + .collect() + } + + /// Plain, filtered text from the live viewport only, excluding scrollback. + #[must_use] + pub fn visible_text(&self) -> String { + let grid = self.terminal.grid(); + let last = grid.bottommost_line(); + let visible_rows = i32::try_from(self.size.rows).unwrap_or_default(); + let first = Line(last.0 - visible_rows + 1); + self.terminal + .bounds_to_string( + Point::new(first, Column(0)), + Point::new(last, Column(grid.columns())), + ) + .chars() + .map(safe_text_character) + .collect() + } + + /// Current OSC window title, if set and within the evidence limit. + #[must_use] + pub fn osc_title(&self) -> Option { + self.osc_title + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone() + } + + /// Most recently observed bounded OSC 9;4 payload, without the OSC + /// command prefix. Other OSC commands are ignored. + #[must_use] + pub fn osc_progress(&self) -> Option<&str> { + self.osc_progress.progress.as_deref() + } +} + +#[derive(Clone, Copy)] +struct GridSize { + rows: usize, + columns: usize, +} + +impl From for GridSize { + fn from(size: TerminalSize) -> Self { + Self { + rows: size.rows, + columns: size.columns, + } + } +} + +impl Dimensions for GridSize { + fn total_lines(&self) -> usize { + self.rows + } + + fn screen_lines(&self) -> usize { + self.rows + } + + fn columns(&self) -> usize { + self.columns + } +} + +fn validate_size(rows: usize, columns: usize) -> io::Result<()> { + let cells = rows.checked_mul(columns); + if rows == 0 + || rows > MAX_ROWS + || !(MIN_COLUMNS..=MAX_COLUMNS).contains(&columns) + || cells.is_none_or(|count| count > MAX_VISIBLE_CELLS) + { + return Err(invalid_input( + "terminal dimensions exceed supported cell limits", + )); + } + Ok(()) +} + +fn history_capacity(requested_lines: usize, columns: usize) -> usize { + requested_lines.min(MAX_SCROLLBACK_CELLS / columns) +} + +fn safe_text_character(character: char) -> char { + let codepoint = u32::from(character); + let bidi_format = matches!( + codepoint, + 0x061C | 0x200E..=0x200F | 0x202A..=0x202E | 0x2066..=0x2069 | 0xFEFF + ); + if bidi_format || (character.is_control() && !matches!(character, '\n' | '\t')) { + '\u{FFFD}' + } else { + character + } +} + +fn invalid_input(message: &str) -> io::Error { + io::Error::new(io::ErrorKind::InvalidInput, message) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_text_and_styling_without_returning_terminal_controls() { + let mut screen = TerminalScreen::new(TerminalSize::new(4, 40).unwrap(), 20).unwrap(); + screen + .process_output(b"\x1b[31mred\x1b[0m visible\r\n") + .unwrap(); + let text = screen.text(); + assert!(text.contains("red visible")); + assert!(!text.contains('\x1b')); + } + + #[test] + fn live_viewport_excludes_scrollback_and_osc_title_is_bounded() { + let mut screen = TerminalScreen::new(TerminalSize::new(2, 20).unwrap(), 10).unwrap(); + screen.process_output(b"first\r\nsecond\r\nthird").unwrap(); + assert!(screen.text().contains("first")); + let visible = screen.visible_text(); + assert!(!visible.contains("first")); + assert!(visible.contains("second")); + assert!(visible.contains("third")); + + screen.process_output(b"\x1b]0;agent title\x07").unwrap(); + assert_eq!(screen.osc_title().as_deref(), Some("agent title")); + screen.process_output(b"\x1b]0;\x07").unwrap(); + assert_eq!(screen.osc_title(), None); + } + + #[test] + fn osc_9_4_progress_capture_handles_fragmented_bel_and_st_terminators() { + let mut screen = TerminalScreen::new(TerminalSize::new(2, 20).unwrap(), 0).unwrap(); + screen.process_output(b"\x1b]9;4;1;50").unwrap(); + assert_eq!(screen.osc_progress(), None); + screen.process_output(b"\x07").unwrap(); + assert_eq!(screen.osc_progress(), Some("4;1;50")); + + screen.process_output(b"\x1b]9;5;ignored\x07").unwrap(); + assert_eq!(screen.osc_progress(), Some("4;1;50")); + + screen.process_output(b"\x1b]9;4;3;?\x1b\\").unwrap(); + assert_eq!(screen.osc_progress(), Some("4;3;?")); + + screen.process_output(b"\x1b]9;4;invalid\x07").unwrap(); + assert_eq!(screen.osc_progress(), None); + + let oversize = format!("\x1b]9;4;{}\x07", "1".repeat(MAX_OSC_PROGRESS_BYTES)); + screen.process_output(oversize.as_bytes()).unwrap(); + assert_eq!(screen.osc_progress(), None); + } + + #[test] + fn clipboard_title_query_and_device_control_sequences_have_no_outward_effect() { + let mut screen = TerminalScreen::new(TerminalSize::new(4, 40).unwrap(), 20).unwrap(); + screen + .process_output( + b"before\x1b]52;c;U0VDUkVU\x07\x1b]0;untrusted title\x07\x1b[6n\x1bP1;2|device payload\x1b\\after", + ) + .unwrap(); + let text = screen.text(); + assert!(text.contains("before")); + assert!(text.contains("after")); + assert!(!text.contains("SECRET")); + assert!(!text.contains("untrusted title")); + assert!(!text.contains("device payload")); + assert!(!text.contains('\x1b')); + assert!(!screen.take_bell()); + } + + #[test] + fn terminal_bell_is_reported_once_and_repeated_bells_are_coalesced() { + let mut screen = TerminalScreen::new(TerminalSize::new(4, 40).unwrap(), 20).unwrap(); + screen.process_output(b"text\x07\x07").unwrap(); + assert!(screen.take_bell()); + assert!(!screen.take_bell()); + screen.process_output(b"\x1b]0;title\x07").unwrap(); + assert!(!screen.take_bell()); + } + + #[test] + fn scrollback_is_capped_by_requested_lines_and_cell_budget() { + let mut screen = TerminalScreen::new(TerminalSize::new(2, 20).unwrap(), 5).unwrap(); + for index in 0..12 { + screen + .process_output(format!("line-{index:02}\r\n").as_bytes()) + .unwrap(); + } + assert!(screen.history_size() <= 5); + let text = screen.text(); + assert!(!text.contains("line-00")); + assert!(text.contains("line-11")); + + let wide = TerminalScreen::new(TerminalSize::new(2, 1000).unwrap(), 10_000).unwrap(); + assert_eq!(wide.history_limit(), MAX_SCROLLBACK_CELLS / 1000); + } + + #[test] + fn resize_reduces_retained_history_to_the_new_cell_budget() { + let mut screen = TerminalScreen::new(TerminalSize::new(2, 20).unwrap(), 5000).unwrap(); + for index in 0..1200 { + screen + .process_output(format!("row-{index:03}\r\n").as_bytes()) + .unwrap(); + } + assert!(screen.history_size() > 1000); + + screen.resize(TerminalSize::new(2, 1000).unwrap()).unwrap(); + + assert_eq!(screen.history_limit(), 1000); + assert!(screen.history_size() <= screen.history_limit()); + assert!(screen.text().contains("row-1199")); + } + + #[test] + fn shrinking_after_wide_glyphs_and_hostile_erase_sequences_does_not_panic() { + let mut screen = TerminalScreen::new(TerminalSize::new(2, 8).unwrap(), 8).unwrap(); + screen + .process_output("ab界\r\nwide text".as_bytes()) + .unwrap(); + screen.resize(TerminalSize::new(2, 2).unwrap()).unwrap(); + screen.process_output(b"\x1b[K\x1b[2Jsafe").unwrap(); + assert!(screen.text().contains("safe")); + assert_eq!( + screen.size(), + TerminalSize { + rows: 2, + columns: 2 + } + ); + } + + #[test] + fn screen_dimensions_and_input_chunks_are_bounded() { + assert!(TerminalSize::new(1000, 1000).is_err()); + assert!(TerminalSize::new(1, 1).is_err()); + assert!(TerminalSize::new(0, 80).is_err()); + + let mut screen = TerminalScreen::new(TerminalSize::new(24, 80).unwrap(), 100).unwrap(); + assert!(screen + .process_output(&vec![b'x'; MAX_OUTPUT_CHUNK + 1]) + .is_err()); + assert!(TerminalScreen::new(TerminalSize::new(24, 80).unwrap(), 10_001).is_err()); + } + + #[test] + fn bidi_controls_are_replaced_in_exported_text() { + let mut screen = TerminalScreen::new(TerminalSize::new(2, 40).unwrap(), 0).unwrap(); + screen.process_output("safe\u{202e}txt".as_bytes()).unwrap(); + assert!(screen.text().contains("safe�txt")); + assert!(!screen.text().contains('\u{202e}')); + } +} diff --git a/crates/rover-source/Cargo.toml b/crates/rover-source/Cargo.toml new file mode 100644 index 0000000..2f00334 --- /dev/null +++ b/crates/rover-source/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "rover-source" +version = "0.1.0" +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[dependencies] +rover-core = { path = "../rover-core" } +serde = { version = "=1.0.229", default-features = false, features = ["std", "derive"] } +serde_json = { version = "=1.0.151", default-features = false, features = ["std"] } + +[target.'cfg(unix)'.dependencies] +rover-store = { path = "../rover-store" } + +[lints] +workspace = true diff --git a/crates/rover-source/src/file_browser.rs b/crates/rover-source/src/file_browser.rs new file mode 100644 index 0000000..548f99a --- /dev/null +++ b/crates/rover-source/src/file_browser.rs @@ -0,0 +1,593 @@ +//! Rooted repository file browsing and editing for the Rust CLI/TUI. + +use std::collections::{BTreeMap, BTreeSet}; +use std::io; +use std::path::{Path, PathBuf}; + +use rover_core::Sha256Digest; +use rover_store::files::{SafeDir, SafeEntryKind}; + +use crate::git; + +/// Maximum entries returned by one directory listing or quick-open scan. +pub const MAX_BROWSER_ENTRIES: usize = 10_000; +/// Maximum file preview bytes returned to the TUI. +pub const MAX_FILE_PREVIEW_BYTES: usize = 256 * 1024; +const MAX_BROWSER_DEPTH: usize = 48; + +/// Filesystem kind presented by the read-only browser. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum BrowserEntryKind { + /// Regular file, eligible for a bounded text preview. + File, + /// Directory, eligible for navigation. + Directory, + /// Symlink, displayed but never traversed or opened. + Symlink, + /// Special filesystem object, displayed but never opened. + Other, +} + +/// Git worktree status used for file-tree coloring and labels. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum BrowserGitStatus { + /// Added in the index or worktree. + Added, + /// Modified or type-changed. + Modified, + /// Deleted from the index or worktree. + Deleted, + /// Renamed or copied according to porcelain status. + Renamed, + /// Untracked by Git. + Untracked, + /// Conflict stages are present. + Conflict, +} + +/// One repository-relative path in a browser result. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BrowserEntry { + /// Slash-separated repository-relative path. + pub path: String, + /// No-follow filesystem kind. + pub kind: BrowserEntryKind, + /// Git worktree status; directories aggregate descendant status. + pub git_status: Option, +} + +/// Bounded direct-child file-tree listing. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BrowserListing { + /// Sorted entries; directories sort before non-directories. + pub entries: Vec, + /// More eligible entries existed than the configured result limit. + pub truncated: bool, + /// Git status could not be read or parsed; filesystem browsing remains available. + pub git_error: Option, +} + +/// Read one directory in a canonical repository without following links. +/// +/// # Errors +/// +/// Returns an error for an invalid project path, invalid relative directory, +/// unsafe directory entry, or filesystem read failure. +pub fn browse_directory( + repository: &Path, + relative_directory: &str, + include_hidden: bool, +) -> io::Result { + let root = open_repository(repository)?; + let (status_map, git_error) = load_git_status(repository); + let (raw_entries, truncated) = root.list_directory_path( + relative_directory, + &enumeration_path(repository, relative_directory)?, + include_hidden, + MAX_BROWSER_ENTRIES, + )?; + let mut entries = raw_entries + .into_iter() + .map(|entry| { + let path = join_relative(relative_directory, &entry.name); + let git_status = status_for_path(&status_map, &path, entry.kind); + BrowserEntry { + path, + kind: browser_kind(entry.kind), + git_status, + } + }) + .collect::>(); + let existing = entries + .iter() + .map(|entry| entry.path.clone()) + .collect::>(); + let prefix = if relative_directory.is_empty() { + String::new() + } else { + format!("{relative_directory}/") + }; + let mut truncated = truncated; + for (path, status) in &status_map { + let Some(name) = path.strip_prefix(&prefix) else { + continue; + }; + if *status != BrowserGitStatus::Deleted + || name.is_empty() + || name.contains('/') + || (!include_hidden && name.starts_with('.')) + || existing.contains(path) + { + continue; + } + if entries.len() >= MAX_BROWSER_ENTRIES { + truncated = true; + break; + } + entries.push(BrowserEntry { + path: path.clone(), + kind: BrowserEntryKind::File, + git_status: Some(BrowserGitStatus::Deleted), + }); + } + entries.sort_by(|left, right| { + (left.kind != BrowserEntryKind::Directory) + .cmp(&(right.kind != BrowserEntryKind::Directory)) + .then_with(|| left.path.cmp(&right.path)) + }); + Ok(BrowserListing { + entries, + truncated, + git_error, + }) +} + +/// Enumerate repository files for hidden-file quick-open, excluding links, +/// special files, `.git`, and trees deeper than the explicit bound. +/// +/// # Errors +/// +/// Returns an error for an invalid project path, unsafe directory entry, or +/// filesystem enumeration failure. +pub fn quick_open_files(repository: &Path) -> io::Result { + let root = open_repository(repository)?; + let (status_map, git_error) = load_git_status(repository); + let mut pending = vec![String::new()]; + let mut entries = Vec::new(); + let mut truncated = false; + let mut visited = 0usize; + while let Some(directory) = pending.pop() { + if directory.split('/').count() > MAX_BROWSER_DEPTH { + truncated = true; + continue; + } + let enumeration = enumeration_path(repository, &directory)?; + let remaining = MAX_BROWSER_ENTRIES.saturating_sub(visited); + if remaining == 0 { + truncated = true; + break; + } + let (children, listing_truncated) = + root.list_directory_path(&directory, &enumeration, true, remaining)?; + truncated |= listing_truncated; + for child in children { + visited = visited.saturating_add(1); + let path = join_relative(&directory, &child.name); + match child.kind { + SafeEntryKind::Directory => pending.push(path), + SafeEntryKind::File => entries.push(BrowserEntry { + git_status: status_for_path(&status_map, &path, child.kind), + path, + kind: BrowserEntryKind::File, + }), + SafeEntryKind::Symlink | SafeEntryKind::Other => {} + } + } + } + entries.sort_by(|left, right| left.path.cmp(&right.path)); + Ok(BrowserListing { + entries, + truncated, + git_error, + }) +} + +/// Read at most 256 KiB from one regular repository file without following +/// symlinks. The returned boolean indicates omitted trailing bytes. +/// +/// # Errors +/// +/// Returns an error for an invalid project path, unsafe relative path, +/// symlink, non-regular file, or filesystem read failure. +pub fn read_repository_file(repository: &Path, relative_path: &str) -> io::Result<(Vec, bool)> { + open_repository(repository)?.read_regular_file_path(relative_path, MAX_FILE_PREVIEW_BYTES) +} + +/// Read one regular text-edit candidate up to Rover's 8 MiB source file cap. +/// +/// # Errors +/// +/// Returns an error for an unsafe path, symlink, non-regular file, oversized +/// file, or filesystem failure. +pub fn read_repository_file_for_edit( + repository: &Path, + relative_path: &str, +) -> io::Result> { + let (bytes, truncated) = open_repository(repository)? + .read_regular_file_path(relative_path, crate::MAX_FILE_BYTES)?; + if truncated { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "file exceeds the 8 MiB edit limit", + )); + } + Ok(bytes) +} + +/// Replace one existing regular repository file if its bytes still match the +/// digest observed when editing began. The replacement is rooted, no-follow, +/// atomic, and synced before this function returns. +/// +/// # Errors +/// +/// Returns an error for unsafe paths, oversized files, symlinks or special +/// entries, stale content, or filesystem failures. +pub fn save_repository_file( + repository: &Path, + relative_path: &str, + expected_sha256: &str, + bytes: &[u8], +) -> io::Result<()> { + validate_relative_path(relative_path)?; + if bytes.len() > crate::MAX_FILE_BYTES { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "edited file exceeds the 8 MiB source file limit", + )); + } + let root = open_repository(repository)?; + let (current, truncated) = root.read_regular_file_path(relative_path, crate::MAX_FILE_BYTES)?; + if truncated { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "current file exceeds the source file limit", + )); + } + if Sha256Digest::of(¤t).to_hex() != expected_sha256 { + return Err(io::Error::new( + io::ErrorKind::WouldBlock, + "file changed since editing began; reload before saving", + )); + } + root.atomic_replace_regular_file_path(relative_path, bytes, crate::MAX_FILE_BYTES) +} + +fn open_repository(repository: &Path) -> io::Result { + let canonical = std::fs::canonicalize(repository)?; + if !canonical.is_dir() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "repository path is not a directory", + )); + } + SafeDir::open_directory(canonical) +} + +fn enumeration_path(repository: &Path, relative_directory: &str) -> io::Result { + if relative_directory.is_empty() { + return Ok(repository.to_path_buf()); + } + validate_relative_path(relative_directory)?; + Ok(repository.join(relative_directory)) +} + +fn validate_relative_path(path: &str) -> io::Result<()> { + if path.is_empty() + || path.starts_with('/') + || path.contains('\\') + || path.contains('\0') + || path.split('/').any(|part| { + part.is_empty() || matches!(part, "." | "..") || part.eq_ignore_ascii_case(".git") + }) + { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "repository-relative path is unsafe", + )); + } + Ok(()) +} + +fn join_relative(directory: &str, name: &str) -> String { + if directory.is_empty() { + name.to_owned() + } else { + format!("{directory}/{name}") + } +} + +fn browser_kind(kind: SafeEntryKind) -> BrowserEntryKind { + match kind { + SafeEntryKind::File => BrowserEntryKind::File, + SafeEntryKind::Directory => BrowserEntryKind::Directory, + SafeEntryKind::Symlink => BrowserEntryKind::Symlink, + SafeEntryKind::Other => BrowserEntryKind::Other, + } +} + +fn load_git_status(repository: &Path) -> (BTreeMap, Option) { + match git( + repository, + &[ + "status", + "--porcelain=v1", + "-z", + "--untracked-files=all", + "--renames", + "--", + ], + ) { + Ok(bytes) => match parse_git_status(&bytes) { + Ok(status) => (status, None), + Err(error) => (BTreeMap::new(), Some(error.to_string())), + }, + Err(error) => (BTreeMap::new(), Some(error.to_string())), + } +} + +fn parse_git_status(bytes: &[u8]) -> io::Result> { + let mut statuses = BTreeMap::new(); + let mut records = bytes.split(|byte| *byte == 0).peekable(); + while let Some(record) = records.next() { + if record.is_empty() { + continue; + } + if record.len() < 4 || record[2] != b' ' { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Git returned malformed porcelain status", + )); + } + let path = std::str::from_utf8(&record[3..]).map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidData, + "Git path is not UTF-8 and cannot be shown by the TUI", + ) + })?; + validate_relative_path(path)?; + let status = parse_status_code([record[0], record[1]]); + statuses.insert(path.to_owned(), status); + if (record[0] == b'R' || record[0] == b'C' || record[1] == b'R' || record[1] == b'C') + && records.next().is_none() + { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Git rename status omitted its source path", + )); + } + } + Ok(statuses) +} + +fn parse_status_code(code: [u8; 2]) -> BrowserGitStatus { + let [index, worktree] = code; + if matches!( + (index, worktree), + (b'U', _) | (_, b'U') | (b'A', b'A') | (b'D', b'D') + ) { + BrowserGitStatus::Conflict + } else if index == b'?' && worktree == b'?' { + BrowserGitStatus::Untracked + } else if index == b'D' || worktree == b'D' { + BrowserGitStatus::Deleted + } else if index == b'A' || worktree == b'A' { + BrowserGitStatus::Added + } else if index == b'R' || worktree == b'R' || index == b'C' || worktree == b'C' { + BrowserGitStatus::Renamed + } else { + BrowserGitStatus::Modified + } +} + +fn status_for_path( + statuses: &BTreeMap, + path: &str, + kind: SafeEntryKind, +) -> Option { + statuses.get(path).copied().or_else(|| { + (kind == SafeEntryKind::Directory) + .then(|| { + statuses + .range(format!("{path}/")..) + .next() + .filter(|(candidate, _)| candidate.starts_with(&format!("{path}/"))) + .map(|(_, status)| *status) + }) + .flatten() + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + use std::os::unix::fs::symlink; + use std::process::Command; + use std::sync::atomic::{AtomicU64, Ordering}; + + static NEXT_FIXTURE: AtomicU64 = AtomicU64::new(0); + + struct Repo(PathBuf); + + impl Repo { + fn new() -> Self { + let id = NEXT_FIXTURE.fetch_add(1, Ordering::Relaxed); + let path = + std::env::temp_dir().join(format!("rover-browser-{}-{id}", std::process::id())); + fs::create_dir(&path).unwrap(); + git_fixture(&path, &["init", "-q"]); + git_fixture(&path, &["config", "user.email", "rover@example.invalid"]); + git_fixture(&path, &["config", "user.name", "Rover fixture"]); + fs::create_dir(path.join("src")).unwrap(); + fs::write(path.join("src/main.rs"), "fn main() {}\n").unwrap(); + git_fixture(&path, &["add", "--", "src/main.rs"]); + git_fixture(&path, &["commit", "-q", "-m", "fixture"]); + Self(path) + } + + fn write(&self, path: &str, bytes: &[u8]) { + let full = self.0.join(path); + fs::create_dir_all(full.parent().unwrap()).unwrap(); + fs::write(full, bytes).unwrap(); + } + } + + impl Drop for Repo { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.0); + } + } + + fn git_fixture(repository: &Path, arguments: &[&str]) { + let status = Command::new("git") + .args(["-C"]) + .arg(repository) + .args(arguments) + .env("GIT_CONFIG_NOSYSTEM", "1") + .env("GIT_CONFIG_GLOBAL", "/dev/null") + .status() + .unwrap(); + assert!(status.success()); + } + + #[test] + fn browser_lists_hidden_and_git_state_without_traversing_symlinks() { + let repo = Repo::new(); + repo.write("src/main.rs", b"changed"); + repo.write("src/new.rs", b"new"); + repo.write("src/gone.rs", b"will be deleted"); + git_fixture(&repo.0, &["add", "--", "src/gone.rs"]); + git_fixture(&repo.0, &["commit", "-q", "-m", "add deletion fixture"]); + fs::remove_file(repo.0.join("src/gone.rs")).unwrap(); + repo.write(".hidden/settings.json", b"hidden"); + symlink(std::env::temp_dir(), repo.0.join("outside-link")).unwrap(); + + let visible = browse_directory(&repo.0, "", false).unwrap(); + assert!(visible + .entries + .iter() + .all(|entry| !entry.path.starts_with('.'))); + assert!(visible + .entries + .iter() + .any(|entry| entry.path == "outside-link" && entry.kind == BrowserEntryKind::Symlink)); + let hidden = browse_directory(&repo.0, "", true).unwrap(); + assert!(hidden.entries.iter().any(|entry| entry.path == ".hidden")); + let source = browse_directory(&repo.0, "src", false).unwrap(); + assert_eq!( + source + .entries + .iter() + .find(|entry| entry.path == "src/main.rs") + .unwrap() + .git_status, + Some(BrowserGitStatus::Modified) + ); + assert_eq!( + source + .entries + .iter() + .find(|entry| entry.path == "src/new.rs") + .unwrap() + .git_status, + Some(BrowserGitStatus::Untracked) + ); + assert_eq!( + source + .entries + .iter() + .find(|entry| entry.path == "src/gone.rs") + .unwrap() + .git_status, + Some(BrowserGitStatus::Deleted) + ); + assert!(browse_directory(&repo.0, "outside-link", true).is_err()); + } + + #[test] + fn quick_open_includes_hidden_files_but_skips_git_and_links() { + let repo = Repo::new(); + repo.write(".hidden/config.toml", b"setting=true"); + repo.write("nested/deeper/note.md", b"note"); + symlink( + std::env::temp_dir().join("not-a-rover-file"), + repo.0.join("alias"), + ) + .unwrap(); + let listing = quick_open_files(&repo.0).unwrap(); + assert!(listing + .entries + .iter() + .any(|entry| entry.path == ".hidden/config.toml")); + assert!(listing + .entries + .iter() + .any(|entry| entry.path == "nested/deeper/note.md")); + assert!(listing + .entries + .iter() + .all(|entry| !entry.path.starts_with(".git/"))); + assert!(listing.entries.iter().all(|entry| entry.path != "alias")); + } + + #[test] + fn bounded_preview_refuses_traversal_links_and_oversize_limits() { + let repo = Repo::new(); + repo.write("preview.txt", b"hello preview"); + symlink( + std::env::temp_dir().join("external-secret"), + repo.0.join("link.txt"), + ) + .unwrap(); + let (bytes, truncated) = read_repository_file(&repo.0, "preview.txt").unwrap(); + assert_eq!(bytes, b"hello preview"); + assert!(!truncated); + repo.write("large.txt", &vec![b'x'; MAX_FILE_PREVIEW_BYTES + 9]); + let (large, truncated) = read_repository_file(&repo.0, "large.txt").unwrap(); + assert_eq!(large.len(), MAX_FILE_PREVIEW_BYTES); + assert!(truncated); + assert!(read_repository_file(&repo.0, "../external-secret").is_err()); + assert!(read_repository_file(&repo.0, "link.txt").is_err()); + } + + #[test] + fn save_checks_original_digest_and_atomically_replaces_regular_file() { + let repo = Repo::new(); + let original = b"fn main() {}\n"; + let expected = Sha256Digest::of(original).to_hex(); + save_repository_file(&repo.0, "src/main.rs", &expected, b"fn main() { 1; }\n").unwrap(); + assert_eq!( + fs::read(repo.0.join("src/main.rs")).unwrap(), + b"fn main() { 1; }\n" + ); + let stale = save_repository_file(&repo.0, "src/main.rs", &expected, b"stale"); + assert_eq!(stale.unwrap_err().kind(), io::ErrorKind::WouldBlock); + assert!(save_repository_file(&repo.0, "../escape", &expected, b"bad").is_err()); + symlink(std::env::temp_dir(), repo.0.join("linked.rs")).unwrap(); + assert!(save_repository_file(&repo.0, "linked.rs", &expected, b"bad").is_err()); + } + + #[test] + fn porcelain_parser_handles_status_codes_and_rejects_invalid_paths() { + let bytes = b" M src/main.rs\0?? notes.txt\0A added.rs\0D gone.rs\0UU conflict.rs\0R renamed.rs\0old-name.rs\0"; + let statuses = parse_git_status(bytes).unwrap(); + assert_eq!(statuses["src/main.rs"], BrowserGitStatus::Modified); + assert_eq!(statuses["notes.txt"], BrowserGitStatus::Untracked); + assert_eq!(statuses["added.rs"], BrowserGitStatus::Added); + assert_eq!(statuses["gone.rs"], BrowserGitStatus::Deleted); + assert_eq!(statuses["conflict.rs"], BrowserGitStatus::Conflict); + assert_eq!(statuses["renamed.rs"], BrowserGitStatus::Renamed); + assert!(parse_git_status(b" M ../escape\0").is_err()); + assert!(parse_git_status(b"bad\0").is_err()); + } +} diff --git a/crates/rover-source/src/lib.rs b/crates/rover-source/src/lib.rs new file mode 100644 index 0000000..3c24364 --- /dev/null +++ b/crates/rover-source/src/lib.rs @@ -0,0 +1,2953 @@ +//! Git-backed, content-addressed source capture for the Rover CLI/TUI. +//! +//! Committed captures read Git blobs directly, so checkout filters do not run. +//! Working-tree captures read regular files twice and report that the result is +//! not a filesystem-atomic snapshot. + +use std::collections::{BTreeMap, HashMap, HashSet}; +use std::error::Error; +use std::fmt; +use std::fs::{self, File}; +use std::io::{self, Read, Write}; +use std::path::{Component, Path, PathBuf}; +use std::process::{Command, Stdio}; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::thread; +use std::time::{Duration, Instant}; + +use rover_core::{Sha256Digest, Timestamp, SCHEMA}; +use serde::{Deserialize, Serialize}; + +#[cfg(unix)] +mod file_browser; +#[cfg(unix)] +pub use file_browser::{ + browse_directory, quick_open_files, read_repository_file, read_repository_file_for_edit, + save_repository_file, BrowserEntry, BrowserEntryKind, BrowserGitStatus, BrowserListing, + MAX_BROWSER_ENTRIES, MAX_FILE_PREVIEW_BYTES, +}; + +#[cfg(unix)] +use rover_store::files::{SafeDir, StateRoot}; + +/// Maximum bytes captured for one source file. +pub const MAX_FILE_BYTES: usize = 8 * 1024 * 1024; +/// Maximum combined source bytes in one snapshot. +pub const MAX_SNAPSHOT_BYTES: usize = 128 * 1024 * 1024; +/// Maximum source file count in one snapshot. +pub const MAX_FILES: usize = 10_000; +const MAX_GIT_STDERR: usize = 1024 * 1024; +const GIT_TIMEOUT: Duration = Duration::from_secs(30); +const EXACT_COMMIT_CONSISTENCY: &str = "exact Git commit blobs; no checkout filters"; +const WORKTREE_CONSISTENCY: &str = + "two identical content reads; frozen bytes retained; not a filesystem-atomic capture"; +static DIFF_SEQUENCE: AtomicU64 = AtomicU64::new(0); + +/// File metadata and digest retained in a captured snapshot. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct SnapshotFile { + /// Slash-separated repository-relative path. + pub path: String, + /// Portable permission bits, currently 0644 or 0755. + pub mode: u32, + /// Lowercase hexadecimal SHA-256 digest of the captured bytes. + pub sha256: String, + /// Exact byte length. + pub size: i64, +} + +/// Stable snapshot metadata stored by Rover. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct Snapshot { + /// Rover schema marker. + pub schema: String, + /// Stable `snap_` identity derived from repository and file list. + pub id: String, + /// Canonical repository root. + pub repository: String, + /// Commit reference or `WORKTREE`. + pub source_ref: String, + /// Resolved base commit identity. + pub commit: String, + /// Path-sorted source file inventory. + pub files: Vec, + /// RFC3339 UTC capture timestamp. + pub created_at: String, + /// Explicit consistency guarantee for this capture. + pub consistency: String, +} + +/// Snapshot metadata plus verified source bytes keyed by SHA-256. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct CapturedSnapshot { + /// Persistable metadata record. + pub snapshot: Snapshot, + /// Content-addressed bytes required to persist or materialize the snapshot. + pub objects: BTreeMap>, +} + +/// One added, deleted, or changed path in a candidate comparison. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct Change { + /// Repository-relative path. + pub path: String, + /// `added`, `deleted`, or `modified`. + pub status: String, + /// Rover's filename-based risk category. + pub category: String, +} + +/// A deterministic observation about candidate changes. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct Finding { + /// Stable rule identifier. + pub rule: String, + /// Human-readable explanation. + pub message: String, + /// Optional repository-relative path. + #[serde(skip_serializing_if = "Option::is_none")] + pub path: Option, + /// Evidence classification. + pub source: String, +} + +/// Deterministic base-to-candidate comparison result. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct Inspection { + /// Rover schema marker. + pub schema: String, + /// Validated base snapshot. + pub base: Snapshot, + /// Validated candidate snapshot. + pub candidate: Snapshot, + /// Path-sorted observed changes. + pub changes: Vec, + /// Findings derived only from those observed paths and statuses. + pub findings: Vec, +} + +/// One registered Git worktree and its porcelain status metadata. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct WorktreeInfo { + /// Registered worktree path. + pub path: String, + /// Current commit object ID, when Git reports one. + #[serde(skip_serializing_if = "Option::is_none")] + pub head: Option, + /// Attached branch reference, when present. + #[serde(skip_serializing_if = "Option::is_none")] + pub branch: Option, + /// Whether HEAD is detached. + pub detached: bool, + /// Whether the entry is a bare repository. + pub bare: bool, + /// Lock reason, if the worktree is locked. + #[serde(skip_serializing_if = "Option::is_none")] + pub locked: Option, + /// Prune reason, if Git marks the worktree prunable. + #[serde(skip_serializing_if = "Option::is_none")] + pub prunable: Option, +} + +/// Observed worktree state compared with its requested frozen base. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct WorktreeInspection { + /// Registered worktree metadata. + pub worktree: WorktreeInfo, + /// Stable identity of the snapshot used as the inspection baseline. + pub base_snapshot_id: String, + /// Captured source manifest, labeled as a two-read non-atomic snapshot. + pub snapshot: Snapshot, + /// Path-sorted changes relative to the base snapshot. + pub changes: Vec, + /// Whether any tracked or non-ignored untracked source differs. + pub dirty: bool, +} + +/// Result of verifying that a persisted snapshot record and every referenced +/// immutable content object are present and valid. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct SnapshotVerification { + /// Verified snapshot identity. + pub snapshot_id: String, + /// Number of metadata entries checked against stored bytes. + pub verified_files: usize, + /// Total verified source bytes. + pub verified_bytes: u64, +} + +/// Inputs for committed or working-tree capture. +#[derive(Clone, Debug)] +pub struct CaptureOptions { + /// Repository path or any path inside a Git worktree. + pub repository: PathBuf, + /// State root that will hold snapshot records and blobs in later storage + /// integration. Capture rejects state roots located inside the repository. + pub state_root: PathBuf, + /// Git commit-ish, defaulting to `HEAD`; the exact value `WORKTREE` selects + /// working-tree capture. + pub source_ref: String, + /// Include non-ignored untracked files when capturing `WORKTREE`. + pub include_untracked: bool, +} + +/// Capture failures are fail-closed and include a concise operation context. +#[derive(Debug)] +pub struct CaptureError(String); + +impl fmt::Display for CaptureError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str(&self.0) + } +} + +impl Error for CaptureError {} + +impl CaptureError { + fn new(message: impl Into) -> Self { + Self(message.into()) + } +} + +/// Capture a committed Git tree or a two-read working-tree snapshot. +/// +/// The returned object bytes are immutable for the lifetime of the value. This +/// function never writes to the source repository or state root. +/// +/// # Errors +/// +/// Fails on invalid roots/references, Git errors, unsupported Git tree entries, +/// unsafe or changing working files, unresolved LFS pointers, and size limits. +pub fn capture(options: &CaptureOptions) -> Result { + if options.state_root.as_os_str().is_empty() { + return Err(CaptureError::new("state root path is required")); + } + let repository = discover(&options.repository)?; + let state_root = canonicalize_allow_missing(&options.state_root)?; + if state_root.starts_with(&repository) { + return Err(CaptureError::new( + "Rover state root must be outside the repository", + )); + } + let source_ref = if options.source_ref.is_empty() { + "HEAD" + } else { + &options.source_ref + }; + let first = collect(&repository, source_ref, options.include_untracked)?; + let (files, objects, consistency) = if source_ref == "WORKTREE" { + let second = collect(&repository, source_ref, options.include_untracked)?; + if first.commit != second.commit || first.files != second.files { + return Err(CaptureError::new( + "workspace changed during capture; pause writers and retry", + )); + } + (first.files, first.objects, WORKTREE_CONSISTENCY) + } else { + (first.files, first.objects, EXACT_COMMIT_CONSISTENCY) + }; + let repository_text = repository + .to_str() + .ok_or_else(|| CaptureError::new("repository root is not valid UTF-8"))? + .to_owned(); + let source_commit = first.commit; + let id = snapshot_id(&repository_text, &files)?; + let created_at = Timestamp::now() + .to_rfc3339() + .map_err(|error| CaptureError::new(format!("format capture timestamp: {error}")))?; + Ok(CapturedSnapshot { + snapshot: Snapshot { + schema: SCHEMA.to_owned(), + id, + repository: repository_text, + source_ref: source_ref.to_owned(), + commit: source_commit, + files, + created_at, + consistency: consistency.to_owned(), + }, + objects, + }) +} + +/// Validate snapshot identity, paths, file metadata, and aggregate limits. +/// +/// # Errors +/// +/// Returns an error when any identity, path, mode, digest, size, or tree +/// relationship is invalid. +pub fn validate_snapshot(snapshot: &Snapshot) -> Result<(), CaptureError> { + if snapshot.schema != SCHEMA + || snapshot.id != snapshot_id(&snapshot.repository, &snapshot.files)? + { + return Err(CaptureError::new("snapshot identity mismatch")); + } + if !valid_object_id(&snapshot.commit) { + return Err(CaptureError::new("invalid snapshot commit identity")); + } + if snapshot.files.len() > MAX_FILES { + return Err(CaptureError::new("snapshot has too many files")); + } + let max_file_bytes = i64::try_from(MAX_FILE_BYTES) + .map_err(|_| CaptureError::new("file size limit does not fit snapshot metadata"))?; + let max_snapshot_bytes = i64::try_from(MAX_SNAPSHOT_BYTES) + .map_err(|_| CaptureError::new("snapshot size limit does not fit metadata"))?; + let mut total = 0_i64; + let mut seen = HashSet::new(); + for file in &snapshot.files { + validate_relative_path(&file.path)?; + let folded = file.path.to_lowercase(); + if !seen.insert(folded) { + return Err(CaptureError::new("duplicate or case-colliding path")); + } + if !matches!(file.mode, 0o644 | 0o755) { + return Err(CaptureError::new("invalid snapshot file mode")); + } + if file.size < 0 || file.size > max_file_bytes { + return Err(CaptureError::new("invalid snapshot file size")); + } + total = total + .checked_add(file.size) + .ok_or_else(|| CaptureError::new("snapshot size overflow"))?; + if file.sha256.len() != 64 + || !file + .sha256 + .bytes() + .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte)) + { + return Err(CaptureError::new("invalid snapshot object digest")); + } + } + if total > max_snapshot_bytes { + return Err(CaptureError::new("snapshot size budget exceeded")); + } + for file in &snapshot.files { + let components = file.path.split('/').collect::>(); + for end in 1..components.len() { + let parent = components[..end].join("/").to_lowercase(); + if seen.contains(&parent) { + return Err(CaptureError::new("snapshot file/directory conflict")); + } + } + } + Ok(()) +} + +/// Compare two validated snapshots and return path-sorted changes and findings. +/// The snapshot IDs bind the repository and path inventory; content and mode +/// differences determine whether each shared path changed. +/// +/// # Errors +/// +/// Returns an error if either snapshot fails identity, path, metadata, or size +/// validation. +pub fn compare_snapshots( + base: &Snapshot, + candidate: &Snapshot, +) -> Result { + validate_snapshot(base)?; + validate_snapshot(candidate)?; + let old = base + .files + .iter() + .map(|file| (file.path.as_str(), file)) + .collect::>(); + let new = candidate + .files + .iter() + .map(|file| (file.path.as_str(), file)) + .collect::>(); + let mut paths = old + .keys() + .chain(new.keys()) + .copied() + .collect::>(); + let mut paths = paths.drain().collect::>(); + paths.sort_unstable(); + let mut inspection = Inspection { + schema: SCHEMA.to_owned(), + base: base.clone(), + candidate: candidate.clone(), + changes: Vec::new(), + findings: Vec::new(), + }; + for path in paths { + let before = old.get(path); + let after = new.get(path); + let status = match (before, after) { + (None, Some(_)) => "added", + (Some(_), None) => "deleted", + (Some(before), Some(after)) + if before.sha256 != after.sha256 || before.mode != after.mode => + { + "modified" + } + _ => continue, + }; + let category = category(path); + inspection.changes.push(Change { + path: path.to_owned(), + status: status.to_owned(), + category: category.to_owned(), + }); + if category == "verification-config" || category == "ci" { + inspection.findings.push(Finding { + rule: "verification_configuration_changed".to_owned(), + message: "Verification-related input changed; prior approved policy is retained" + .to_owned(), + path: Some(path.to_owned()), + source: "observed_diff".to_owned(), + }); + } + if category == "test" && status == "deleted" { + inspection.findings.push(Finding { + rule: "test_file_deleted".to_owned(), + message: "A test-classified file was deleted; classification is filename-based" + .to_owned(), + path: Some(path.to_owned()), + source: "path_heuristic".to_owned(), + }); + } + } + Ok(inspection) +} + +/// Compose an explicitly supplied file inventory from already stored, +/// content-addressed objects and persist the resulting immutable snapshot. +/// +/// # Errors +/// +/// Returns an error if the origin is invalid, the inventory violates snapshot +/// rules, any referenced object is missing/corrupt, or record persistence fails. +#[cfg(unix)] +pub fn compose_snapshot( + state: &StateRoot, + origin: &Snapshot, + mut files: Vec, + label: &str, +) -> Result { + validate_snapshot(origin)?; + files.sort_by(|left, right| left.path.cmp(&right.path)); + let mut composed = origin.clone(); + composed.files = files; + label.clone_into(&mut composed.source_ref); + composed.created_at = Timestamp::now() + .to_rfc3339() + .map_err(|error| CaptureError::new(format!("format composition timestamp: {error}")))?; + "composed from verified content objects".clone_into(&mut composed.consistency); + composed.id = snapshot_id(&composed.repository, &composed.files)?; + validate_snapshot(&composed)?; + verified_contents(state, &composed)?; + state + .records() + .put("snapshot", &composed.id, &composed, "snapshot.composed") + .map_err(|error| CaptureError::new(format!("persist composed snapshot: {error}")))?; + Ok(composed) +} + +/// Load a retained snapshot and verify its record identity plus every backing +/// content object. Snapshot records and immutable objects are not pruned by +/// this API; cleanup requires a separately reviewed retention policy. +/// +/// # Errors +/// +/// Returns an error if the record is absent/mismatched, its metadata is +/// invalid, or any referenced content object is missing/corrupt. +#[cfg(unix)] +pub fn verify_retained_snapshot( + state: &StateRoot, + snapshot_id: &str, +) -> Result { + let snapshot = state + .records() + .get_typed::("snapshot", snapshot_id) + .map_err(|error| CaptureError::new(format!("load retained snapshot: {error}")))?; + validate_snapshot(&snapshot)?; + if snapshot.id != snapshot_id { + return Err(CaptureError::new("retained snapshot record ID mismatch")); + } + let contents = verified_contents(state, &snapshot)?; + let verified_bytes = contents.iter().try_fold(0_u64, |total, bytes| { + total.checked_add(u64::try_from(bytes.len()).unwrap_or(u64::MAX)) + }); + let verified_bytes = + verified_bytes.ok_or_else(|| CaptureError::new("verified snapshot byte count overflow"))?; + Ok(SnapshotVerification { + snapshot_id: snapshot.id, + verified_files: snapshot.files.len(), + verified_bytes, + }) +} + +/// Overlay explicitly selected paths from one exact-base candidate onto a +/// base snapshot. Paths absent from the candidate, duplicates, and an empty +/// path selection are rejected. +/// +/// # Errors +/// +/// Returns an error for invalid snapshots, repository/base mismatch, invalid +/// or duplicate paths, missing candidate paths, or invalid stored objects. +#[cfg(unix)] +pub fn overlay_snapshot( + state: &StateRoot, + base: &Snapshot, + candidate: &Snapshot, + paths: &[String], +) -> Result { + validate_exact_base_pair(base, candidate)?; + if paths.is_empty() { + return Err(CaptureError::new("explicit overlay paths required")); + } + let mut files = base + .files + .iter() + .map(|file| (file.path.clone(), file.clone())) + .collect::>(); + let candidate_files = candidate + .files + .iter() + .map(|file| (file.path.as_str(), file)) + .collect::>(); + let mut selected = HashSet::new(); + for path in paths { + validate_relative_path(path)?; + if !selected.insert(path.as_str()) { + return Err(CaptureError::new("duplicate overlay path")); + } + let file = candidate_files + .get(path.as_str()) + .ok_or_else(|| CaptureError::new(format!("overlay file absent: {path}")))?; + files.insert(path.clone(), (*file).clone()); + } + compose_snapshot( + state, + base, + files.into_values().collect(), + &format!("overlay:{}", candidate.id), + ) +} + +/// Merge multiple exact-base candidate snapshots. Disjoint changes and +/// identical edits compose; different edits to the same path fail closed. +/// +/// # Errors +/// +/// Returns an error for invalid/mismatched snapshots, conflicting edits, or +/// missing/corrupt stored objects. Conflicts require an explicit integration +/// task and are never resolved by ordering candidates. +#[cfg(unix)] +pub fn merge_snapshots( + state: &StateRoot, + base: &Snapshot, + candidates: &[Snapshot], +) -> Result { + validate_snapshot(base)?; + let mut result = base + .files + .iter() + .map(|file| (file.path.clone(), file.clone())) + .collect::>(); + let mut edits: HashMap> = HashMap::new(); + for candidate in candidates { + validate_exact_base_pair(base, candidate)?; + let comparison = compare_snapshots(base, candidate)?; + let candidate_files = candidate + .files + .iter() + .map(|file| (file.path.as_str(), file)) + .collect::>(); + for change in comparison.changes { + let file = candidate_files + .get(change.path.as_str()) + .map(|file| (*file).clone()); + if let Some(previous) = edits.get(&change.path) { + if previous != &file { + return Err(CaptureError::new(format!( + "integration conflict: {}", + change.path + ))); + } + } + edits.insert(change.path.clone(), file.clone()); + if let Some(file) = file { + result.insert(change.path, file); + } else { + result.remove(&change.path); + } + } + } + compose_snapshot(state, base, result.into_values().collect(), "integration") +} + +fn validate_exact_base_pair(base: &Snapshot, candidate: &Snapshot) -> Result<(), CaptureError> { + validate_snapshot(base)?; + validate_snapshot(candidate)?; + if base.repository != candidate.repository || base.commit != candidate.commit { + return Err(CaptureError::new( + "candidate repository or exact base commit mismatch", + )); + } + Ok(()) +} + +fn category(path: &str) -> &'static str { + let lower = path.to_lowercase(); + let basename = lower.rsplit('/').next().unwrap_or(&lower); + if lower.starts_with(".github/") || lower.contains(".gitlab-ci") { + "ci" + } else if lower.starts_with(".rover/") { + "verification-config" + } else if lower.contains("migration") { + "migration" + } else if lower.starts_with("docs/") + || Path::new(&lower) + .extension() + .is_some_and(|extension| extension.eq_ignore_ascii_case("md")) + { + "documentation" + } else if is_test_path(&lower, basename) { + "test" + } else if matches!( + basename, + "go.mod" | "go.sum" | "package.json" | "requirements.txt" | "pyproject.toml" + ) || basename.contains("lock") + { + "dependency" + } else if [".yaml", ".yml", ".toml", ".json"] + .iter() + .any(|suffix| basename.ends_with(suffix)) + { + "configuration" + } else { + "source" + } +} + +fn is_test_path(path: &str, basename: &str) -> bool { + path.split('/') + .any(|segment| matches!(segment, "test" | "tests" | "__tests__")) + || basename.starts_with("test") + || [ + "_test.go", + "_test.py", + ".test.js", + ".test.ts", + ".test.jsx", + ".test.tsx", + "-test.js", + "-test.ts", + ] + .iter() + .any(|suffix| basename.ends_with(suffix)) +} + +/// Produce an applicable binary Git patch using only verified retained bytes. +/// The temporary bare repository has no checkout, filters, hooks, or worktree. +/// +/// # Errors +/// +/// Returns an error for invalid snapshots, missing/corrupt blobs, unsupported +/// Git patch path framing, a Git failure, or bounded output overflow. +#[cfg(unix)] +pub fn diff( + state: &StateRoot, + base: &Snapshot, + candidate: &Snapshot, +) -> Result, CaptureError> { + validate_snapshot(base)?; + validate_snapshot(candidate)?; + let temporary = DiffDirectory::create(state.path())?; + git(&temporary.path, &["init", "--bare", "--quiet"])?; + let mut input = Vec::new(); + let mut marks = HashMap::new(); + for snapshot in [base, candidate] { + for file in &snapshot.files { + if marks.contains_key(&file.sha256) { + continue; + } + let bytes = state + .read_blob(&file.sha256) + .map_err(|error| CaptureError::new(format!("read diff object: {error}")))?; + if i64::try_from(bytes.len()).unwrap_or(i64::MAX) != file.size { + return Err(CaptureError::new("diff object size differs from snapshot")); + } + let mark = marks.len() + 1; + marks.insert(file.sha256.clone(), mark); + writeln!(input, "blob\nmark :{mark}\ndata {}", bytes.len()) + .and_then(|()| input.write_all(&bytes)) + .and_then(|()| input.write_all(b"\n")) + .map_err(|error| CaptureError::new(format!("frame diff object: {error}")))?; + } + } + for (name, snapshot) in [("base", base), ("candidate", candidate)] { + writeln!( + input, + "commit refs/heads/{name}\ncommitter Rover 1 +0000\ndata {}\n{name}", + name.len() + ) + .map_err(|error| CaptureError::new(format!("frame diff commit: {error}")))?; + for file in &snapshot.files { + if file.path.contains(['"', '\n', '\r']) { + return Err(CaptureError::new(format!( + "unsupported path for diff framing: {:?}", + file.path + ))); + } + let mode = if file.mode == 0o755 { + "100755" + } else { + "100644" + }; + let mark = marks + .get(&file.sha256) + .ok_or_else(|| CaptureError::new("diff object mark missing"))?; + writeln!(input, "M {mode} :{mark} \"{}\"", file.path) + .map_err(|error| CaptureError::new(format!("frame diff path: {error}")))?; + } + input.extend_from_slice(b"\n"); + } + input.extend_from_slice(b"done\n"); + run_fast_import(&temporary.path, &input)?; + git( + &temporary.path, + &[ + "diff", + "--binary", + "--no-ext-diff", + "--no-textconv", + "--no-color", + "refs/heads/base", + "refs/heads/candidate", + "--", + ], + ) +} + +#[cfg(unix)] +struct DiffDirectory { + path: PathBuf, +} + +#[cfg(unix)] +impl DiffDirectory { + fn create(root: &Path) -> Result { + use std::os::unix::fs::PermissionsExt as _; + + for _ in 0..16 { + let sequence = DIFF_SEQUENCE.fetch_add(1, Ordering::Relaxed); + let path = root.join(format!("diff-{}-{sequence}", std::process::id())); + match fs::create_dir(&path) { + Ok(()) => { + if let Err(error) = + fs::set_permissions(&path, fs::Permissions::from_mode(0o700)) + { + let _ = fs::remove_dir(&path); + return Err(CaptureError::new(format!("secure diff directory: {error}"))); + } + return Ok(Self { path }); + } + Err(error) if error.kind() == io::ErrorKind::AlreadyExists => {} + Err(error) => { + return Err(CaptureError::new(format!("create diff directory: {error}"))); + } + } + } + Err(CaptureError::new( + "could not allocate unique diff directory", + )) + } +} + +#[cfg(unix)] +impl Drop for DiffDirectory { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.path); + } +} + +#[cfg(unix)] +fn run_fast_import(directory: &Path, input: &[u8]) -> Result<(), CaptureError> { + let mut command = Command::new("git"); + command.args([ + "-c", + "core.hooksPath=/dev/null", + "-c", + "core.fsmonitor=false", + "-C", + ]); + command.arg(directory).args(["fast-import", "--quiet"]); + command.env_clear(); + if let Some(path) = std::env::var_os("PATH") { + command.env("PATH", path); + } + command + .env("HOME", directory) + .env("LC_ALL", "C") + .env("GIT_CONFIG_NOSYSTEM", "1") + .env("GIT_CONFIG_GLOBAL", "/dev/null") + .env("GIT_NO_REPLACE_OBJECTS", "1") + .stdin(Stdio::piped()) + .stdout(Stdio::null()) + .stderr(Stdio::null()); + let mut child = command + .spawn() + .map_err(|error| CaptureError::new(format!("start Git fast-import: {error}")))?; + let write_result = child + .stdin + .take() + .ok_or_else(|| CaptureError::new("Git fast-import stdin unavailable"))? + .write_all(input); + if let Err(error) = write_result { + let _ = child.kill(); + let _ = child.wait(); + return Err(CaptureError::new(format!("write Git fast-import: {error}"))); + } + let deadline = Instant::now() + GIT_TIMEOUT; + loop { + match child.try_wait() { + Ok(Some(status)) if status.success() => return Ok(()), + Ok(Some(status)) => { + return Err(CaptureError::new(format!( + "Git fast-import failed with {status}" + ))); + } + Ok(None) if Instant::now() < deadline => thread::sleep(Duration::from_millis(5)), + Ok(None) => { + let _ = child.kill(); + let _ = child.wait(); + return Err(CaptureError::new("Git fast-import timed out")); + } + Err(error) => { + let _ = child.kill(); + let _ = child.wait(); + return Err(CaptureError::new(format!( + "wait for Git fast-import: {error}" + ))); + } + } + } +} + +/// List Git's registered worktrees using its NUL-delimited porcelain format. +/// +/// # Errors +/// +/// Returns an error if Git fails or reports malformed/non-UTF-8 metadata. +pub fn list_worktrees(repository: &Path) -> Result, CaptureError> { + let root = discover(repository)?; + let output = git(&root, &["worktree", "list", "--porcelain", "-z"])?; + parse_worktree_list(&output) +} + +fn parse_worktree_list(output: &[u8]) -> Result, CaptureError> { + let mut worktrees = Vec::new(); + let mut fields = Vec::new(); + for raw_field in output.split(|byte| *byte == 0) { + if raw_field.is_empty() { + if !fields.is_empty() { + worktrees.push(parse_worktree_fields(&fields)?); + fields.clear(); + } + } else { + fields.push( + std::str::from_utf8(raw_field) + .map_err(|_| CaptureError::new("worktree metadata is not UTF-8"))?, + ); + } + } + if !fields.is_empty() { + worktrees.push(parse_worktree_fields(&fields)?); + } + if worktrees.is_empty() { + return Err(CaptureError::new("Git reported no worktrees")); + } + Ok(worktrees) +} + +fn parse_worktree_fields(fields: &[&str]) -> Result { + let mut path = None; + let mut head = None; + let mut branch = None; + let mut detached = false; + let mut bare = false; + let mut locked = None; + let mut prunable = None; + for field in fields { + if let Some(value) = field.strip_prefix("worktree ") { + if path.replace(value.to_owned()).is_some() || value.is_empty() { + return Err(CaptureError::new("invalid worktree path metadata")); + } + } else if let Some(value) = field.strip_prefix("HEAD ") { + if !valid_object_id(value) || head.replace(value.to_owned()).is_some() { + return Err(CaptureError::new("invalid worktree HEAD metadata")); + } + } else if let Some(value) = field.strip_prefix("branch ") { + if value.is_empty() || branch.replace(value.to_owned()).is_some() { + return Err(CaptureError::new("invalid worktree branch metadata")); + } + } else if *field == "detached" { + detached = true; + } else if *field == "bare" { + bare = true; + } else if *field == "locked" || field.starts_with("locked ") { + locked = Some(field.strip_prefix("locked ").unwrap_or_default().to_owned()); + } else if *field == "prunable" || field.starts_with("prunable ") { + prunable = Some( + field + .strip_prefix("prunable ") + .unwrap_or_default() + .to_owned(), + ); + } else { + return Err(CaptureError::new(format!( + "unsupported worktree metadata field: {field}" + ))); + } + } + Ok(WorktreeInfo { + path: path.ok_or_else(|| CaptureError::new("worktree path metadata is missing"))?, + head, + branch, + detached, + bare, + locked, + prunable, + }) +} + +/// Create a detached worktree at a validated snapshot's commit, then populate +/// its files from immutable stored objects without running checkout filters. +/// +/// # Errors +/// +/// Returns an error for an invalid snapshot, existing/unsafe destination, +/// failed Git operation, or missing/corrupt materialization blob. A failure +/// after Git registers the worktree leaves that registered path for inspection. +#[cfg(unix)] +pub fn add_worktree( + state: &StateRoot, + base: &Snapshot, + destination: &Path, +) -> Result<(), CaptureError> { + validate_snapshot(base)?; + match fs::symlink_metadata(destination) { + Ok(_) => return Err(CaptureError::new("worktree destination must not exist")), + Err(error) if error.kind() == io::ErrorKind::NotFound => {} + Err(error) => { + return Err(CaptureError::new(format!( + "inspect worktree destination: {error}" + ))); + } + } + let destination = canonicalize_allow_missing(destination)?; + let parent = destination + .parent() + .ok_or_else(|| CaptureError::new("worktree destination has no parent"))?; + if !parent.is_dir() { + return Err(CaptureError::new("worktree destination parent must exist")); + } + let _admission_lock = state + .lock_worktree_admission(&destination) + .map_err(|error| CaptureError::new(format!("lock worktree destination: {error}")))?; + match fs::symlink_metadata(&destination) { + Ok(_) => return Err(CaptureError::new("worktree destination must not exist")), + Err(error) if error.kind() == io::ErrorKind::NotFound => {} + Err(error) => { + return Err(CaptureError::new(format!( + "recheck worktree destination: {error}" + ))); + } + } + let destination_text = destination + .to_str() + .ok_or_else(|| CaptureError::new("worktree destination is not UTF-8"))?; + git( + Path::new(&base.repository), + &[ + "worktree", + "add", + "--detach", + "--no-checkout", + destination_text, + &base.commit, + ], + )?; + if let Err(error) = git(&destination, &["read-tree", &base.commit]) { + return Err(CaptureError::new(format!( + "worktree registered but index initialization failed: {error}" + ))); + } + if let Err(error) = materialize(state, base, &destination) { + return Err(CaptureError::new(format!( + "worktree registered but snapshot materialization failed: {error}" + ))); + } + Ok(()) +} + +/// Replace a newly created task worktree's base files with a same-repository +/// initial snapshot while preserving the exact base commit in Git's HEAD. +/// Every initial object and all preconditions are checked before any base file +/// is removed. File/directory shape changes that could block safe exclusive +/// materialization are rejected without modifying the worktree. +/// +/// # Errors +/// +/// Returns an error for repository mismatch, a non-task/unregistered worktree, +/// a HEAD other than the frozen base commit, dirty worktree contents, invalid +/// snapshot structure, conflicting file/directory layouts, or missing/corrupt +/// content objects. +#[cfg(unix)] +pub fn replace_worktree_snapshot( + state: &StateRoot, + base: &Snapshot, + initial: &Snapshot, + worktree_path: &Path, +) -> Result<(), CaptureError> { + validate_snapshot(base)?; + validate_snapshot(initial)?; + if initial.repository != base.repository { + return Err(CaptureError::new("input snapshot repository mismatch")); + } + validate_replacement_layout(&base.files, &initial.files)?; + let contents = verified_contents(state, initial)?; + + let worktree = find_worktree(Path::new(&base.repository), worktree_path)?; + if worktree.head.as_deref() != Some(base.commit.as_str()) { + return Err(CaptureError::new( + "task worktree HEAD does not match frozen base commit", + )); + } + let canonical_worktree = Path::new(&worktree.path); + let tasks_storage = fs::canonicalize(state.path().join("tasks")) + .map_err(|error| CaptureError::new(format!("resolve Rover task root: {error}")))?; + let task_directory = canonical_worktree + .parent() + .ok_or_else(|| CaptureError::new("worktree path has no task directory"))?; + let task_directory_parent = task_directory + .parent() + .ok_or_else(|| CaptureError::new("worktree path is outside task storage"))?; + if task_directory_parent != tasks_storage + || canonical_worktree.file_name() != Some(std::ffi::OsStr::new("workspace")) + || task_directory + .file_name() + .and_then(|name| name.to_str()) + .and_then(|name| rover_core::Id::parse(name.to_owned()).ok()) + .is_none() + { + return Err(CaptureError::new( + "workspace replacement is restricted to a Rover task worktree", + )); + } + let status = git( + canonical_worktree, + &[ + "status", + "--porcelain=v1", + "-z", + "--untracked-files=all", + "--ignored=matching", + ], + )?; + if !status.is_empty() { + return Err(CaptureError::new( + "base worktree changed before initial snapshot replacement", + )); + } + + let root = SafeDir::open_directory(canonical_worktree) + .map_err(|error| CaptureError::new(format!("open task worktree: {error}")))?; + for file in &base.files { + root.remove_regular_file_path(&file.path).map_err(|error| { + CaptureError::new(format!("remove frozen base file {}: {error}", file.path)) + })?; + } + write_materialized(&root, initial, contents) +} + +fn validate_replacement_layout( + base_files: &[SnapshotFile], + initial_files: &[SnapshotFile], +) -> Result<(), CaptureError> { + let base_paths = base_files + .iter() + .map(|file| file.path.to_lowercase()) + .collect::>(); + let initial_paths = initial_files + .iter() + .map(|file| file.path.to_lowercase()) + .collect::>(); + for (files, other_paths) in [(base_files, &initial_paths), (initial_files, &base_paths)] { + for file in files { + let components = file.path.split('/').collect::>(); + for end in 1..components.len() { + let parent = components[..end].join("/").to_lowercase(); + if other_paths.contains(&parent) { + return Err(CaptureError::new( + "base and initial snapshots have incompatible file/directory layouts", + )); + } + } + } + } + Ok(()) +} + +/// Inspect a registered worktree against its frozen base snapshot. Working +/// files are read twice and untracked non-ignored files are included. +/// +/// # Errors +/// +/// Returns an error if the path is not a registered worktree or source capture +/// encounters unsafe, changing, or unsupported inputs. +#[cfg(unix)] +pub fn inspect_worktree( + state: &StateRoot, + base: &Snapshot, + path: &Path, +) -> Result { + validate_snapshot(base)?; + let info = find_worktree(Path::new(&base.repository), path)?; + let snapshot = capture(&CaptureOptions { + repository: PathBuf::from(&info.path), + state_root: state.path().to_path_buf(), + source_ref: "WORKTREE".to_owned(), + include_untracked: true, + })? + .snapshot; + let comparison = compare_snapshots(base, &snapshot)?; + let dirty = !comparison.changes.is_empty(); + Ok(WorktreeInspection { + worktree: info, + base_snapshot_id: base.id.clone(), + snapshot, + changes: comparison.changes, + dirty, + }) +} + +/// Resolve a branch, tag, or commit-ish to a full immutable commit object ID. +/// Options and non-commit objects are rejected before they can become a base. +/// +/// # Errors +/// +/// Returns an error for an invalid repository/reference or when Git cannot +/// resolve the reference to a commit. +pub fn resolve_reference(repository: &Path, reference: &str) -> Result { + let repository = discover(repository)?; + resolve(&repository, reference) +} + +/// Derive a stable Rover resource identifier for serializing work on one local +/// branch. The repository's canonical path and fully qualified branch ref are +/// both part of the digest, so same-named branches in separate repositories do +/// not contend. +/// +/// Pass the returned ID as a named resource to `Store::acquire_resources`. +/// +/// # Errors +/// +/// Returns an error for an invalid repository or Git branch name. +pub fn branch_reservation_resource( + repository: &Path, + branch: &str, +) -> Result { + if branch.is_empty() || branch.starts_with('-') || branch.contains('\0') || branch.len() > 1024 + { + return Err(CaptureError::new("invalid Git branch name")); + } + let repository = discover(repository)?; + let qualified = format!("refs/heads/{branch}"); + git(&repository, &["check-ref-format", &qualified]) + .map_err(|error| CaptureError::new(format!("invalid Git branch name: {error}")))?; + let repository_bytes = repository.as_os_str().as_encoded_bytes(); + let mut framed = Vec::with_capacity(repository_bytes.len() + branch.len() + 24); + framed.extend_from_slice(b"rover-branch-reservation/v1:"); + framed.extend_from_slice(repository_bytes); + framed.push(0); + framed.extend_from_slice(qualified.as_bytes()); + Ok(format!("branch-{}", Sha256Digest::of(&framed))) +} + +/// Remove a registered worktree only when its tracked, untracked, and ignored +/// contents are all clean. Git performs the final non-forced removal check. +/// +/// # Errors +/// +/// Returns an error for the main worktree, unknown/unsafe path, dirty contents, +/// or any Git removal failure. +pub fn remove_worktree(repository: &Path, path: &Path) -> Result<(), CaptureError> { + let root = discover(repository)?; + let info = find_worktree(&root, path)?; + if Path::new(&info.path) == root { + return Err(CaptureError::new("refusing to remove the main worktree")); + } + let status = git( + Path::new(&info.path), + &[ + "status", + "--porcelain=v1", + "-z", + "--untracked-files=all", + "--ignored=matching", + ], + )?; + if !status.is_empty() { + return Err(CaptureError::new( + "refusing to remove a worktree with modified, untracked, or ignored files", + )); + } + let path_argument = info.path.as_str(); + git(&root, &["worktree", "remove", "--", path_argument])?; + if list_worktrees(&root)? + .iter() + .any(|entry| entry.path == info.path) + { + return Err(CaptureError::new("Git still reports removed worktree")); + } + Ok(()) +} + +fn find_worktree(repository: &Path, path: &Path) -> Result { + let metadata = fs::symlink_metadata(path) + .map_err(|error| CaptureError::new(format!("inspect worktree path: {error}")))?; + if metadata.file_type().is_symlink() || !metadata.is_dir() { + return Err(CaptureError::new("worktree path must be a real directory")); + } + let canonical = fs::canonicalize(path) + .map_err(|error| CaptureError::new(format!("resolve worktree path: {error}")))?; + let canonical = canonical + .to_str() + .ok_or_else(|| CaptureError::new("worktree path is not UTF-8"))?; + list_worktrees(repository)? + .into_iter() + .find(|worktree| worktree.path == canonical) + .ok_or_else(|| CaptureError::new("path is not a registered worktree")) +} + +/// Confirm that materialized source files still have the captured bytes and +/// executable mode. +/// +/// # Errors +/// +/// Returns an error if the snapshot is invalid or an input is missing, unsafe, +/// modified, or has a different executable mode. +pub fn inputs_unchanged(snapshot: &Snapshot, directory: &Path) -> Result<(), CaptureError> { + validate_snapshot(snapshot)?; + for expected in &snapshot.files { + let (bytes, mode) = read_regular(directory, &expected.path).map_err(|error| { + CaptureError::new(format!( + "input {} missing or unsafe: {error}", + expected.path + )) + })?; + let digest = Sha256Digest::of(&bytes).to_hex(); + if digest != expected.sha256 + || i64::try_from(bytes.len()).unwrap_or(i64::MAX) != expected.size + || mode != expected.mode + { + return Err(CaptureError::new(format!( + "check modified source input {}", + expected.path + ))); + } + } + Ok(()) +} + +/// Verify all referenced blobs, then materialize files exclusively under a +/// private output root. No checkout filter or repository program is executed. +/// +/// # Errors +/// +/// Returns an error for an invalid snapshot, missing or corrupt objects, an +/// unsafe destination or parent, or an output file that already exists. +#[cfg(unix)] +pub fn materialize( + state: &StateRoot, + snapshot: &Snapshot, + destination: &Path, +) -> Result<(), CaptureError> { + validate_snapshot(snapshot)?; + let contents = verified_contents(state, snapshot)?; + let root = prepare_materialization_root(destination)?; + write_materialized(&root, snapshot, contents) +} + +#[cfg(unix)] +fn verified_contents(state: &StateRoot, snapshot: &Snapshot) -> Result>, CaptureError> { + let mut contents = Vec::with_capacity(snapshot.files.len()); + for file in &snapshot.files { + let bytes = state + .read_blob(&file.sha256) + .map_err(|error| CaptureError::new(format!("read snapshot object: {error}")))?; + if i64::try_from(bytes.len()).unwrap_or(i64::MAX) != file.size + || Sha256Digest::of(&bytes).to_hex() != file.sha256 + { + return Err(CaptureError::new("snapshot size or digest mismatch")); + } + contents.push(bytes); + } + Ok(contents) +} + +#[cfg(unix)] +fn write_materialized( + root: &SafeDir, + snapshot: &Snapshot, + contents: Vec>, +) -> Result<(), CaptureError> { + for (file, bytes) in snapshot.files.iter().zip(contents) { + let directory = root + .ensure_subdirectory_path(&file.path) + .map_err(|error| CaptureError::new(format!("create source directory: {error}")))?; + let name = file + .path + .rsplit('/') + .next() + .ok_or_else(|| CaptureError::new("snapshot path has no filename"))?; + directory + .create_new_file(name, &bytes, file.mode) + .map_err(|error| CaptureError::new(format!("materialize {}: {error}", file.path)))?; + } + Ok(()) +} + +#[cfg(unix)] +fn prepare_materialization_root(path: &Path) -> Result { + let root_path = canonicalize_allow_missing(path)?; + if root_path.parent().is_none() { + return Err(CaptureError::new( + "materialization destination must be a dedicated directory", + )); + } + fs::create_dir_all(&root_path) + .map_err(|error| CaptureError::new(format!("create materialization root: {error}")))?; + let root = SafeDir::open_directory(&root_path) + .map_err(|error| CaptureError::new(format!("open materialization root: {error}")))?; + root.set_permissions(0o700).map_err(|error| { + CaptureError::new(format!("make materialization root private: {error}")) + })?; + Ok(root) +} + +/// Capture and persist immutable objects plus the snapshot record. +/// +/// This adapter is available where Rover's admitted `StateRoot` filesystem +/// implementation is available. Blob publication precedes the transactional +/// snapshot record, so a failed record write can leave only unreachable, +/// content-addressed objects. +/// +/// # Errors +/// +/// Returns capture errors or storage errors. A published object digest must +/// match the metadata computed from the captured bytes. +#[cfg(unix)] +pub fn capture_into_state( + options: &CaptureOptions, + state: &rover_store::files::StateRoot, +) -> Result { + let mut state_bound_options = options.clone(); + state_bound_options.state_root = state.path().to_path_buf(); + let captured = capture(&state_bound_options)?; + for (expected_digest, bytes) in &captured.objects { + let actual_digest = state + .put_blob(bytes) + .map_err(|error| CaptureError::new(format!("publish source object: {error}")))? + .to_hex(); + if &actual_digest != expected_digest { + return Err(CaptureError::new( + "published source object digest differs from captured metadata", + )); + } + } + state + .records() + .put( + "snapshot", + &captured.snapshot.id, + &captured.snapshot, + "snapshot.captured", + ) + .map_err(|error| CaptureError::new(format!("persist source snapshot: {error}")))?; + Ok(captured.snapshot) +} + +struct CaptureBody { + commit: String, + files: Vec, + objects: BTreeMap>, +} + +fn collect( + repository: &Path, + source_ref: &str, + include_untracked: bool, +) -> Result { + let head = resolve(repository, "HEAD")?; + let mut body = CaptureBody { + commit: head, + files: Vec::new(), + objects: BTreeMap::new(), + }; + let mut seen = HashSet::new(); + let mut total = 0_usize; + if source_ref == "WORKTREE" { + let staged = git(repository, &["ls-files", "--stage", "-z"])?; + if staged + .split(|byte| *byte == 0) + .any(|record| record.starts_with(b"160000 ")) + { + return Err(CaptureError::new( + "submodules are unsupported in this alpha", + )); + } + let mut args = vec!["ls-files", "-z", "--cached"]; + if include_untracked { + args.extend(["--others", "--exclude-standard"]); + } + let output = git(repository, &args)?; + let mut unique = HashSet::new(); + for raw_path in output + .split(|byte| *byte == 0) + .filter(|entry| !entry.is_empty()) + { + let path = utf8_path(raw_path)?; + if !unique.insert(path.to_owned()) || path.starts_with(".rover-results/") { + continue; + } + match read_regular(repository, path) { + Ok((bytes, mode)) => add_file(&mut body, &mut seen, &mut total, path, mode, bytes)?, + Err(error) if error.kind() == io::ErrorKind::NotFound => {} + Err(error) => return Err(CaptureError::new(format!("read {path}: {error}"))), + } + } + } else { + body.commit = resolve(repository, source_ref)?; + let tree = git(repository, &["ls-tree", "-rz", "--full-tree", &body.commit])?; + for record in tree + .split(|byte| *byte == 0) + .filter(|entry| !entry.is_empty()) + { + let separator = record + .iter() + .position(|byte| *byte == b'\t') + .ok_or_else(|| CaptureError::new("malformed Git tree entry"))?; + let header = std::str::from_utf8(&record[..separator]) + .map_err(|_| CaptureError::new("malformed Git tree header"))?; + let path = utf8_path(&record[separator + 1..])?; + let mut fields = header.split_ascii_whitespace(); + let mode = fields.next().unwrap_or_default(); + let object_type = fields.next().unwrap_or_default(); + let object_id = fields.next().unwrap_or_default(); + if fields.next().is_some() { + return Err(CaptureError::new("malformed Git tree entry")); + } + let file_mode = match mode { + "100644" => 0o644, + "100755" => 0o755, + other => { + return Err(CaptureError::new(format!( + "unsupported Git entry mode {other} at {path} (symlink/submodule)" + ))) + } + }; + if object_type != "blob" || !valid_object_id(object_id) { + return Err(CaptureError::new("invalid Git blob identity")); + } + let size = git(repository, &["cat-file", "-s", object_id])?; + let size_text = std::str::from_utf8(&size) + .map_err(|_| CaptureError::new("Git blob size is not UTF-8"))? + .trim(); + let size = size_text + .parse::() + .map_err(|_| CaptureError::new("invalid Git blob size"))?; + if size > MAX_FILE_BYTES { + return Err(CaptureError::new(format!("oversized Git blob: {path}"))); + } + let bytes = git(repository, &["cat-file", "blob", object_id])?; + if bytes.len() != size { + return Err(CaptureError::new(format!("Git blob size changed: {path}"))); + } + add_file(&mut body, &mut seen, &mut total, path, file_mode, bytes)?; + } + } + body.files.sort_by(|left, right| left.path.cmp(&right.path)); + Ok(body) +} + +fn add_file( + body: &mut CaptureBody, + seen: &mut HashSet, + total: &mut usize, + path: &str, + mode: u32, + bytes: Vec, +) -> Result<(), CaptureError> { + validate_relative_path(path)?; + if bytes.len() > MAX_FILE_BYTES { + return Err(CaptureError::new(format!("file exceeds 8 MiB: {path}"))); + } + if bytes.starts_with(b"version https://git-lfs.github.com/spec/v1\n") { + return Err(CaptureError::new(format!( + "unresolved Git LFS pointer: {path}" + ))); + } + let folded = path.to_lowercase(); + if !seen.insert(folded) { + return Err(CaptureError::new(format!( + "duplicate or case-colliding source path: {path}" + ))); + } + *total = total + .checked_add(bytes.len()) + .ok_or_else(|| CaptureError::new("snapshot byte count overflow"))?; + if *total > MAX_SNAPSHOT_BYTES { + return Err(CaptureError::new("snapshot exceeds 128 MiB")); + } + if body.files.len() >= MAX_FILES { + return Err(CaptureError::new("snapshot exceeds 10000 files")); + } + let digest = Sha256Digest::of(&bytes).to_hex(); + body.files.push(SnapshotFile { + path: path.to_owned(), + mode, + sha256: digest.clone(), + size: i64::try_from(bytes.len()) + .map_err(|_| CaptureError::new("file size does not fit snapshot schema"))?, + }); + body.objects.insert(digest, bytes); + Ok(()) +} + +fn snapshot_id(repository: &str, files: &[SnapshotFile]) -> Result { + #[derive(Serialize)] + struct Identity<'a> { + #[serde(rename = "Repository")] + repository: &'a str, + #[serde(rename = "Files")] + files: &'a [SnapshotFile], + } + let json = serde_json::to_vec(&Identity { repository, files }) + .map_err(|error| CaptureError::new(format!("serialize snapshot identity: {error}")))?; + // Go's encoding/json escapes HTML and the two JavaScript line separators. + // The model's identity hash depends on those exact bytes. + let json = go_json_escape(&json); + Ok(format!("snap_{}", Sha256Digest::of(&json).to_hex())) +} + +fn go_json_escape(json: &[u8]) -> Vec { + let text = String::from_utf8_lossy(json); + text.replace('&', "\\u0026") + .replace('<', "\\u003c") + .replace('>', "\\u003e") + .replace('\u{2028}', "\\u2028") + .replace('\u{2029}', "\\u2029") + .into_bytes() +} + +fn discover(repository: &Path) -> Result { + let path = if repository.as_os_str().is_empty() { + std::env::current_dir().map_err(|error| CaptureError::new(error.to_string()))? + } else { + lexical_absolute(repository)? + }; + let output = git(&path, &["rev-parse", "--show-toplevel"])?; + let root = std::str::from_utf8(&output) + .map_err(|_| CaptureError::new("repository root is not UTF-8"))? + .strip_suffix('\n') + .ok_or_else(|| CaptureError::new("Git returned an unterminated repository path"))?; + if root.contains(['\r', '\n']) { + return Err(CaptureError::new( + "repository root containing a newline is unsupported", + )); + } + fs::canonicalize(root).map_err(|error| CaptureError::new(error.to_string())) +} + +fn resolve(repository: &Path, reference: &str) -> Result { + if reference.is_empty() + || reference.starts_with('-') + || reference.contains('\0') + || reference.len() > 1024 + { + return Err(CaptureError::new("invalid Git reference")); + } + let output = git( + repository, + &[ + "rev-parse", + "--verify", + "--end-of-options", + &format!("{reference}^{{commit}}"), + ], + )?; + let object_id = std::str::from_utf8(&output) + .map_err(|_| CaptureError::new("Git returned a non-UTF-8 commit identity"))? + .trim(); + if !valid_object_id(object_id) { + return Err(CaptureError::new("Git returned an invalid commit identity")); + } + Ok(object_id.to_owned()) +} + +fn valid_object_id(value: &str) -> bool { + matches!(value.len(), 40 | 64) + && value + .bytes() + .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte)) +} + +fn utf8_path(bytes: &[u8]) -> Result<&str, CaptureError> { + std::str::from_utf8(bytes).map_err(|_| CaptureError::new("repository path is not valid UTF-8")) +} + +fn git(repository: &Path, arguments: &[&str]) -> Result, CaptureError> { + if arguments.is_empty() { + return Err(CaptureError::new("Git requires at least one argument")); + } + let mut command = Command::new("git"); + command.args([ + "--no-optional-locks", + "-c", + &format!("core.hooksPath={}", null_device()), + "-c", + "core.fsmonitor=false", + "-c", + "core.untrackedCache=false", + "-c", + "core.pager=cat", + "-c", + "color.ui=false", + "-c", + "protocol.file.allow=never", + "-C", + ]); + command.arg(repository).args(arguments); + command.env_clear(); + for (key, value) in std::env::vars_os() { + if !key + .to_string_lossy() + .to_ascii_uppercase() + .starts_with("GIT_") + { + command.env(key, value); + } + } + command.env("GIT_CONFIG_NOSYSTEM", "1"); + command.env("GIT_CONFIG_GLOBAL", null_device()); + command.env("GIT_TERMINAL_PROMPT", "0"); + command.env("GIT_NO_REPLACE_OBJECTS", "1"); + command.env("GIT_OPTIONAL_LOCKS", "0"); + command.env("LC_ALL", "C"); + command.stdout(Stdio::piped()).stderr(Stdio::piped()); + let mut child = command + .spawn() + .map_err(|error| CaptureError::new(format!("start Git: {error}")))?; + let stdout = child + .stdout + .take() + .ok_or_else(|| CaptureError::new("Git stdout pipe unavailable"))?; + let stderr = child + .stderr + .take() + .ok_or_else(|| CaptureError::new("Git stderr pipe unavailable"))?; + let stdout_reader = thread::spawn(move || read_limited(stdout, MAX_SNAPSHOT_BYTES)); + let stderr_reader = thread::spawn(move || read_limited(stderr, MAX_GIT_STDERR)); + let deadline = Instant::now() + GIT_TIMEOUT; + let status = loop { + match child.try_wait() { + Ok(Some(status)) => break status, + Ok(None) if Instant::now() < deadline => thread::sleep(Duration::from_millis(5)), + Ok(None) => { + let _ = child.kill(); + let _ = child.wait(); + let _ = stdout_reader.join(); + let _ = stderr_reader.join(); + return Err(CaptureError::new("Git command timed out")); + } + Err(error) => { + let _ = child.kill(); + let _ = child.wait(); + let _ = stdout_reader.join(); + let _ = stderr_reader.join(); + return Err(CaptureError::new(format!("wait for Git: {error}"))); + } + } + }; + let output = stdout_reader + .join() + .map_err(|_| CaptureError::new("Git stdout reader panicked"))? + .map_err(|error| CaptureError::new(format!("read Git stdout: {error}")))?; + let error_output = stderr_reader + .join() + .map_err(|_| CaptureError::new("Git stderr reader panicked"))? + .map_err(|error| CaptureError::new(format!("read Git stderr: {error}")))?; + if !status.success() { + return Err(CaptureError::new(format!( + "git {} failed: {}", + arguments[0], + String::from_utf8_lossy(&error_output).trim() + ))); + } + Ok(output) +} + +fn read_limited(reader: impl Read, maximum: usize) -> io::Result> { + let limit = u64::try_from(maximum).unwrap_or(u64::MAX).saturating_add(1); + let mut output = Vec::new(); + reader.take(limit).read_to_end(&mut output)?; + if output.len() > maximum { + return Err(io::Error::other("Git output exceeded capture limit")); + } + Ok(output) +} + +fn null_device() -> &'static str { + if cfg!(windows) { + "NUL" + } else { + "/dev/null" + } +} + +fn lexical_absolute(path: &Path) -> Result { + let absolute = if path.is_absolute() { + path.to_path_buf() + } else { + std::env::current_dir() + .map_err(|error| CaptureError::new(error.to_string()))? + .join(path) + }; + let mut normalized = PathBuf::new(); + for component in absolute.components() { + match component { + Component::CurDir => {} + Component::ParentDir => { + normalized.pop(); + } + component => normalized.push(component.as_os_str()), + } + } + Ok(normalized) +} + +fn canonicalize_allow_missing(path: &Path) -> Result { + let absolute = lexical_absolute(path)?; + let mut existing = absolute.as_path(); + let mut suffix = Vec::new(); + loop { + match fs::symlink_metadata(existing) { + Ok(_) => break, + Err(error) if error.kind() == io::ErrorKind::NotFound => { + let name = existing + .file_name() + .ok_or_else(|| CaptureError::new("path has no existing ancestor"))?; + suffix.push(name.to_os_string()); + existing = existing + .parent() + .ok_or_else(|| CaptureError::new("path has no existing ancestor"))?; + } + Err(error) => return Err(CaptureError::new(format!("inspect path: {error}"))), + } + } + let mut ancestor = Some(existing); + while let Some(candidate) = ancestor { + let metadata = fs::symlink_metadata(candidate) + .map_err(|error| CaptureError::new(format!("inspect path ancestor: {error}")))?; + if metadata.file_type().is_symlink() && !is_platform_path_symlink(candidate) { + return Err(CaptureError::new(format!( + "unsafe symlink in path ancestor: {}", + candidate.display() + ))); + } + ancestor = candidate.parent(); + } + let canonical = fs::canonicalize(existing) + .map_err(|error| CaptureError::new(format!("resolve path: {error}")))?; + let mut resolved = canonical; + for component in suffix.iter().rev() { + resolved.push(component); + } + Ok(resolved) +} + +fn is_platform_path_symlink(path: &Path) -> bool { + #[cfg(target_os = "macos")] + { + path == Path::new("/tmp") || path == Path::new("/var") + } + #[cfg(not(target_os = "macos"))] + { + let _ = path; + false + } +} + +fn validate_relative_path(path: &str) -> Result<(), CaptureError> { + let candidate = Path::new(path); + if path.is_empty() + || candidate.is_absolute() + || path.contains('\\') + || path.contains('\0') + || path + .split('/') + .any(|component| component.is_empty() || matches!(component, "." | "..")) + || candidate.components().any(|component| { + matches!( + component, + Component::ParentDir | Component::RootDir | Component::Prefix(_) + ) + }) + || candidate.to_string_lossy() != path + { + return Err(CaptureError::new(format!( + "unsafe repository path {path:?}" + ))); + } + for component in path.split('/') { + if component.eq_ignore_ascii_case(".git") { + return Err(CaptureError::new("nested .git paths are unsupported")); + } + } + Ok(()) +} + +fn read_regular(repository: &Path, path: &str) -> io::Result<(Vec, u32)> { + validate_relative_path(path) + .map_err(|error| io::Error::new(io::ErrorKind::InvalidInput, error))?; + let mut current = repository.to_path_buf(); + let parts = path.split('/').collect::>(); + for part in &parts { + current.push(part); + let metadata = fs::symlink_metadata(¤t)?; + if metadata.file_type().is_symlink() { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "symlink source unsupported", + )); + } + } + let metadata = fs::symlink_metadata(¤t)?; + if !metadata.file_type().is_file() { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "special source file unsupported", + )); + } + if metadata.len() > MAX_FILE_BYTES as u64 { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "source file exceeds 8 MiB", + )); + } + let mut file = File::open(¤t)?; + let mut bytes = Vec::new(); + Read::by_ref(&mut file) + .take((MAX_FILE_BYTES as u64) + 1) + .read_to_end(&mut bytes)?; + if bytes.len() > MAX_FILE_BYTES { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "file grew past 8 MiB", + )); + } + #[cfg(unix)] + let mode = { + use std::os::unix::fs::PermissionsExt as _; + if metadata.permissions().mode() & 0o111 != 0 { + 0o755 + } else { + 0o644 + } + }; + #[cfg(not(unix))] + let mode = 0o644; + Ok((bytes, mode)) +} + +#[cfg(test)] +mod tests { + use std::fs; + use std::path::{Path, PathBuf}; + use std::process::Command; + use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; + use std::sync::{Arc, Barrier}; + use std::thread; + use std::time::Duration; + use std::time::{SystemTime, UNIX_EPOCH}; + + use super::{ + add_file, add_worktree, branch_reservation_resource, capture, compare_snapshots, + compose_snapshot, diff, inputs_unchanged, inspect_worktree, list_worktrees, materialize, + merge_snapshots, overlay_snapshot, remove_worktree, replace_worktree_snapshot, + resolve_reference, validate_relative_path, validate_snapshot, verify_retained_snapshot, + CaptureBody, CaptureOptions, SnapshotFile, EXACT_COMMIT_CONSISTENCY, MAX_FILES, + MAX_FILE_BYTES, MAX_SNAPSHOT_BYTES, WORKTREE_CONSISTENCY, + }; + use rover_core::Sha256Digest; + use std::collections::{BTreeMap, HashSet}; + + static SEQUENCE: AtomicU64 = AtomicU64::new(0); + + struct Repo { + container: PathBuf, + root: PathBuf, + } + + impl Repo { + fn new() -> Self { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("clock follows epoch") + .as_nanos(); + let container = std::env::temp_dir().join(format!( + "rover-source-test-{}-{timestamp}-{}", + std::process::id(), + SEQUENCE.fetch_add(1, Ordering::Relaxed) + )); + fs::create_dir_all(&container).expect("create fixture container"); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt as _; + fs::set_permissions(&container, fs::Permissions::from_mode(0o700)) + .expect("make fixture container private"); + } + let root = container.join("repo"); + fs::create_dir(&root).expect("create repository"); + let repo = Self { container, root }; + repo.git(&["init", "-q"]); + repo.git(&["config", "user.email", "rover@example.invalid"]); + repo.git(&["config", "user.name", "Rover Test"]); + repo.git(&["config", "commit.gpgsign", "false"]); + repo + } + + fn git(&self, args: &[&str]) -> String { + let output = Command::new("git") + .args(args) + .current_dir(&self.root) + .output() + .expect("run git fixture command"); + assert!( + output.status.success(), + "git {args:?}: {}", + String::from_utf8_lossy(&output.stderr) + ); + String::from_utf8_lossy(&output.stdout).trim().to_owned() + } + + fn file(&self, name: &str, contents: &[u8]) { + let path = self.root.join(name); + if let Some(parent) = path.parent() { + fs::create_dir_all(parent).expect("create nested parent"); + } + fs::write(path, contents).expect("write fixture file"); + } + + fn commit_file(&self, name: &str, contents: &[u8]) { + self.file(name, contents); + self.git(&["add", "--", name]); + self.git(&["commit", "-q", "-m", "fixture"]); + } + } + + impl Drop for Repo { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.container); + } + } + + fn options(repo: &Path, source_ref: &str, include_untracked: bool) -> CaptureOptions { + CaptureOptions { + repository: repo.to_path_buf(), + state_root: repo.parent().unwrap().join("state"), + source_ref: source_ref.to_owned(), + include_untracked, + } + } + + #[test] + fn committed_capture_reads_exact_git_blobs_and_labels_consistency() { + let repo = Repo::new(); + repo.commit_file("src/value.txt", b"committed bytes"); + let commit = repo.git(&["rev-parse", "HEAD"]); + repo.file("src/value.txt", b"working bytes"); + + let captured = capture(&options(&repo.root, "HEAD", false)).expect("capture commit"); + + assert_eq!(captured.snapshot.commit, commit); + assert_eq!(captured.snapshot.consistency, EXACT_COMMIT_CONSISTENCY); + assert_eq!(captured.snapshot.files.len(), 1); + let file = &captured.snapshot.files[0]; + assert_eq!(file.path, "src/value.txt"); + assert_eq!( + file.size, + i64::try_from(b"committed bytes".len()).expect("fixture length fits i64") + ); + assert_eq!( + captured.objects.get(&file.sha256).unwrap(), + b"committed bytes" + ); + assert_eq!(captured.snapshot.source_ref, "HEAD"); + } + + #[test] + fn committed_capture_does_not_run_checkout_filters() { + let repo = Repo::new(); + let marker = repo.container.join("filter-ran"); + repo.file(".gitattributes", b"data.txt filter=probe\n"); + repo.file("data.txt", b"committed source bytes"); + repo.git(&["add", "--", ".gitattributes", "data.txt"]); + repo.git(&[ + "config", + "filter.probe.smudge", + &format!("touch {} && cat", marker.display()), + ]); + repo.git(&["commit", "-q", "-m", "filtered fixture"]); + + let captured = capture(&options(&repo.root, "HEAD", false)).expect("capture commit"); + + assert_eq!(captured.snapshot.files.len(), 2); + let data = captured + .snapshot + .files + .iter() + .find(|file| file.path == "data.txt") + .unwrap(); + assert_eq!( + captured.objects.get(&data.sha256).unwrap(), + b"committed source bytes" + ); + assert!(!marker.exists(), "Git checkout filter unexpectedly ran"); + } + + #[test] + fn working_capture_labels_two_reads_and_requires_untracked_opt_in() { + let repo = Repo::new(); + repo.commit_file("tracked.txt", b"base"); + repo.file("tracked.txt", b"working"); + repo.file("new.txt", b"untracked"); + + let without_untracked = capture(&options(&repo.root, "WORKTREE", false)) + .expect("capture tracked working files"); + assert_eq!(without_untracked.snapshot.consistency, WORKTREE_CONSISTENCY); + assert_eq!(without_untracked.snapshot.files.len(), 1); + assert_eq!( + without_untracked + .objects + .get(&without_untracked.snapshot.files[0].sha256) + .unwrap(), + b"working" + ); + + let with_untracked = capture(&options(&repo.root, "WORKTREE", true)) + .expect("capture opted-in untracked files"); + assert_eq!(with_untracked.snapshot.files.len(), 2); + assert_eq!(with_untracked.snapshot.files[0].path, "new.txt"); + assert_eq!(with_untracked.snapshot.files[1].path, "tracked.txt"); + assert_ne!(without_untracked.snapshot.id, with_untracked.snapshot.id); + } + + #[test] + fn capture_refuses_state_root_inside_repository() { + let repo = Repo::new(); + repo.commit_file("file.txt", b"bytes"); + let mut capture_options = options(&repo.root, "HEAD", false); + capture_options.state_root = repo.root.join(".rover"); + + assert!(capture(&capture_options).is_err()); + + capture_options.state_root = repo.container.clone(); + assert!( + capture(&capture_options).is_ok(), + "Go contract permits an ancestor state root outside the repository" + ); + } + + #[test] + fn snapshot_identity_bytes_match_go_json_field_names_and_html_escaping() { + let files = [super::SnapshotFile { + path: ".rs".to_owned(), + mode: 0o644, + sha256: "ab".repeat(32), + size: 3, + }]; + let go_identity_json = format!( + r#"{{"Repository":"repo\u003c\u0026\u003e","Files":[{{"path":"\u003csource\u003e.rs","mode":420,"sha256":"{}","size":3}}]}}"#, + "ab".repeat(32) + ); + let expected = format!( + "snap_{}", + Sha256Digest::of(go_identity_json.as_bytes()).to_hex() + ); + + assert_eq!(super::snapshot_id("repo<&>", &files).unwrap(), expected); + } + + #[test] + fn repository_path_validation_rejects_escape_and_noncanonical_forms() { + for path in [ + "", + ".", + "../outside", + "dir/../outside", + "dir/./file", + "dir//file", + "/absolute", + "dir\\file", + "dir/.git/config", + "dir/.GIT/config", + ] { + assert!( + validate_relative_path(path).is_err(), + "unsafe path unexpectedly accepted: {path:?}" + ); + } + assert!(validate_relative_path("src/module.rs").is_ok()); + } + + #[cfg(unix)] + #[test] + fn working_capture_rejects_symlink_sources() { + let repo = Repo::new(); + repo.commit_file("tracked.txt", b"base"); + let outside = repo.container.join("outside.txt"); + fs::write(&outside, b"outside bytes").unwrap(); + std::os::unix::fs::symlink(&outside, repo.root.join("link.txt")).unwrap(); + repo.git(&["add", "--", "link.txt"]); + + assert!(capture(&options(&repo.root, "WORKTREE", false)).is_err()); + } + + #[cfg(unix)] + #[test] + fn committed_capture_rejects_git_symlink_entries() { + let repo = Repo::new(); + repo.file("target.txt", b"target"); + std::os::unix::fs::symlink("target.txt", repo.root.join("link.txt")).unwrap(); + repo.git(&["add", "--", "target.txt", "link.txt"]); + repo.git(&["commit", "-q", "-m", "symlink fixture"]); + + let error = capture(&options(&repo.root, "HEAD", false)).unwrap_err(); + assert!(error.to_string().contains("symlink/submodule")); + } + + #[test] + fn capture_rejects_unresolved_lfs_pointer_files() { + let repo = Repo::new(); + repo.commit_file( + "pointer.dat", + b"version https://git-lfs.github.com/spec/v1\noid sha256:deadbeef\nsize 1\n", + ); + + let error = capture(&options(&repo.root, "HEAD", false)).unwrap_err(); + assert!(error.to_string().contains("unresolved Git LFS pointer")); + } + + #[test] + fn working_capture_rejects_gitlinks_before_reading_worktree_paths() { + let repo = Repo::new(); + repo.commit_file("tracked.txt", b"base"); + let child_commit = repo.git(&["rev-parse", "HEAD"]); + repo.git(&[ + "update-index", + "--add", + "--cacheinfo", + &format!("160000,{child_commit},modules/child"), + ]); + + let error = capture(&options(&repo.root, "WORKTREE", false)).unwrap_err(); + assert!(error.to_string().contains("submodules are unsupported")); + + repo.git(&["commit", "-q", "-m", "gitlink fixture"]); + let error = capture(&options(&repo.root, "HEAD", false)).unwrap_err(); + assert!(error + .to_string() + .contains("unsupported Git entry mode 160000")); + } + + #[test] + fn capture_rejects_case_collisions_when_filesystem_can_represent_them() { + let repo = Repo::new(); + repo.commit_file("tracked.txt", b"base"); + repo.file("Duplicate.txt", b"upper"); + repo.file("duplicate.txt", b"lower"); + let spellings = fs::read_dir(&repo.root) + .unwrap() + .map(|entry| entry.unwrap().file_name().to_string_lossy().to_lowercase()) + .filter(|name| name == "duplicate.txt") + .count(); + if spellings < 2 { + assert_eq!( + fs::read(repo.root.join("Duplicate.txt")).unwrap(), + b"lower", + "case-insensitive filesystem should alias the two path spellings" + ); + return; + } + + let error = capture(&options(&repo.root, "WORKTREE", true)).unwrap_err(); + assert!(error.to_string().contains("case-colliding")); + } + + #[test] + fn git_path_parser_rejects_non_utf8_paths() { + assert!(super::utf8_path(b"valid/path").is_ok()); + assert!(super::utf8_path(b"invalid-\xff-path").is_err()); + } + + #[test] + fn capture_enforces_per_file_snapshot_and_file_count_limits() { + let repo = Repo::new(); + repo.commit_file("tracked.txt", b"base"); + repo.file("oversized.bin", &vec![b'x'; MAX_FILE_BYTES + 1]); + assert!(capture(&options(&repo.root, "WORKTREE", true)).is_err()); + + let mut body = CaptureBody { + commit: String::new(), + files: Vec::new(), + objects: BTreeMap::new(), + }; + let mut paths = HashSet::new(); + let mut total = MAX_SNAPSHOT_BYTES; + assert!(add_file( + &mut body, + &mut paths, + &mut total, + "overflow.bin", + 0o644, + vec![b'x'] + ) + .is_err()); + + let mut collision_body = CaptureBody { + commit: String::new(), + files: Vec::new(), + objects: BTreeMap::new(), + }; + let mut collision_paths = HashSet::new(); + let mut collision_total = 0; + add_file( + &mut collision_body, + &mut collision_paths, + &mut collision_total, + "Case.txt", + 0o644, + b"first".to_vec(), + ) + .unwrap(); + assert!(add_file( + &mut collision_body, + &mut collision_paths, + &mut collision_total, + "case.txt", + 0o644, + b"second".to_vec() + ) + .is_err()); + + body.files = (0..MAX_FILES) + .map(|index| SnapshotFile { + path: format!("file-{index}"), + mode: 0o644, + sha256: String::new(), + size: 0, + }) + .collect(); + total = 0; + assert!(add_file( + &mut body, + &mut paths, + &mut total, + "last.bin", + 0o644, + Vec::new() + ) + .is_err()); + } + + #[cfg(unix)] + #[test] + fn capture_into_state_persists_record_and_verified_blobs() { + let repo = Repo::new(); + repo.commit_file("data.txt", b"persist me"); + let state_path = repo.container.join("state"); + let state = rover_store::files::StateRoot::open(&state_path).expect("open state root"); + + let snapshot = super::capture_into_state(&options(&repo.root, "HEAD", false), &state) + .expect("capture into state"); + + let saved = state + .records() + .get_typed::("snapshot", &snapshot.id) + .expect("read stored snapshot"); + assert_eq!(saved, snapshot); + assert_eq!( + state.read_blob(&snapshot.files[0].sha256).unwrap(), + b"persist me" + ); + } + + #[cfg(unix)] + #[test] + fn snapshot_materializes_exact_bytes_modes_and_rejects_modified_inputs() { + use std::os::unix::fs::PermissionsExt as _; + + let repo = Repo::new(); + repo.commit_file("nested/run.sh", b"#!/bin/sh\necho frozen\n"); + repo.git(&["update-index", "--chmod=+x", "nested/run.sh"]); + repo.git(&["commit", "-q", "-m", "executable fixture"]); + let state_path = repo.container.join("state"); + let state = rover_store::files::StateRoot::open(&state_path).expect("open state root"); + let snapshot = super::capture_into_state(&options(&repo.root, "HEAD", false), &state) + .expect("capture snapshot"); + + validate_snapshot(&snapshot).expect("validate captured snapshot"); + let destination = repo.container.join("materialized"); + materialize(&state, &snapshot, &destination).expect("materialize snapshot"); + assert_eq!( + fs::read(destination.join("nested/run.sh")).unwrap(), + b"#!/bin/sh\necho frozen\n" + ); + assert_eq!( + fs::metadata(destination.join("nested/run.sh")) + .unwrap() + .permissions() + .mode() + & 0o777, + 0o755 + ); + inputs_unchanged(&snapshot, &destination).expect("verify unchanged inputs"); + + fs::write(destination.join("nested/run.sh"), b"changed").unwrap(); + assert!(inputs_unchanged(&snapshot, &destination).is_err()); + } + + #[cfg(unix)] + #[test] + fn materialization_rejects_existing_files_without_replacing_them() { + let repo = Repo::new(); + repo.commit_file("nested/value.txt", b"frozen bytes"); + let state_path = repo.container.join("state"); + let state = rover_store::files::StateRoot::open(&state_path).expect("open state root"); + let snapshot = super::capture_into_state(&options(&repo.root, "HEAD", false), &state) + .expect("capture snapshot"); + let destination = repo.container.join("materialized"); + fs::create_dir_all(destination.join("nested")).unwrap(); + fs::write(destination.join("nested/value.txt"), b"preserve me").unwrap(); + + assert!(materialize(&state, &snapshot, &destination).is_err()); + assert_eq!( + fs::read(destination.join("nested/value.txt")).unwrap(), + b"preserve me" + ); + } + + #[cfg(unix)] + #[test] + fn materialization_checks_all_objects_before_creating_destination() { + let repo = Repo::new(); + repo.commit_file("value.txt", b"snapshot bytes"); + let snapshot = capture(&options(&repo.root, "HEAD", false)) + .expect("capture snapshot") + .snapshot; + let state_path = repo.container.join("empty-state"); + let state = rover_store::files::StateRoot::open(&state_path).expect("open empty state"); + let destination = repo.container.join("should-not-exist"); + + assert!(materialize(&state, &snapshot, &destination).is_err()); + assert!(!destination.exists()); + } + + #[test] + fn snapshot_validation_rejects_identity_mode_digest_and_tree_conflicts() { + let repo = Repo::new(); + repo.commit_file("dir/value.txt", b"snapshot bytes"); + let captured = capture(&options(&repo.root, "HEAD", false)).expect("capture snapshot"); + validate_snapshot(&captured.snapshot).expect("valid captured snapshot"); + + let mut bad_identity = captured.snapshot.clone(); + bad_identity.id.push('x'); + assert!(validate_snapshot(&bad_identity).is_err()); + + let mut bad_mode = captured.snapshot.clone(); + bad_mode.files[0].mode = 0o600; + bad_mode.id = super::snapshot_id(&bad_mode.repository, &bad_mode.files).unwrap(); + assert!(validate_snapshot(&bad_mode).is_err()); + + let mut bad_digest = captured.snapshot.clone(); + bad_digest.files[0].sha256 = "z".repeat(64); + bad_digest.id = super::snapshot_id(&bad_digest.repository, &bad_digest.files).unwrap(); + assert!(validate_snapshot(&bad_digest).is_err()); + + let mut conflict = captured.snapshot.clone(); + conflict.files.push(SnapshotFile { + path: "dir".to_owned(), + mode: 0o644, + sha256: Sha256Digest::of(b"file").to_hex(), + size: 4, + }); + conflict + .files + .sort_by(|left, right| left.path.cmp(&right.path)); + conflict.id = super::snapshot_id(&conflict.repository, &conflict.files).unwrap(); + assert!(validate_snapshot(&conflict).is_err()); + } + + #[cfg(unix)] + #[test] + fn binary_diff_from_retained_snapshots_applies_to_exact_candidate_identity() { + let repo = Repo::new(); + repo.file("a.txt", b"old\n"); + repo.file("drop.txt", b"remove\n"); + repo.file("space name.txt", b"before\n"); + repo.git(&["add", "--", "a.txt", "drop.txt", "space name.txt"]); + repo.git(&["commit", "-q", "-m", "base"]); + let state_path = repo.container.join("state"); + let state = rover_store::files::StateRoot::open(&state_path).expect("open state root"); + let base = super::capture_into_state(&options(&repo.root, "HEAD", false), &state) + .expect("capture base"); + + repo.file("a.txt", b"new\n"); + repo.file("space name.txt", b"after\n"); + repo.file("binary.bin", b"\0\x01\x02"); + fs::remove_file(repo.root.join("drop.txt")).unwrap(); + repo.git(&["add", "--all"]); + repo.git(&["commit", "-q", "-m", "candidate"]); + let candidate = super::capture_into_state(&options(&repo.root, "HEAD", false), &state) + .expect("capture candidate"); + + let patch = diff(&state, &base, &candidate).expect("generate retained-byte patch"); + assert!(String::from_utf8_lossy(&patch).contains("diff --git a/a.txt b/a.txt")); + repo.git(&["checkout", "--detach", &base.commit]); + let patch_file = repo.container.join("candidate.patch"); + fs::write(&patch_file, &patch).unwrap(); + repo.git(&["apply", "--check", patch_file.to_str().unwrap()]); + repo.git(&["apply", patch_file.to_str().unwrap()]); + let applied = + capture(&options(&repo.root, "WORKTREE", true)).expect("capture applied patch"); + assert_eq!(applied.snapshot.id, candidate.id); + } + + #[test] + fn candidate_comparison_sorts_paths_and_reports_policy_and_test_deletions() { + let repo = Repo::new(); + repo.file("tests/a_test.go", b"test"); + repo.file("value", b"base"); + repo.git(&["add", "--", "tests/a_test.go", "value"]); + repo.git(&["commit", "-q", "-m", "base"]); + let base = capture(&options(&repo.root, "HEAD", false)) + .expect("capture base") + .snapshot; + fs::remove_file(repo.root.join("tests/a_test.go")).unwrap(); + repo.file(".github/workflows/ci.yml", b"changed"); + repo.file(".rover/config.json", b"{}"); + let candidate = capture(&options(&repo.root, "WORKTREE", true)) + .expect("capture candidate") + .snapshot; + + let inspection = compare_snapshots(&base, &candidate).expect("compare candidates"); + assert_eq!( + inspection + .changes + .iter() + .map(|change| change.path.as_str()) + .collect::>(), + [ + ".github/workflows/ci.yml", + ".rover/config.json", + "tests/a_test.go" + ] + ); + assert_eq!(inspection.findings.len(), 3); + assert_eq!( + inspection.findings[0].rule, + "verification_configuration_changed" + ); + assert_eq!(inspection.findings[2].rule, "test_file_deleted"); + } + + #[cfg(unix)] + #[test] + fn worktree_lifecycle_lists_inspects_and_removes_only_clean_worktrees() { + let repo = Repo::new(); + repo.commit_file("src/value.txt", b"base bytes"); + let state_path = repo.container.join("state"); + let state = rover_store::files::StateRoot::open(&state_path).expect("open state root"); + let base = super::capture_into_state(&options(&repo.root, "HEAD", false), &state) + .expect("capture base"); + let destination = state + .task_workspace_path("task-worktree-test") + .expect("prepare private task worktree root"); + + assert!(remove_worktree(&repo.root, &repo.root).is_err()); + add_worktree(&state, &base, &destination).expect("add detached worktree"); + assert!(add_worktree(&state, &base, &destination).is_err()); + let entries = list_worktrees(&repo.root).expect("list worktrees"); + let canonical_destination = fs::canonicalize(&destination).unwrap(); + let canonical_destination = canonical_destination.to_string_lossy(); + let entry = entries + .iter() + .find(|entry| entry.path == canonical_destination) + .unwrap_or_else(|| { + panic!( + "new worktree is not listed: destination={destination:?}, entries={entries:?}" + ) + }); + assert!(entry.detached); + assert_eq!(entry.head.as_deref(), Some(base.commit.as_str())); + assert_eq!( + fs::read(destination.join("src/value.txt")).unwrap(), + b"base bytes" + ); + + fs::write(destination.join("src/value.txt"), b"changed bytes").unwrap(); + let inspection = inspect_worktree(&state, &base, &destination).expect("inspect worktree"); + assert!(inspection.dirty); + assert_eq!(inspection.changes[0].path, "src/value.txt"); + assert_eq!(inspection.snapshot.consistency, WORKTREE_CONSISTENCY); + assert!(remove_worktree(&repo.root, &destination).is_err()); + assert!(destination.join("src/value.txt").exists()); + + fs::write(destination.join("src/value.txt"), b"base bytes").unwrap(); + let clean = inspect_worktree(&state, &base, &destination).expect("inspect clean worktree"); + assert!(!clean.dirty); + remove_worktree(&repo.root, &destination).expect("remove clean worktree"); + assert!(!destination.exists()); + assert!(list_worktrees(&repo.root) + .unwrap() + .iter() + .all(|entry| entry.path != canonical_destination)); + } + + #[cfg(unix)] + #[test] + fn worktree_create_does_not_run_smudge_filters_and_remove_preserves_ignored_data() { + let repo = Repo::new(); + repo.file(".gitattributes", b"data.txt filter=probe\n"); + repo.file(".gitignore", b"ignored.txt\n"); + repo.file("data.txt", b"frozen bytes"); + repo.git(&["add", "--", ".gitattributes", ".gitignore", "data.txt"]); + let marker = repo.container.join("filter-ran"); + repo.git(&[ + "config", + "filter.probe.smudge", + &format!("touch {} && cat", marker.display()), + ]); + repo.git(&["commit", "-q", "-m", "filtered fixture"]); + let state_path = repo.container.join("state"); + let state = rover_store::files::StateRoot::open(&state_path).expect("open state root"); + let base = super::capture_into_state(&options(&repo.root, "HEAD", false), &state) + .expect("capture base"); + let destination = repo.container.join("filter-worktree"); + + add_worktree(&state, &base, &destination).expect("add filter-free worktree"); + assert_eq!( + fs::read(destination.join("data.txt")).unwrap(), + b"frozen bytes" + ); + assert!(!marker.exists()); + fs::write( + destination.join("ignored.txt"), + b"preserve ignored user data", + ) + .unwrap(); + assert!(remove_worktree(&repo.root, &destination).is_err()); + assert_eq!( + fs::read(destination.join("ignored.txt")).unwrap(), + b"preserve ignored user data" + ); + fs::remove_file(destination.join("ignored.txt")).unwrap(); + remove_worktree(&repo.root, &destination).expect("remove clean worktree"); + } + + #[cfg(unix)] + #[test] + fn concurrent_worktree_admission_is_unique_and_cleanup_is_idempotent() { + let repo = Repo::new(); + repo.commit_file("value.txt", b"frozen base"); + let state_path = repo.container.join("state"); + let state = + Arc::new(rover_store::files::StateRoot::open(&state_path).expect("open state root")); + let base = Arc::new( + super::capture_into_state(&options(&repo.root, "HEAD", false), &state) + .expect("capture base"), + ); + let destination = state + .task_workspace_path("task-admission-race") + .expect("prepare worktree parent"); + let barrier = Arc::new(Barrier::new(3)); + let workers = [0, 1] + .into_iter() + .map(|_| { + let state = Arc::clone(&state); + let base = Arc::clone(&base); + let destination = destination.clone(); + let barrier = Arc::clone(&barrier); + thread::spawn(move || { + barrier.wait(); + add_worktree(&state, &base, &destination) + }) + }) + .collect::>(); + barrier.wait(); + let admitted = workers + .into_iter() + .map(|worker| worker.join().expect("admission worker panicked")) + .collect::>(); + assert_eq!(admitted.iter().filter(|result| result.is_ok()).count(), 1); + let canonical_destination = fs::canonicalize(&destination).unwrap(); + let canonical_destination = canonical_destination.to_string_lossy().into_owned(); + assert_eq!( + list_worktrees(&repo.root) + .unwrap() + .iter() + .filter(|entry| entry.path == canonical_destination) + .count(), + 1 + ); + assert_eq!( + fs::read(destination.join("value.txt")).unwrap(), + b"frozen base" + ); + + let barrier = Arc::new(Barrier::new(3)); + let repo_root = repo.root.clone(); + let workers = [0, 1] + .into_iter() + .map(|_| { + let barrier = Arc::clone(&barrier); + let repo_root = repo_root.clone(); + let destination = destination.clone(); + thread::spawn(move || { + barrier.wait(); + remove_worktree(&repo_root, &destination) + }) + }) + .collect::>(); + barrier.wait(); + let removed = workers + .into_iter() + .map(|worker| worker.join().expect("cleanup worker panicked")) + .collect::>(); + assert!(removed.iter().any(Result::is_ok)); + assert!(!destination.exists()); + assert!(list_worktrees(&repo.root) + .unwrap() + .iter() + .all(|entry| entry.path != canonical_destination)); + } + + #[cfg(unix)] + #[test] + fn concurrent_worktree_capture_never_returns_bytes_with_a_wrong_digest() { + let repo = Repo::new(); + let first = vec![b'A'; 4096]; + let second = vec![b'B'; 4096]; + repo.commit_file("value.bin", &first); + let writer = repo.root.join(".value-swap"); + let repository = repo.root.clone(); + let stop = Arc::new(AtomicBool::new(false)); + let barrier = Arc::new(Barrier::new(2)); + let writer_stop = Arc::clone(&stop); + let writer_barrier = Arc::clone(&barrier); + let writer_first = first.clone(); + let writer_second = second.clone(); + let thread = thread::spawn(move || { + writer_barrier.wait(); + let mut use_second = true; + while !writer_stop.load(Ordering::Acquire) { + let bytes = if use_second { + &writer_second + } else { + &writer_first + }; + fs::write(&writer, bytes).expect("write atomic replacement file"); + fs::rename(&writer, repository.join("value.bin")) + .expect("replace tracked input atomically"); + use_second = !use_second; + thread::sleep(Duration::from_millis(3)); + } + }); + barrier.wait(); + let mut successful_captures = 0; + for _ in 0..16 { + if let Ok(captured) = capture(&options(&repo.root, "WORKTREE", false)) { + successful_captures += 1; + validate_snapshot(&captured.snapshot).expect("captured manifest validates"); + for file in &captured.snapshot.files { + let bytes = captured + .objects + .get(&file.sha256) + .expect("captured object exists"); + assert_eq!(i64::try_from(bytes.len()).unwrap(), file.size); + assert_eq!(Sha256Digest::of(bytes).to_hex(), file.sha256); + assert!(bytes == &first || bytes == &second); + } + } + } + stop.store(true, Ordering::Release); + thread.join().expect("workspace writer panicked"); + assert!( + successful_captures > 0, + "capture should succeed during writer activity" + ); + } + + #[test] + fn porcelain_worktree_parser_rejects_unknown_or_invalid_metadata() { + let parsed = super::parse_worktree_list( + b"worktree /tmp/main\0HEAD 0123456789012345678901234567890123456789\0branch refs/heads/main\0\0worktree /tmp/detached\0HEAD abcdefabcdefabcdefabcdefabcdefabcdefabcd\0detached\0locked keep\0\0", + ) + .unwrap(); + assert_eq!(parsed.len(), 2); + assert_eq!(parsed[1].locked.as_deref(), Some("keep")); + assert!(super::parse_worktree_list(b"worktree /tmp/repo\0new-field value\0\0").is_err()); + assert!(super::parse_worktree_list(b"worktree /tmp/repo\0HEAD invalid\0\0").is_err()); + } + + #[test] + fn base_ref_resolution_and_branch_reservations_are_repository_scoped() { + let repo = Repo::new(); + repo.commit_file("value.txt", b"base"); + let commit = repo.git(&["rev-parse", "HEAD"]); + repo.git(&["branch", "topic/one"]); + assert_eq!(resolve_reference(&repo.root, "topic/one").unwrap(), commit); + assert!(resolve_reference(&repo.root, "--help").is_err()); + assert!(resolve_reference(&repo.root, "HEAD^{tree}").is_err()); + + let branch = branch_reservation_resource(&repo.root, "topic/one").unwrap(); + rover_core::Id::parse(branch.clone()).expect("branch resource is a valid Rover ID"); + assert_eq!( + branch, + branch_reservation_resource(&repo.root, "topic/one").unwrap() + ); + assert_ne!( + branch, + branch_reservation_resource(&repo.root, "topic/two").unwrap() + ); + assert!(branch_reservation_resource(&repo.root, "bad..name").is_err()); + + let other = Repo::new(); + other.commit_file("value.txt", b"base"); + assert_ne!( + branch, + branch_reservation_resource(&other.root, "topic/one").unwrap() + ); + + let state_path = repo.container.join("state"); + let state = rover_store::files::StateRoot::open(&state_path).expect("open state root"); + let resources = vec![branch]; + state + .records() + .acquire_resources("task-owner-a", &resources, 4) + .expect("reserve branch resource"); + assert!(state + .records() + .acquire_resources("task-owner-b", &resources, 4) + .is_err()); + state + .records() + .release_resources("task-owner-a") + .expect("release branch reservation"); + state + .records() + .acquire_resources("task-owner-b", &resources, 4) + .expect("branch becomes available after owner release"); + } + + #[cfg(unix)] + #[test] + fn child_snapshot_replaces_files_without_moving_exact_base_commit() { + let repo = Repo::new(); + repo.commit_file("src/value.txt", b"base bytes"); + let base_commit = repo.git(&["rev-parse", "HEAD"]); + let state_path = repo.container.join("state"); + let state = rover_store::files::StateRoot::open(&state_path).expect("open state root"); + let base = super::capture_into_state(&options(&repo.root, "HEAD", false), &state) + .expect("capture base"); + + repo.file("src/value.txt", b"initial snapshot bytes"); + repo.git(&["add", "--", "src/value.txt"]); + repo.git(&["commit", "-q", "-m", "initial snapshot"]); + let initial = super::capture_into_state(&options(&repo.root, "HEAD", false), &state) + .expect("capture initial snapshot"); + let destination = state + .task_workspace_path("task-child-test") + .expect("prepare task workspace"); + add_worktree(&state, &base, &destination).expect("add exact-base worktree"); + + replace_worktree_snapshot(&state, &base, &initial, &destination) + .expect("replace base files with initial snapshot"); + assert_eq!( + fs::read(destination.join("src/value.txt")).unwrap(), + b"initial snapshot bytes" + ); + assert_eq!( + repo.git(&["-C", destination.to_str().unwrap(), "rev-parse", "HEAD"]), + base_commit + ); + let mut child_options = options(&destination, "WORKTREE", true); + child_options.state_root = state.path().to_path_buf(); + let observed = capture(&child_options).expect("capture child"); + assert_eq!(observed.snapshot.commit, base_commit); + assert_eq!( + observed + .objects + .get(&observed.snapshot.files[0].sha256) + .unwrap(), + b"initial snapshot bytes" + ); + } + + #[cfg(unix)] + #[test] + fn child_snapshot_replacement_refuses_dirty_base_without_changing_it() { + let repo = Repo::new(); + repo.commit_file("src/value.txt", b"base bytes"); + let state_path = repo.container.join("state"); + let state = rover_store::files::StateRoot::open(&state_path).expect("open state root"); + let base = super::capture_into_state(&options(&repo.root, "HEAD", false), &state) + .expect("capture base"); + repo.file("src/value.txt", b"new snapshot"); + repo.git(&["add", "--", "src/value.txt"]); + repo.git(&["commit", "-q", "-m", "initial"]); + let initial = super::capture_into_state(&options(&repo.root, "HEAD", false), &state) + .expect("capture initial"); + let destination = state + .task_workspace_path("task-dirty-test") + .expect("prepare task workspace"); + add_worktree(&state, &base, &destination).expect("add worktree"); + fs::write(destination.join("src/value.txt"), b"concurrent edit").unwrap(); + + assert!(replace_worktree_snapshot(&state, &base, &initial, &destination).is_err()); + assert_eq!( + fs::read(destination.join("src/value.txt")).unwrap(), + b"concurrent edit" + ); + } + + #[test] + fn replacement_layout_conflicts_fail_before_any_filesystem_changes() { + let base = [SnapshotFile { + path: "tree/child.txt".to_owned(), + mode: 0o644, + sha256: Sha256Digest::of(b"child").to_hex(), + size: 5, + }]; + let initial = [SnapshotFile { + path: "tree".to_owned(), + mode: 0o644, + sha256: Sha256Digest::of(b"file").to_hex(), + size: 4, + }]; + assert!(super::validate_replacement_layout(&base, &initial).is_err()); + assert!(super::validate_replacement_layout(&initial, &base).is_err()); + } + + #[cfg(unix)] + #[test] + fn exact_base_overlay_and_merge_accept_disjoint_edits_and_reject_conflicts() { + let repo = Repo::new(); + repo.file("a.txt", b"a0"); + repo.file("b.txt", b"b0"); + repo.file("gone.txt", b"gone"); + repo.git(&["add", "--", "a.txt", "b.txt", "gone.txt"]); + repo.git(&["commit", "-q", "-m", "base"]); + let state_path = repo.container.join("state"); + let state = rover_store::files::StateRoot::open(&state_path).expect("open state root"); + let base = super::capture_into_state(&options(&repo.root, "HEAD", false), &state) + .expect("capture merge base"); + let put = |path: &str, bytes: &[u8], mut files: Vec| { + let digest = state.put_blob(bytes).unwrap().to_hex(); + if let Some(file) = files.iter_mut().find(|file| file.path == path) { + file.sha256 = digest; + file.size = i64::try_from(bytes.len()).unwrap(); + } else { + files.push(SnapshotFile { + path: path.to_owned(), + mode: 0o644, + sha256: digest, + size: i64::try_from(bytes.len()).unwrap(), + }); + } + files + }; + + let mut first_files = put("a.txt", b"a1", base.files.clone()); + first_files.retain(|file| file.path != "gone.txt"); + let first = compose_snapshot(&state, &base, first_files, "test:first") + .expect("compose first candidate"); + let second_files = put("b.txt", b"b1", base.files.clone()); + let second_files = put("new.txt", b"new", second_files); + let second = compose_snapshot(&state, &base, second_files, "test:second") + .expect("compose second candidate"); + + let merged = merge_snapshots(&state, &base, &[first.clone(), second.clone()]) + .expect("merge disjoint candidates"); + let verification = verify_retained_snapshot(&state, &merged.id) + .expect("verify persisted integrated snapshot"); + assert_eq!(verification.verified_files, 3); + assert_eq!(verification.verified_bytes, 7); + let merged_paths = merged + .files + .iter() + .map(|file| file.path.as_str()) + .collect::>(); + assert_eq!(merged_paths, ["a.txt", "b.txt", "new.txt"]); + for (path, expected) in [ + ("a.txt", b"a1".as_slice()), + ("b.txt", b"b1"), + ("new.txt", b"new"), + ] { + let file = merged.files.iter().find(|file| file.path == path).unwrap(); + assert_eq!(state.read_blob(&file.sha256).unwrap(), expected); + } + + let overlay = overlay_snapshot(&state, &base, &first, &["a.txt".to_owned()]) + .expect("overlay selected path"); + assert!(overlay.files.iter().any(|file| file.path == "gone.txt")); + assert_eq!(compare_snapshots(&base, &overlay).unwrap().changes.len(), 1); + assert!(overlay_snapshot(&state, &base, &first, &[]).is_err()); + assert!(overlay_snapshot( + &state, + &base, + &first, + &["a.txt".to_owned(), "a.txt".to_owned()] + ) + .is_err()); + + let identical_files = put("a.txt", b"a1", base.files.clone()); + let identical = compose_snapshot(&state, &base, identical_files, "test:identical") + .expect("compose identical edit"); + let identical_merge = merge_snapshots(&state, &base, &[first.clone(), identical]) + .expect("identical edits are compatible"); + assert!(identical_merge + .files + .iter() + .all(|file| file.path != "gone.txt")); + + let edit_deleted_files = put("gone.txt", b"updated", base.files.clone()); + let edit_deleted = compose_snapshot(&state, &base, edit_deleted_files, "test:edit-deleted") + .expect("compose edit of concurrently deleted file"); + assert!(merge_snapshots(&state, &base, &[first.clone(), edit_deleted]).is_err()); + + let conflict_files = put("a.txt", b"other edit", base.files.clone()); + let conflict = compose_snapshot(&state, &base, conflict_files, "test:conflict") + .expect("compose conflicting candidate"); + assert!(merge_snapshots(&state, &base, &[first, conflict]).is_err()); + + let mut other_base = second.clone(); + other_base.commit = "0123456789012345678901234567890123456789".to_owned(); + assert!(merge_snapshots(&state, &base, &[other_base]).is_err()); + + let missing = merged + .files + .iter() + .find(|file| file.path == "new.txt") + .unwrap(); + fs::remove_file(state_path.join("objects").join(&missing.sha256)).unwrap(); + assert!(verify_retained_snapshot(&state, &merged.id).is_err()); + } +} diff --git a/crates/rover-store/Cargo.toml b/crates/rover-store/Cargo.toml new file mode 100644 index 0000000..e7213e1 --- /dev/null +++ b/crates/rover-store/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "rover-store" +version = "0.1.0" +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[dependencies] +rover-core = { path = "../rover-core" } +rusqlite = { version = "=0.40.2", default-features = false, features = ["bundled"] } +serde = { version = "=1.0.229", default-features = false, features = ["std", "derive"] } +serde_json = { version = "=1.0.151", default-features = false, features = ["std", "unbounded_depth"] } +serde_stacker = "=0.1.14" +stacker = "=0.1.25" +sha2 = "=0.11.0" + +[target.'cfg(unix)'.dependencies] +rustix = { version = "=1.1.4", default-features = false, features = ["fs", "std"] } + +[lints] +workspace = true diff --git a/crates/rover-store/src/budget.rs b/crates/rover-store/src/budget.rs new file mode 100644 index 0000000..521df11 --- /dev/null +++ b/crates/rover-store/src/budget.rs @@ -0,0 +1,460 @@ +//! Atomic local budget accounting shared by processes using one Rover store. +//! +//! The ledger is persisted under `settings/budget`. It coordinates one local +//! state store; it does not implement fleet-wide billing or provider usage +//! measurement. + +use std::collections::BTreeMap; +use std::error::Error; +use std::fmt; + +use rover_core::Id; +use rusqlite::{Connection, OptionalExtension, TransactionBehavior}; +use serde::de::Deserializer; +use serde::{Deserialize, Serialize}; + +use crate::{put_in_transaction, Store, StoreError}; + +const SETTINGS_KIND: &str = "settings"; +const BUDGET_ID: &str = "budget"; +const MAX_BUDGET_VALUE: i64 = 1 << 30; + +fn deserialize_integer<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + Ok(Option::::deserialize(deserializer)?.unwrap_or_default()) +} + +fn deserialize_usage<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + Ok( + Option::>>::deserialize(deserializer)? + .unwrap_or_default() + .into_iter() + .map(|(owner, value)| (owner, value.unwrap_or_default())) + .collect(), + ) +} + +/// Per-owner local accounting and an optional aggregate cap. +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +pub struct Budget { + /// Aggregate cap; zero means enforcement is disabled. + #[serde(default, deserialize_with = "deserialize_integer")] + pub cap: i64, + /// Increments after every successful cap, charge, or reset operation. + #[serde(default, deserialize_with = "deserialize_integer")] + pub version: i64, + /// Charged usage by owner. + #[serde(rename = "use", default, deserialize_with = "deserialize_usage")] + pub usage: BTreeMap, +} + +impl Budget { + /// Sum all owner usage without integer overflow. + #[must_use] + pub fn total(&self) -> i128 { + self.usage.values().map(|value| i128::from(*value)).sum() + } +} + +/// A budget ledger or charge failure. +#[derive(Debug)] +pub enum BudgetError { + /// A cap, owner, or charge is outside the accepted range. + InvalidRequest, + /// The stored ledger contains invalid values. + InvalidLedger, + /// A valid charge would exceed a configured nonzero cap. + Exhausted, + /// The backing store failed. + Store(StoreError), +} + +impl fmt::Display for BudgetError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidRequest => formatter.write_str("invalid budget request"), + Self::InvalidLedger => formatter.write_str("invalid budget ledger"), + Self::Exhausted => formatter.write_str("budget exhausted"), + Self::Store(error) => write!(formatter, "budget store failed: {error}"), + } + } +} + +impl Error for BudgetError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::Store(error) => Some(error), + Self::InvalidRequest | Self::InvalidLedger | Self::Exhausted => None, + } + } +} + +impl Store { + /// Read the persisted per-owner budget ledger, defaulting to an uncapped + /// empty ledger when it has not been created. + /// + /// # Errors + /// + /// Returns [`BudgetError::InvalidLedger`] for invalid stored values or + /// [`BudgetError::Store`] for database and JSON errors. + pub fn budget_use(&self) -> Result { + let connection = self.lock().map_err(BudgetError::Store)?; + read_budget(&connection) + } + + /// Set the aggregate cap; zero disables enforcement. Every successful + /// update increments the ledger version. + /// + /// # Errors + /// + /// Returns [`BudgetError::InvalidRequest`] for caps outside 0..=2^30, + /// [`BudgetError::InvalidLedger`] for corrupt stored values, or + /// [`BudgetError::Store`] for database failures. + pub fn set_budget_cap(&self, cap: i64) -> Result<(), BudgetError> { + if !(0..=MAX_BUDGET_VALUE).contains(&cap) { + return Err(BudgetError::InvalidRequest); + } + let mut connection = self.lock().map_err(BudgetError::Store)?; + let transaction = connection + .transaction_with_behavior(TransactionBehavior::Immediate) + .map_err(StoreError::from) + .map_err(BudgetError::Store)?; + let mut budget = read_budget(&transaction)?; + budget.cap = cap; + increment_version(&mut budget)?; + put_in_transaction( + &transaction, + SETTINGS_KIND, + BUDGET_ID, + &budget, + "budget.cap", + ) + .map_err(BudgetError::Store)?; + transaction + .commit() + .map_err(StoreError::from) + .map_err(BudgetError::Store) + } + + /// Add usage for `owner` atomically with aggregate cap enforcement. + /// Rejected charges leave the budget record, version, and event ledger + /// unchanged. A zero cap means uncapped accounting. + /// + /// # Errors + /// + /// Returns [`BudgetError::InvalidRequest`] for invalid owners or charges, + /// [`BudgetError::InvalidLedger`] for corrupt stored values, + /// [`BudgetError::Exhausted`] when the cap would be exceeded, or + /// [`BudgetError::Store`] for database failures. + pub fn charge_budget(&self, owner: &str, amount: i64) -> Result<(), BudgetError> { + if Id::parse(owner).is_err() { + return Err(BudgetError::InvalidRequest); + } + if !(0..=MAX_BUDGET_VALUE).contains(&amount) { + return Err(BudgetError::InvalidRequest); + } + let mut connection = self.lock().map_err(BudgetError::Store)?; + let transaction = connection + .transaction_with_behavior(TransactionBehavior::Immediate) + .map_err(StoreError::from) + .map_err(BudgetError::Store)?; + let mut budget = read_budget(&transaction)?; + let total_after_charge = budget.total() + i128::from(amount); + if budget.cap > 0 && total_after_charge > i128::from(budget.cap) { + return Err(BudgetError::Exhausted); + } + let usage = budget.usage.entry(owner.to_owned()).or_default(); + *usage += amount; + increment_version(&mut budget)?; + put_in_transaction( + &transaction, + SETTINGS_KIND, + BUDGET_ID, + &budget, + "budget.charge", + ) + .map_err(BudgetError::Store)?; + transaction + .commit() + .map_err(StoreError::from) + .map_err(BudgetError::Store) + } + + /// Reset one owner's accounting or all accounting when `owner` is `*`. + /// Every successful reset increments the ledger version. + /// + /// # Errors + /// + /// Returns [`BudgetError::InvalidRequest`] for an invalid owner, + /// [`BudgetError::InvalidLedger`] for corrupt stored values, or + /// [`BudgetError::Store`] for database failures. + pub fn reset_budget(&self, owner: &str) -> Result<(), BudgetError> { + if owner != "*" && Id::parse(owner).is_err() { + return Err(BudgetError::InvalidRequest); + } + let mut connection = self.lock().map_err(BudgetError::Store)?; + let transaction = connection + .transaction_with_behavior(TransactionBehavior::Immediate) + .map_err(StoreError::from) + .map_err(BudgetError::Store)?; + let mut budget = read_budget(&transaction)?; + if owner == "*" { + budget.usage.clear(); + } else { + budget.usage.remove(owner); + } + increment_version(&mut budget)?; + put_in_transaction( + &transaction, + SETTINGS_KIND, + BUDGET_ID, + &budget, + "budget.reset", + ) + .map_err(BudgetError::Store)?; + transaction + .commit() + .map_err(StoreError::from) + .map_err(BudgetError::Store) + } +} + +fn read_budget(connection: &Connection) -> Result { + let encoded = connection + .query_row( + "SELECT payload FROM records WHERE kind=?1 AND id=?2", + (SETTINGS_KIND, BUDGET_ID), + |row| row.get::<_, String>(0), + ) + .optional() + .map_err(StoreError::from) + .map_err(BudgetError::Store)?; + let Some(encoded) = encoded else { + return Ok(Budget::default()); + }; + let budget: Budget = serde_json::from_str(&encoded) + .map_err(StoreError::from) + .map_err(BudgetError::Store)?; + if budget.cap < 0 + || budget.cap > MAX_BUDGET_VALUE + || budget.version < 0 + || budget + .usage + .values() + .any(|value| *value < 0 || *value > MAX_BUDGET_VALUE) + { + return Err(BudgetError::InvalidLedger); + } + Ok(budget) +} + +fn increment_version(budget: &mut Budget) -> Result<(), BudgetError> { + budget.version = budget + .version + .checked_add(1) + .ok_or(BudgetError::InvalidLedger)?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::fs; + use std::path::PathBuf; + use std::sync::atomic::{AtomicU64, Ordering}; + use std::sync::{Arc, Barrier}; + use std::thread; + use std::time::{SystemTime, UNIX_EPOCH}; + + use rusqlite::Connection; + + use super::{Budget, BudgetError}; + use crate::Store; + + static TEST_SEQUENCE: AtomicU64 = AtomicU64::new(0); + + fn store() -> Store { + Store::from_connection(Connection::open_in_memory().expect("in-memory SQLite")) + .expect("migrate in-memory SQLite") + } + + struct TestDatabase(PathBuf); + + impl TestDatabase { + fn new() -> Self { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("clock follows epoch") + .as_nanos(); + let path = std::env::temp_dir().join(format!( + "rover-budget-{}-{timestamp}-{}", + std::process::id(), + TEST_SEQUENCE.fetch_add(1, Ordering::Relaxed) + )); + Self(path) + } + + fn open(&self) -> Store { + Store::from_connection(Connection::open(&self.0).expect("open shared SQLite file")) + .expect("migrate shared SQLite file") + } + } + + impl Drop for TestDatabase { + fn drop(&mut self) { + let _ = fs::remove_file(&self.0); + for suffix in ["-wal", "-shm"] { + let mut sidecar = self.0.as_os_str().to_os_string(); + sidecar.push(suffix); + let _ = fs::remove_file(PathBuf::from(sidecar)); + } + } + } + + #[test] + fn cap_charge_usage_and_reset_match_go_contract() { + let store = store(); + assert_eq!( + store.budget_use().expect("initial ledger"), + Budget::default() + ); + + store + .charge_budget("agent_a", 5) + .expect("uncapped charge succeeds"); + store.set_budget_cap(10).expect("set global cap"); + store + .charge_budget("agent_a", 4) + .expect("charge at cap minus one"); + let before_refusal = store.budget_use().expect("ledger before refusal"); + let events_before_refusal = store.events("budget").expect("budget events"); + assert_eq!(before_refusal.total(), 9); + assert!(matches!( + store.charge_budget("agent_b", 2), + Err(BudgetError::Exhausted) + )); + assert_eq!( + store.budget_use().expect("ledger after refusal"), + before_refusal + ); + assert_eq!( + store.events("budget").expect("budget events after refusal"), + events_before_refusal + ); + + store.reset_budget("agent_a").expect("reset one owner"); + let reset_owner = store.budget_use().expect("ledger after owner reset"); + assert_eq!(reset_owner.total(), 0); + assert_eq!(reset_owner.cap, 10); + store + .charge_budget("agent_b", 1 << 20) + .expect_err("cap still enforced until explicitly disabled"); + store.set_budget_cap(0).expect("disable cap"); + store + .charge_budget("agent_b", 1 << 20) + .expect("uncapped charge accepted"); + store.reset_budget("*").expect("reset every owner"); + let all_reset = store.budget_use().expect("ledger after full reset"); + assert_eq!(all_reset.total(), 0); + assert!(all_reset.usage.is_empty()); + assert!(all_reset.version > reset_owner.version); + } + + #[test] + fn invalid_requests_and_corrupt_ledgers_are_rejected() { + let store = store(); + for owner in ["", "bad/owner", "sp ace", "semi;colon"] { + assert!(matches!( + store.charge_budget(owner, 1), + Err(BudgetError::InvalidRequest) + )); + } + assert!(matches!( + store.charge_budget("agent_a", -1), + Err(BudgetError::InvalidRequest) + )); + assert!(matches!( + store.set_budget_cap(-5), + Err(BudgetError::InvalidRequest) + )); + assert!(matches!( + store.reset_budget("bad/owner"), + Err(BudgetError::InvalidRequest) + )); + + store + .put( + "settings", + "budget", + &serde_json::json!({"cap": 0, "version": 0, "use": {"agent_a": -1}}), + "", + ) + .expect("write invalid ledger fixture"); + assert!(matches!( + store.budget_use(), + Err(BudgetError::InvalidLedger) + )); + + for legacy in [ + serde_json::json!({"cap": 3}), + serde_json::json!({"cap": 3, "use": null}), + serde_json::json!({"cap": null, "version": null, "use": {"agent_a": null}}), + ] { + store + .put("settings", "budget", &legacy, "") + .expect("write legacy compatible ledger"); + let decoded = store.budget_use().expect("decode omitted or null use map"); + if legacy.get("cap").is_some_and(serde_json::Value::is_null) { + assert_eq!(decoded.cap, 0); + assert_eq!(decoded.usage.get("agent_a"), Some(&0)); + } else { + assert_eq!(decoded.cap, 3); + } + assert_eq!(decoded.version, 0); + if !legacy.get("use").is_some_and(serde_json::Value::is_object) { + assert!(decoded.usage.is_empty()); + } + } + } + + #[test] + fn independent_connections_cannot_overcharge_the_shared_cap() { + let database = TestDatabase::new(); + let first = Arc::new(database.open()); + first.set_budget_cap(10).expect("set shared cap"); + let second = Arc::new(database.open()); + let barrier = Arc::new(Barrier::new(3)); + let workers = [ + (Arc::clone(&first), "agent_a"), + (Arc::clone(&second), "agent_b"), + ] + .into_iter() + .map(|(store, owner)| { + let barrier = Arc::clone(&barrier); + thread::spawn(move || { + barrier.wait(); + store.charge_budget(owner, 7) + }) + }) + .collect::>(); + barrier.wait(); + + let results = workers + .into_iter() + .map(|worker| worker.join().expect("budget worker did not panic")) + .collect::>(); + assert_eq!(results.iter().filter(|result| result.is_ok()).count(), 1); + assert_eq!( + results + .iter() + .filter(|result| matches!(result, Err(BudgetError::Exhausted))) + .count(), + 1 + ); + assert_eq!(first.budget_use().expect("read final ledger").total(), 7); + } +} diff --git a/crates/rover-store/src/files.rs b/crates/rover-store/src/files.rs new file mode 100644 index 0000000..a7d947a --- /dev/null +++ b/crates/rover-store/src/files.rs @@ -0,0 +1,1739 @@ +//! Unix directory-handle anchored file operations for Rover state. +//! +//! These APIs operate on one already-admitted directory and one basename per +//! call. They do not create or admit a Rover state root. + +use std::fs::{self, File}; +use std::io::{self, Read, Write}; +use std::os::fd::{AsFd, OwnedFd}; +use std::path::{Component, Path, PathBuf}; +use std::sync::atomic::{AtomicU64, Ordering}; + +use rover_core::Sha256Digest; +use rusqlite::{Connection, OpenFlags}; +use rustix::fs::{ + fchmod, flock, fstat, fsync, linkat, mkdirat, open, openat, renameat, statat, unlinkat, + AtFlags, FileType, FlockOperation, Mode, OFlags, RawMode, +}; + +use crate::{Store, StoreError}; + +const MAX_BLOB_BYTES: u64 = 16 * 1024 * 1024; +static TEMP_SEQUENCE: AtomicU64 = AtomicU64::new(0); + +/// Entry kind returned by a descriptor-validated directory enumeration. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum SafeEntryKind { + /// Regular file that can be opened through the bounded reader. + File, + /// Directory that can be entered without following a link. + Directory, + /// Symlink shown for awareness but never traversed or opened. + Symlink, + /// Device, socket, FIFO, or other unsupported filesystem object. + Other, +} + +/// One UTF-8 name and no-follow type from a safe directory listing. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct SafeDirectoryEntry { + /// One child basename relative to the listed directory. + pub name: String, + /// Type observed without following the entry. + pub kind: SafeEntryKind, +} + +/// A validated open directory. All later operations are relative to its file +/// descriptor, so renaming the path after opening does not redirect access. +pub struct SafeDir { + descriptor: OwnedFd, +} + +impl SafeDir { + /// Open an existing directory without following a symlink at its final + /// path component. Unlike [`SafeDir::open`], this does not impose private + /// permission bits and is intended for user-selected workspace roots. + /// + /// # Errors + /// + /// Returns an I/O error if the path is a symlink or is not a directory. + pub fn open_directory(path: impl AsRef) -> io::Result { + let descriptor = open( + path.as_ref(), + OFlags::RDONLY | OFlags::DIRECTORY | OFlags::NOFOLLOW | OFlags::CLOEXEC, + Mode::empty(), + )?; + if !FileType::from_raw_mode(fstat(&descriptor)?.st_mode).is_dir() { + return Err(invalid_data("path is not a directory")); + } + Ok(Self { descriptor }) + } + + /// List one directory beneath this handle and validate each returned name + /// against the descriptor-opened directory without following symlinks. + /// + /// The empty path lists this directory. `enumeration_path` must name the + /// same directory through its ordinary filesystem path; entries observed + /// there are revalidated against this handle before they are returned. + /// Names are UTF-8-only, `.git` is always omitted, and hidden names are + /// included only when requested. + /// The result is sorted by name. The boolean reports whether more entries + /// existed than the requested limit. + /// + /// # Errors + /// + /// Returns an error for an unsafe path, invalid limit, unreadable + /// directory, or a non-UTF-8 entry name. + pub fn list_directory_path( + &self, + relative_directory: &str, + enumeration_path: &Path, + include_hidden: bool, + limit: usize, + ) -> io::Result<(Vec, bool)> { + if !(1..=10_000).contains(&limit) { + return Err(invalid_input("directory entry limit is outside 1..=10000")); + } + let directory = self.open_relative_directory(relative_directory)?; + let iterator = fs::read_dir(enumeration_path)?; + let mut entries = Vec::new(); + let mut truncated = false; + for entry in iterator { + let entry = entry?; + let name = entry + .file_name() + .into_string() + .map_err(|_| invalid_data("directory entry name is not UTF-8"))?; + if name == "." + || name == ".." + || name.eq_ignore_ascii_case(".git") + || (!include_hidden && name.starts_with('.')) + { + continue; + } + validate_basename(&name)?; + let metadata = match statat(&directory.descriptor, &name, AtFlags::SYMLINK_NOFOLLOW) { + Ok(metadata) => metadata, + Err(error) if error == rustix::io::Errno::NOENT => continue, + Err(error) => return Err(error.into()), + }; + let kind = FileType::from_raw_mode(metadata.st_mode); + let kind = if kind.is_file() { + SafeEntryKind::File + } else if kind.is_dir() { + SafeEntryKind::Directory + } else if kind.is_symlink() { + SafeEntryKind::Symlink + } else { + SafeEntryKind::Other + }; + if entries.len() == limit { + truncated = true; + break; + } + entries.push(SafeDirectoryEntry { name, kind }); + } + entries.sort_by(|left, right| left.name.cmp(&right.name)); + Ok((entries, truncated)) + } + + /// Read a regular file beneath this handle, rejecting symlinks and + /// returning at most `max_bytes` plus a truncation flag. + /// + /// # Errors + /// + /// Returns an error for an unsafe path, invalid bound, missing parent, + /// symlink, non-regular file, or read failure. + pub fn read_regular_file_path( + &self, + relative_file_path: &str, + max_bytes: usize, + ) -> io::Result<(Vec, bool)> { + let max_bytes_u64 = u64::try_from(max_bytes) + .map_err(|_| invalid_input("file read limit exceeds 16 MiB"))?; + if max_bytes_u64 > MAX_BLOB_BYTES { + return Err(invalid_input("file read limit exceeds 16 MiB")); + } + let components = relative_path_components(relative_file_path, false)?; + let (filename, parents) = components + .split_last() + .ok_or_else(|| invalid_input("relative file path is empty"))?; + let parent = self.open_relative_directory(&parents.join("/"))?; + validate_basename(filename)?; + let descriptor = openat( + &parent.descriptor, + *filename, + OFlags::RDONLY | OFlags::NOFOLLOW | OFlags::CLOEXEC | OFlags::NONBLOCK, + Mode::empty(), + )?; + let file = File::from(descriptor); + if !FileType::from_raw_mode(fstat(file.as_fd())?.st_mode).is_file() { + return Err(invalid_data("file viewer only opens regular files")); + } + let mut bytes = Vec::with_capacity(max_bytes.min(64 * 1024)); + file.take(max_bytes_u64.saturating_add(1)) + .read_to_end(&mut bytes)?; + let truncated = bytes.len() > max_bytes; + bytes.truncate(max_bytes); + Ok((bytes, truncated)) + } + + /// Atomically replace a regular file beneath this handle without following + /// any symlink in the path. Existing files keep their permission bits; + /// new files are created with mode `0644`. The replacement is durable to + /// the extent provided by syncing the file and containing directory. + /// + /// # Errors + /// + /// Returns an error for an unsafe path, oversized content, a symlink or + /// non-regular existing entry, or any filesystem operation failure. + pub fn atomic_replace_regular_file_path( + &self, + relative_file_path: &str, + bytes: &[u8], + maximum_bytes: usize, + ) -> io::Result<()> { + if bytes.len() > maximum_bytes + || u64::try_from(maximum_bytes).map_or(true, |limit| limit > MAX_BLOB_BYTES) + { + return Err(invalid_input("replacement file exceeds its byte limit")); + } + let components = relative_path_components(relative_file_path, false)?; + let (filename, parents) = components + .split_last() + .ok_or_else(|| invalid_input("relative file path is empty"))?; + let parent = self.open_relative_directory(&parents.join("/"))?; + validate_basename(filename)?; + let mode = match statat(&parent.descriptor, *filename, AtFlags::SYMLINK_NOFOLLOW) { + Ok(metadata) if FileType::from_raw_mode(metadata.st_mode).is_file() => { + metadata.st_mode & 0o777 + } + Ok(_) => return Err(invalid_data("replacement target is not a regular file")), + Err(error) if error == rustix::io::Errno::NOENT => 0o644, + Err(error) => return Err(error.into()), + }; + let (temp_name, mut file) = parent.create_temp("edit")?; + let _cleanup = TempEntry::new(&parent, temp_name.clone()); + let mode = RawMode::try_from(mode) + .map_err(|_| invalid_input("file mode does not fit platform mode type"))?; + fchmod(file.as_fd(), Mode::from_raw_mode(mode))?; + file.write_all(bytes)?; + file.sync_all()?; + drop(file); + renameat( + &parent.descriptor, + &temp_name, + &parent.descriptor, + *filename, + )?; + fsync(&parent.descriptor)?; + Ok(()) + } + + fn open_relative_directory(&self, relative_directory: &str) -> io::Result { + let components = relative_path_components(relative_directory, true)?; + let descriptor = self.descriptor.as_fd().try_clone_to_owned()?; + let mut directory = SafeDir { descriptor }; + for component in components { + directory = directory.open_subdirectory(component)?; + } + Ok(directory) + } + + /// Open an existing directory without following a symlink in its final + /// path component. The directory must be mode `0700`: owner access is + /// required and group/other access is rejected. + /// + /// # Errors + /// + /// Returns an I/O error if the path is a symlink, is not a directory, or + /// does not have the required private permissions. + pub fn open(path: impl AsRef) -> io::Result { + let descriptor = open( + path.as_ref(), + OFlags::RDONLY | OFlags::DIRECTORY | OFlags::NOFOLLOW | OFlags::CLOEXEC, + Mode::empty(), + )?; + let metadata = fstat(&descriptor)?; + if !FileType::from_raw_mode(metadata.st_mode).is_dir() { + return Err(invalid_data("state path is not a directory")); + } + if metadata.st_mode & 0o777 != 0o700 { + return Err(invalid_data("state directory permissions must be 0700")); + } + Ok(Self { descriptor }) + } + + /// Open a child directory relative to this descriptor, creating it with + /// mode `0700` if it does not exist. Existing symlinks and non-directories + /// are refused; existing directory permissions are preserved. + /// + /// # Errors + /// + /// Returns an I/O error if the name is not one safe path component, the + /// child is not a directory, or directory creation/open fails. + pub fn ensure_subdirectory(&self, name: &str) -> io::Result { + validate_basename(name)?; + let descriptor = match openat( + &self.descriptor, + name, + OFlags::RDONLY | OFlags::DIRECTORY | OFlags::NOFOLLOW | OFlags::CLOEXEC, + Mode::empty(), + ) { + Ok(descriptor) => descriptor, + Err(error) if error == rustix::io::Errno::NOENT => { + match mkdirat(&self.descriptor, name, Mode::from(0o700)) { + Ok(()) => fsync(&self.descriptor)?, + Err(error) if error == rustix::io::Errno::EXIST => {} + Err(error) => return Err(error.into()), + } + openat( + &self.descriptor, + name, + OFlags::RDONLY | OFlags::DIRECTORY | OFlags::NOFOLLOW | OFlags::CLOEXEC, + Mode::empty(), + )? + } + Err(error) => return Err(error.into()), + }; + if !FileType::from_raw_mode(fstat(&descriptor)?.st_mode).is_dir() { + return Err(invalid_data("child path is not a directory")); + } + Ok(Self { descriptor }) + } + + /// Remove one regular file beneath this directory by a slash-separated + /// relative path. Every parent is opened without following symlinks and + /// the leaf is unlinked relative to its parent descriptor. + /// + /// # Errors + /// + /// Returns an I/O error for unsafe paths, missing/non-directory parents, + /// non-regular leaf entries, or failed unlink/sync operations. + pub fn remove_regular_file_path(&self, relative_file_path: &str) -> io::Result<()> { + if relative_file_path.is_empty() || relative_file_path.contains('\\') { + return Err(invalid_input("relative file path is invalid")); + } + let components = relative_file_path.split('/').collect::>(); + if components + .iter() + .any(|component| component.is_empty() || *component == "." || *component == "..") + { + return Err(invalid_input( + "relative file path contains an unsafe component", + )); + } + let descriptor = self.descriptor.as_fd().try_clone_to_owned()?; + let mut parent = SafeDir { descriptor }; + for component in &components[..components.len() - 1] { + parent = parent.open_subdirectory(component)?; + } + let name = components[components.len() - 1]; + validate_basename(name)?; + let metadata = statat(&parent.descriptor, name, AtFlags::SYMLINK_NOFOLLOW)?; + if !FileType::from_raw_mode(metadata.st_mode).is_file() { + return Err(invalid_data("removal target is not a regular file")); + } + unlinkat(&parent.descriptor, name, AtFlags::empty())?; + fsync(&parent.descriptor)?; + Ok(()) + } + + fn lock_exclusive_file(&self, name: &str) -> io::Result { + validate_basename(name)?; + let descriptor = openat( + &self.descriptor, + name, + OFlags::RDWR | OFlags::CREATE | OFlags::NOFOLLOW | OFlags::CLOEXEC, + Mode::from(0o600), + )?; + let file = File::from(descriptor); + let metadata = fstat(file.as_fd())?; + if !FileType::from_raw_mode(metadata.st_mode).is_file() { + return Err(invalid_data( + "worktree admission lock is not a regular file", + )); + } + fchmod(file.as_fd(), Mode::from(0o600))?; + flock(file.as_fd(), FlockOperation::LockExclusive)?; + Ok(file) + } + + fn open_subdirectory(&self, name: &str) -> io::Result { + validate_basename(name)?; + let descriptor = openat( + &self.descriptor, + name, + OFlags::RDONLY | OFlags::DIRECTORY | OFlags::NOFOLLOW | OFlags::CLOEXEC, + Mode::empty(), + )?; + if !FileType::from_raw_mode(fstat(&descriptor)?.st_mode).is_dir() { + return Err(invalid_data("child path is not a directory")); + } + Ok(Self { descriptor }) + } + + /// Walk or create parent directories for a slash-separated relative file + /// path and return the final parent directory descriptor. The last path + /// component is intentionally left for a subsequent file operation. + /// + /// # Errors + /// + /// Returns an I/O error if the path is absolute, contains an unsafe + /// component, or any parent cannot be opened as a directory. + pub fn ensure_subdirectory_path(&self, relative_file_path: &str) -> io::Result { + if relative_file_path.is_empty() || relative_file_path.contains('\\') { + return Err(invalid_input("relative file path is invalid")); + } + let components = relative_file_path.split('/').collect::>(); + if components + .iter() + .any(|component| component.is_empty() || *component == "." || *component == "..") + { + return Err(invalid_input( + "relative file path contains an unsafe component", + )); + } + let descriptor = self.descriptor.as_fd().try_clone_to_owned()?; + let mut directory = SafeDir { descriptor }; + for component in &components[..components.len() - 1] { + directory = directory.ensure_subdirectory(component)?; + } + Ok(directory) + } + + /// Set permission bits on this already-open directory descriptor and sync + /// the metadata update. + /// + /// # Errors + /// + /// Returns an I/O error if the mode is unsupported or the update/sync fails. + pub fn set_permissions(&self, mode: u32) -> io::Result<()> { + if mode & !0o777 != 0 { + return Err(invalid_input("directory mode contains unsupported bits")); + } + let mode = RawMode::try_from(mode) + .map_err(|_| invalid_input("directory mode does not fit platform mode type"))?; + fchmod(&self.descriptor, Mode::from_raw_mode(mode))?; + fsync(&self.descriptor)?; + Ok(()) + } + + /// Create a new regular file relative to this directory descriptor. The + /// operation is exclusive, never follows or replaces an existing entry, + /// applies the exact requested mode, syncs file bytes, then syncs the + /// directory entry. + /// + /// # Errors + /// + /// Returns an I/O error for an invalid basename or mode, an existing + /// destination entry, or a failed write/sync. + pub fn create_new_file(&self, name: &str, bytes: &[u8], mode: u32) -> io::Result<()> { + validate_basename(name)?; + if mode & !0o777 != 0 { + return Err(invalid_input( + "file mode contains unsupported permission bits", + )); + } + let raw_mode = RawMode::try_from(mode) + .map_err(|_| invalid_input("file mode does not fit platform mode type"))?; + let mut file = File::from(openat( + &self.descriptor, + name, + OFlags::WRONLY | OFlags::CREATE | OFlags::EXCL | OFlags::NOFOLLOW | OFlags::CLOEXEC, + Mode::from_raw_mode(raw_mode), + )?); + let result = (|| { + fchmod(file.as_fd(), Mode::from_raw_mode(raw_mode))?; + file.write_all(bytes)?; + file.sync_all()?; + fsync(&self.descriptor)?; + Ok(()) + })(); + drop(file); + if result.is_err() { + let _ = unlinkat(&self.descriptor, name, AtFlags::empty()); + } + result + } + + /// Create a new mode-controlled regular file relative to this directory + /// descriptor and return its open handle for bounded streaming writes. + /// The directory entry is synced before the handle is returned; callers + /// must sync the file after completing writes. + /// + /// # Errors + /// + /// Returns an I/O error for an unsafe name or mode, an existing entry, or + /// failure to create, set permissions, or sync the directory entry. + pub fn create_new_file_writer(&self, name: &str, mode: u32) -> io::Result { + validate_basename(name)?; + if mode & !0o777 != 0 { + return Err(invalid_input( + "file mode contains unsupported permission bits", + )); + } + let raw_mode = RawMode::try_from(mode) + .map_err(|_| invalid_input("file mode does not fit platform mode type"))?; + let file = File::from(openat( + &self.descriptor, + name, + OFlags::WRONLY | OFlags::CREATE | OFlags::EXCL | OFlags::NOFOLLOW | OFlags::CLOEXEC, + Mode::from_raw_mode(raw_mode), + )?); + let result = (|| { + fchmod(file.as_fd(), Mode::from_raw_mode(raw_mode))?; + fsync(&self.descriptor)?; + Ok(()) + })(); + if let Err(error) = result { + drop(file); + let _ = unlinkat(&self.descriptor, name, AtFlags::empty()); + return Err(error); + } + Ok(file) + } + + /// Store bytes under their lowercase SHA-256 digest, without replacing an + /// existing object. A pre-existing entry is accepted only if it validates. + /// + /// Writes have no additional size cap, matching Rover's current Go store. + /// + /// # Errors + /// + /// Returns an I/O error if writing, syncing, publication, or verification + /// fails. + pub fn put_blob(&self, bytes: &[u8]) -> io::Result { + let digest = Sha256Digest::of(bytes).to_hex(); + let (temp_name, mut temp_file) = self.create_temp("object")?; + let _cleanup = TempEntry::new(self, temp_name.clone()); + fchmod(temp_file.as_fd(), Mode::from(0o600))?; + temp_file.write_all(bytes)?; + temp_file.sync_all()?; + drop(temp_file); + + match linkat( + &self.descriptor, + &temp_name, + &self.descriptor, + &digest, + AtFlags::empty(), + ) { + Ok(()) => {} + Err(error) if error == rustix::io::Errno::EXIST => { + let existing = self.read_blob(&digest)?; + if existing != bytes { + return Err(invalid_data( + "existing object bytes differ from digest input", + )); + } + } + Err(error) => return Err(error.into()), + } + fsync(&self.descriptor)?; + Sha256Digest::parse_hex(&digest) + .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error.to_string())) + } + + /// Read and verify a content-addressed object. + /// + /// # Errors + /// + /// Returns an error for malformed digests, symlinks, non-regular files, + /// objects over 16 MiB, and digest mismatches. + pub fn read_blob(&self, digest: &str) -> io::Result> { + if digest.len() != 64 + || !digest + .bytes() + .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte)) + { + return Err(invalid_input("invalid object digest")); + } + let file = File::from(openat( + &self.descriptor, + digest, + OFlags::RDONLY | OFlags::NOFOLLOW | OFlags::NONBLOCK | OFlags::CLOEXEC, + Mode::empty(), + )?); + let metadata = fstat(file.as_fd())?; + if !FileType::from_raw_mode(metadata.st_mode).is_file() { + return Err(invalid_data("object is not a regular file")); + } + if metadata.st_size < 0 + || u64::try_from(metadata.st_size).map_or(true, |size| size > MAX_BLOB_BYTES) + { + return Err(invalid_data("object too large")); + } + let capacity = usize::try_from(metadata.st_size) + .map_err(|_| invalid_data("object size does not fit memory address space"))?; + let mut bytes = Vec::with_capacity(capacity); + file.take(MAX_BLOB_BYTES + 1).read_to_end(&mut bytes)?; + if bytes.len() as u64 > MAX_BLOB_BYTES { + return Err(invalid_data("object too large")); + } + let actual = Sha256Digest::of(&bytes).to_hex(); + if actual != digest { + return Err(invalid_data("object digest mismatch")); + } + Ok(bytes) + } + + /// Write a bounded file without following a final-component symlink. + /// Files are opened exclusively as temporary entries, assigned the exact + /// requested permission bits, synced, atomically renamed, then directory + /// synced. `name` must be a single normal path component. + /// + /// # Errors + /// + /// Returns an I/O error if the name or mode is invalid, or any filesystem + /// operation fails. + pub fn atomic_write(&self, name: &str, bytes: &[u8], mode: u32) -> io::Result<()> { + validate_basename(name)?; + if mode & !0o777 != 0 { + return Err(invalid_input( + "file mode contains unsupported permission bits", + )); + } + let (temp_name, mut file) = self.create_temp("tmp")?; + let _cleanup = TempEntry::new(self, temp_name.clone()); + let mode = RawMode::try_from(mode) + .map_err(|_| invalid_input("file mode does not fit platform mode type"))?; + fchmod(file.as_fd(), Mode::from_raw_mode(mode))?; + file.write_all(bytes)?; + file.sync_all()?; + drop(file); + renameat(&self.descriptor, &temp_name, &self.descriptor, name)?; + fsync(&self.descriptor)?; + Ok(()) + } + + /// Read at most `maximum` bytes from a no-follow regular file. + /// + /// Like Go's `BoundedFile`, this returns a truncated buffer when a regular + /// file exceeds the requested bound. + /// + /// # Errors + /// + /// Returns an I/O error for an invalid name, symlink, or non-regular file. + /// A zero or negative bound returns an empty buffer after file validation, + /// matching Go's `io.LimitReader` behavior. + pub fn bounded_read(&self, name: &str, maximum: i64) -> io::Result> { + validate_basename(name)?; + let file = File::from(openat( + &self.descriptor, + name, + OFlags::RDONLY | OFlags::NOFOLLOW | OFlags::NONBLOCK | OFlags::CLOEXEC, + Mode::empty(), + )?); + let metadata = fstat(file.as_fd())?; + if !FileType::from_raw_mode(metadata.st_mode).is_file() { + return Err(invalid_data("bounded input is not a regular file")); + } + let mut bytes = Vec::new(); + let limit = if maximum <= 0 { + 0 + } else { + u64::try_from(maximum).map_err(|_| invalid_input("file bound overflow"))? + }; + file.take(limit).read_to_end(&mut bytes)?; + Ok(bytes) + } + + fn create_temp(&self, prefix: &str) -> io::Result<(String, File)> { + for _ in 0..128 { + let sequence = TEMP_SEQUENCE.fetch_add(1, Ordering::Relaxed); + let name = format!(".{prefix}-{}-{sequence}", std::process::id()); + match openat( + &self.descriptor, + &name, + OFlags::WRONLY | OFlags::CREATE | OFlags::EXCL | OFlags::NOFOLLOW | OFlags::CLOEXEC, + Mode::from(0o600), + ) { + Ok(file) => { + return Ok((name, File::from(file))); + } + Err(error) if error == rustix::io::Errno::EXIST => {} + Err(error) => return Err(error.into()), + } + } + Err(io::Error::new( + io::ErrorKind::AlreadyExists, + "could not allocate unique temporary entry", + )) + } +} + +struct TempEntry<'a> { + directory: &'a SafeDir, + name: String, +} + +impl<'a> TempEntry<'a> { + fn new(directory: &'a SafeDir, name: String) -> Self { + Self { directory, name } + } +} + +impl Drop for TempEntry<'_> { + fn drop(&mut self) { + let _ = unlinkat(&self.directory.descriptor, &self.name, AtFlags::empty()); + } +} + +/// An admitted Rover state root with its `SQLite` records and private file areas. +/// +/// Opening a state root initializes an empty directory or opens a validated +/// Rover database in place. Existing state is never deleted or migrated by +/// interpreting unknown data. +pub struct StateRoot { + path: PathBuf, + root: SafeDir, + records: Store, + objects: SafeDir, + tasks: SafeDir, + checks: SafeDir, +} + +impl StateRoot { + /// Admit or create a dedicated Unix Rover state directory and open its + /// `SQLite` record store and private subdirectories. + /// + /// The root must not be a filesystem root, the current directory, or the + /// home directory. A nonempty directory must contain a regular `rover.db`; + /// its schema is then validated by the existing migration ledger. + /// + /// # Errors + /// + /// Returns an I/O error for unsafe paths, unrelated nonempty directories, + /// unsafe file types/permissions, or SQLite/migration failures. + pub fn open(path: impl AsRef) -> io::Result { + let path = canonical_state_path(path.as_ref())?; + if path.parent().is_none() { + return Err(invalid_input("state must be a dedicated directory")); + } + for base in [ + std::env::current_dir().ok(), + std::env::var_os("HOME").map(PathBuf::from), + ] + .into_iter() + .flatten() + { + if let Ok(base) = fs::canonicalize(base) { + if base == path { + return Err(invalid_input( + "state must not be the home or current working directory", + )); + } + } + } + if std::env::temp_dir() + .canonicalize() + .is_ok_and(|temporary_root| temporary_root == path) + { + return Err(invalid_input( + "state must be a dedicated directory below the temporary root", + )); + } + + match fs::read_dir(&path) { + Ok(mut entries) => { + if entries.next().transpose()?.is_some() { + let database = fs::symlink_metadata(path.join("rover.db")); + if !database.is_ok_and(|metadata| metadata.file_type().is_file()) { + return Err(invalid_data( + "nonempty state destination is not an existing Rover store", + )); + } + } + } + Err(error) if error.kind() == io::ErrorKind::NotFound => {} + Err(error) => return Err(error), + } + + fs::create_dir_all(&path)?; + let root = open_private_directory(&path)?; + let objects = open_private_directory(&path.join("objects"))?; + let tasks = open_private_directory(&path.join("tasks"))?; + let checks = open_private_directory(&path.join("checks"))?; + prepare_database(&root)?; + + let connection = Connection::open_with_flags( + path.join("rover.db"), + OpenFlags::SQLITE_OPEN_READ_WRITE + | OpenFlags::SQLITE_OPEN_CREATE + | OpenFlags::SQLITE_OPEN_NOFOLLOW + | OpenFlags::SQLITE_OPEN_FULL_MUTEX, + ) + .map_err(sqlite_error)?; + let records = Store::from_connection(connection).map_err(store_error)?; + + Ok(Self { + path, + root, + records, + objects, + tasks, + checks, + }) + } + + /// Return the canonical path of the admitted state root. + #[must_use] + pub fn path(&self) -> &Path { + &self.path + } + + /// Prepare and return the dedicated workspace path for a Rover task. + /// The private task directory is created through the admitted `tasks` + /// directory handle; the workspace leaf is intentionally left absent for + /// `git worktree add` to create. + /// + /// # Errors + /// + /// Returns an I/O error if `owner` is not a valid Rover ID or the task + /// directory cannot be opened/created safely. + pub fn task_workspace_path(&self, owner: &str) -> io::Result { + rover_core::Id::parse(owner.to_owned()) + .map_err(|_| invalid_input("task owner is not a valid Rover ID"))?; + self.tasks.ensure_subdirectory(owner)?; + Ok(self.path.join("tasks").join(owner).join("workspace")) + } + + /// Acquire a per-destination OS lock for Git worktree admission. The lock + /// file lives in Rover's private task area and persists so concurrent + /// processes always lock the same inode; closing the returned handle + /// releases the lock, including during process exit. + /// + /// # Errors + /// + /// Returns an I/O error if `workspace` is not absolute or the private lock + /// file cannot be opened and locked safely. + pub fn lock_worktree_admission(&self, workspace: &Path) -> io::Result { + if !workspace.is_absolute() { + return Err(invalid_input("worktree path must be absolute")); + } + let key = format!( + "worktree-{}", + Sha256Digest::of(workspace.as_os_str().as_encoded_bytes()) + ); + self.tasks.lock_exclusive_file(&key) + } + + /// Borrow the transactional record and event store. + #[must_use] + pub const fn records(&self) -> &Store { + &self.records + } + + /// Store an artifact in the content-addressed objects directory. + /// + /// # Errors + /// + /// Returns an I/O error if writing, syncing, publication, or verification + /// fails. + pub fn put_blob(&self, bytes: &[u8]) -> io::Result { + self.objects.put_blob(bytes) + } + + /// Read and verify a content-addressed artifact. + /// + /// # Errors + /// + /// Returns an I/O error if the digest is invalid or the stored object + /// fails type, size, or hash validation. + pub fn read_blob(&self, digest: &str) -> io::Result> { + self.objects.read_blob(digest) + } + + /// Borrow the private task-output directory handle. + #[must_use] + pub const fn tasks(&self) -> &SafeDir { + &self.tasks + } + + /// Borrow the private check-output directory handle. + #[must_use] + pub const fn checks(&self) -> &SafeDir { + &self.checks + } + + /// Keep the state-root directory descriptor alive for the lifetime of this + /// object, anchoring the stored database path's containing directory. + #[must_use] + pub const fn root_handle(&self) -> &SafeDir { + &self.root + } +} + +/// Create or admit a private directory and return a descriptor anchored to it. +/// Existing ancestors are opened without following symlinks; only the final +/// directory is changed to mode `0700`. Filesystem roots, the shared temporary +/// root, the current directory, and the user's home directory are refused. +/// +/// # Errors +/// +/// Returns an I/O error if the path or one of its ancestors is unsafe, a +/// component is not a directory, or permissions cannot be applied. +pub fn prepare_private_directory(path: impl AsRef) -> io::Result { + let canonical = canonical_state_path(path.as_ref())?; + if canonical.parent().is_none() { + return Err(invalid_input( + "private directory must not be a filesystem root", + )); + } + let mut protected = vec![std::env::temp_dir()]; + if let Ok(current) = std::env::current_dir() { + protected.push(current); + } + if let Some(home) = std::env::var_os("HOME") { + protected.push(PathBuf::from(home)); + } + for protected_path in protected { + if canonical_state_path(&protected_path).is_ok_and(|path| path == canonical) { + return Err(invalid_input( + "private directory must not be the temporary root, current directory, or home", + )); + } + } + let mut directory = SafeDir::open_directory(Path::new("/"))?; + for component in canonical.components() { + match component { + Component::RootDir | Component::CurDir => {} + Component::Normal(name) => { + let name = name + .to_str() + .ok_or_else(|| invalid_input("directory path component is not UTF-8"))?; + directory = directory.ensure_subdirectory(name)?; + } + Component::ParentDir | Component::Prefix(_) => { + return Err(invalid_input("directory path is not canonical")); + } + } + } + directory.set_permissions(0o700)?; + Ok(directory) +} + +pub(crate) fn canonical_state_path(path: &Path) -> io::Result { + let absolute = if path.is_absolute() { + normalize_path(path) + } else { + normalize_path(&std::env::current_dir()?.join(path)) + }; + let mut existing = absolute.as_path(); + loop { + match fs::symlink_metadata(existing) { + Ok(metadata) => { + if !metadata.file_type().is_symlink() && !metadata.is_dir() { + return Err(invalid_data("state path ancestor is not a directory")); + } + break; + } + Err(error) if error.kind() == io::ErrorKind::NotFound => { + existing = existing + .parent() + .ok_or_else(|| invalid_input("state path has no existing ancestor"))?; + } + Err(error) => return Err(error), + } + } + + let mut ancestor = Some(existing); + while let Some(candidate) = ancestor { + let metadata = fs::symlink_metadata(candidate)?; + if metadata.file_type().is_symlink() { + #[cfg(target_os = "macos")] + if candidate == Path::new("/tmp") || candidate == Path::new("/var") { + break; + } + return Err(invalid_data("state path has a symlinked ancestor")); + } + ancestor = candidate.parent(); + } + + let canonical_existing = fs::canonicalize(existing)?; + let remaining = absolute + .strip_prefix(existing) + .map_err(|_| invalid_data("state path ancestor relationship changed"))?; + Ok(normalize_path(&canonical_existing.join(remaining))) +} + +fn open_private_directory(path: &Path) -> io::Result { + if !path.exists() { + match fs::create_dir(path) { + Ok(()) => {} + Err(error) if error.kind() == io::ErrorKind::AlreadyExists => {} + Err(error) => return Err(error), + } + } + let descriptor = open( + path, + OFlags::RDONLY | OFlags::DIRECTORY | OFlags::NOFOLLOW | OFlags::CLOEXEC, + Mode::empty(), + )?; + let metadata = fstat(&descriptor)?; + if !FileType::from_raw_mode(metadata.st_mode).is_dir() { + return Err(invalid_data("state directory component is not a directory")); + } + fchmod(&descriptor, Mode::from(0o700))?; + fsync(&descriptor)?; + Ok(SafeDir { descriptor }) +} + +fn prepare_database(root: &SafeDir) -> io::Result<()> { + match statat(&root.descriptor, "rover.db", AtFlags::SYMLINK_NOFOLLOW) { + Ok(metadata) => { + if !FileType::from_raw_mode(metadata.st_mode).is_file() { + return Err(invalid_data("database must be a regular file")); + } + } + Err(error) if error == rustix::io::Errno::NOENT => { + match openat( + &root.descriptor, + "rover.db", + OFlags::WRONLY | OFlags::CREATE | OFlags::EXCL | OFlags::NOFOLLOW | OFlags::CLOEXEC, + Mode::from(0o600), + ) { + Ok(file) => { + File::from(file).sync_all()?; + fsync(&root.descriptor)?; + } + Err(error) if error == rustix::io::Errno::EXIST => {} + Err(error) => return Err(error.into()), + } + } + Err(error) => return Err(error.into()), + } + let database = File::from(openat( + &root.descriptor, + "rover.db", + OFlags::RDWR | OFlags::NOFOLLOW | OFlags::NONBLOCK | OFlags::CLOEXEC, + Mode::empty(), + )?); + let metadata = fstat(database.as_fd())?; + if !FileType::from_raw_mode(metadata.st_mode).is_file() { + return Err(invalid_data("database must be a regular file")); + } + fchmod(database.as_fd(), Mode::from(0o600))?; + database.sync_all() +} + +fn sqlite_error(error: rusqlite::Error) -> io::Error { + io::Error::other(error) +} + +fn store_error(error: StoreError) -> io::Error { + io::Error::other(error) +} + +/// Test whether `path` is lexically contained in `root`, using path components +/// rather than string prefixes. This does not resolve symlinks or touch disk. +#[must_use] +pub fn is_within(root: &Path, path: &Path) -> bool { + let root = normalize_path(root); + let path = normalize_path(path); + path.starts_with(root) +} + +fn normalize_path(path: &Path) -> PathBuf { + let mut normalized = PathBuf::new(); + for component in path.components() { + match component { + Component::Prefix(prefix) => normalized.push(prefix.as_os_str()), + Component::RootDir => normalized.push(component.as_os_str()), + Component::CurDir => {} + Component::ParentDir => { + if !normalized.pop() { + normalized.push(component.as_os_str()); + } + } + Component::Normal(part) => normalized.push(part), + } + } + normalized +} + +fn validate_basename(name: &str) -> io::Result<()> { + if name.is_empty() || name.contains('/') || name.contains('\\') || name.contains('\0') { + return Err(invalid_input("file name contains a forbidden character")); + } + let mut components = Path::new(name).components(); + if !matches!(components.next(), Some(Component::Normal(_))) || components.next().is_some() { + return Err(invalid_input("file name must be one normal path component")); + } + Ok(()) +} + +fn relative_path_components(path: &str, allow_empty: bool) -> io::Result> { + if path.is_empty() && allow_empty { + return Ok(Vec::new()); + } + if path.is_empty() || path.starts_with('/') || path.contains('\\') || path.contains('\0') { + return Err(invalid_input("relative path is invalid")); + } + let components = path.split('/').collect::>(); + if components.iter().any(|component| { + component.is_empty() + || matches!(*component, "." | "..") + || component.eq_ignore_ascii_case(".git") + }) { + return Err(invalid_input("relative path contains an unsafe component")); + } + for component in &components { + validate_basename(component)?; + } + Ok(components) +} + +fn invalid_data(message: &'static str) -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, message) +} + +fn invalid_input(message: &'static str) -> io::Error { + io::Error::new(io::ErrorKind::InvalidInput, message) +} + +#[cfg(test)] +mod tests { + use std::fs; + use std::io; + use std::os::unix::fs::{symlink, PermissionsExt}; + use std::path::{Path, PathBuf}; + use std::sync::atomic::{AtomicU64, Ordering}; + use std::sync::mpsc; + use std::sync::Arc; + use std::thread; + use std::time::{SystemTime, UNIX_EPOCH}; + + use super::{is_within, prepare_private_directory, SafeDir, StateRoot}; + + static TEST_SEQUENCE: AtomicU64 = AtomicU64::new(0); + + struct TestDirectory(PathBuf); + + impl TestDirectory { + fn new() -> Self { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("clock follows epoch") + .as_nanos(); + let path = std::env::temp_dir().join(format!( + "rover-files-{}-{timestamp}-{}", + std::process::id(), + TEST_SEQUENCE.fetch_add(1, Ordering::Relaxed) + )); + fs::create_dir(&path).expect("create test directory"); + fs::set_permissions(&path, fs::Permissions::from_mode(0o700)) + .expect("set private test directory"); + Self(path) + } + + fn open(&self) -> SafeDir { + SafeDir::open(&self.0).expect("open safe directory") + } + } + + impl Drop for TestDirectory { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.0); + } + } + + #[test] + fn prepare_private_directory_creates_and_hardens_only_the_leaf() { + let directory = TestDirectory::new(); + let parent = directory.0.join("ordinary-parent"); + fs::create_dir(&parent).expect("create parent"); + fs::set_permissions(&parent, fs::Permissions::from_mode(0o755)) + .expect("make parent nonprivate"); + let leaf = parent.join("output"); + let anchored = prepare_private_directory(&leaf).expect("prepare output directory"); + assert_eq!( + fs::metadata(&leaf) + .expect("inspect leaf") + .permissions() + .mode() + & 0o777, + 0o700 + ); + assert_eq!( + fs::metadata(&parent) + .expect("inspect parent") + .permissions() + .mode() + & 0o777, + 0o755 + ); + assert!(anchored.create_new_file("log", b"private", 0o600).is_ok()); + assert!(prepare_private_directory("/").is_err()); + assert!(prepare_private_directory(std::env::current_dir().unwrap()).is_err()); + assert!(prepare_private_directory(std::env::temp_dir()).is_err()); + } + + #[test] + fn blobs_round_trip_and_validate_digest_names() { + let directory = TestDirectory::new(); + let files = directory.open(); + let digest = files + .put_blob(b"content addressed") + .expect("put blob") + .to_hex(); + assert_eq!(digest.len(), 64); + assert_eq!( + fs::metadata(directory.0.join(&digest)) + .expect("blob metadata") + .permissions() + .mode() + & 0o777, + 0o600 + ); + assert_eq!( + files.read_blob(&digest).expect("read blob"), + b"content addressed" + ); + assert_eq!( + files + .read_blob("../../outside") + .expect_err("reject traversal") + .kind(), + io::ErrorKind::InvalidInput + ); + assert_eq!( + files + .read_blob(&"A".repeat(64)) + .expect_err("reject uppercase digest") + .kind(), + io::ErrorKind::InvalidInput + ); + } + + #[test] + fn duplicate_concurrent_blob_publication_is_idempotent() { + let directory = TestDirectory::new(); + let files = directory.open(); + let payload = b"shared object".to_vec(); + let handles: Vec<_> = (0..8) + .map(|_| { + let path = directory.0.clone(); + let payload = payload.clone(); + std::thread::spawn(move || { + SafeDir::open(path) + .expect("open concurrent directory") + .put_blob(&payload) + .expect("concurrent blob write") + }) + }) + .collect(); + let digests: Vec<_> = handles + .into_iter() + .map(|handle| handle.join().expect("worker completes")) + .collect(); + assert!(digests.iter().all(|digest| digest == &digests[0])); + assert_eq!( + fs::read_dir(&directory.0) + .expect("list blob directory") + .count(), + 1, + "temporary entries are removed" + ); + assert_eq!( + files + .read_blob(&digests[0].to_hex()) + .expect("read published blob"), + payload + ); + } + + #[test] + fn blob_symlinks_tampering_and_oversize_files_are_rejected() { + let directory = TestDirectory::new(); + let files = directory.open(); + let digest = files.put_blob(b"original").expect("put blob").to_hex(); + fs::write(directory.0.join(&digest), b"modified").expect("tamper blob"); + assert!(files.put_blob(b"original").is_err()); + assert_eq!( + files + .read_blob(&digest) + .expect_err("reject tampering") + .kind(), + io::ErrorKind::InvalidData + ); + + fs::remove_file(directory.0.join(&digest)).expect("remove tampered file"); + let outside = directory.0.with_extension("outside"); + fs::write(&outside, b"outside").expect("write outside target"); + symlink(&outside, directory.0.join(&digest)).expect("plant blob symlink"); + assert!(files.read_blob(&digest).is_err()); + + fs::remove_file(directory.0.join(&digest)).expect("remove blob symlink"); + fs::create_dir(directory.0.join(&digest)).expect("create object directory"); + assert_eq!( + files + .read_blob(&digest) + .expect_err("reject object directory") + .kind(), + io::ErrorKind::InvalidData + ); + fs::remove_dir(directory.0.join(&digest)).expect("remove object directory"); + fs::write(directory.0.join(&digest), vec![0; 16 * 1024 * 1024 + 1]) + .expect("write oversized object"); + assert_eq!( + files + .read_blob(&digest) + .expect_err("reject oversized object") + .kind(), + io::ErrorKind::InvalidData + ); + let _ = fs::remove_file(outside); + } + + #[test] + fn atomic_write_replaces_symlink_entry_with_requested_mode() { + let directory = TestDirectory::new(); + let files = directory.open(); + let outside = directory.0.with_extension("target"); + fs::write(&outside, b"leave alone").expect("write outside target"); + symlink(&outside, directory.0.join("output")).expect("plant target symlink"); + + files + .atomic_write("output", b"new content", 0o640) + .expect("atomically replace target entry"); + assert_eq!( + fs::read(&outside).expect("read external target"), + b"leave alone" + ); + assert_eq!( + fs::read(directory.0.join("output")).expect("read output"), + b"new content" + ); + assert_eq!( + fs::metadata(directory.0.join("output")) + .expect("output metadata") + .permissions() + .mode() + & 0o777, + 0o640 + ); + let _ = fs::remove_file(outside); + } + + #[test] + fn blob_read_accepts_the_exact_sixteen_mibibyte_limit() { + let directory = TestDirectory::new(); + let files = directory.open(); + let payload = vec![0x5a; 16 * 1024 * 1024]; + let digest = files + .put_blob(&payload) + .expect("put blob at size limit") + .to_hex(); + assert_eq!(files.read_blob(&digest).expect("read size limit"), payload); + } + + #[test] + fn safe_directory_rejects_links_and_public_permissions() { + let directory = TestDirectory::new(); + let link = directory.0.with_extension("link"); + symlink(&directory.0, &link).expect("create directory symlink"); + assert!(SafeDir::open(&link).is_err()); + + fs::set_permissions(&directory.0, fs::Permissions::from_mode(0o755)) + .expect("make directory public"); + assert!(SafeDir::open(&directory.0).is_err()); + fs::set_permissions(&directory.0, fs::Permissions::from_mode(0o500)) + .expect("remove owner write permission"); + assert!(SafeDir::open(&directory.0).is_err()); + fs::set_permissions(&directory.0, fs::Permissions::from_mode(0o700)) + .expect("restore private mode"); + let _ = fs::remove_file(link); + } + + #[test] + fn directory_handles_create_nested_dirs_and_exclusive_files_without_following_links() { + let directory = TestDirectory::new(); + let root = SafeDir::open_directory(&directory.0).expect("open output directory"); + let nested = root + .ensure_subdirectory("nested") + .expect("create nested output directory"); + nested + .create_new_file("file.txt", b"frozen bytes", 0o755) + .expect("create output file"); + assert_eq!( + fs::read(directory.0.join("nested/file.txt")).unwrap(), + b"frozen bytes" + ); + assert_eq!( + fs::metadata(directory.0.join("nested/file.txt")) + .unwrap() + .permissions() + .mode() + & 0o777, + 0o755 + ); + assert!(nested + .create_new_file("file.txt", b"replacement", 0o644) + .is_err()); + + let target = directory.0.join("outside.txt"); + fs::write(&target, b"outside").unwrap(); + symlink(&target, directory.0.join("nested/link.txt")).unwrap(); + assert!(nested + .create_new_file("link.txt", b"must not follow", 0o600) + .is_err()); + assert!(root.ensure_subdirectory("nested/link.txt").is_err()); + assert_eq!(fs::read(target).unwrap(), b"outside"); + } + + #[test] + fn descriptor_relative_file_removal_rejects_symlinks_and_traversal() { + let directory = TestDirectory::new(); + let root = directory.open(); + let nested = directory.0.join("nested"); + fs::create_dir(&nested).unwrap(); + fs::write(nested.join("remove.txt"), b"remove me").unwrap(); + let outside = directory.0.join("outside.txt"); + fs::write(&outside, b"keep me").unwrap(); + symlink(&outside, nested.join("link.txt")).unwrap(); + + root.remove_regular_file_path("nested/remove.txt") + .expect("remove regular nested file"); + assert!(!nested.join("remove.txt").exists()); + assert!(root.remove_regular_file_path("nested/link.txt").is_err()); + assert!(root.remove_regular_file_path("../outside.txt").is_err()); + assert_eq!(fs::read(outside).unwrap(), b"keep me"); + } + + #[test] + fn output_directory_open_accepts_nonprivate_existing_root_without_following_final_link() { + let directory = TestDirectory::new(); + fs::set_permissions(&directory.0, fs::Permissions::from_mode(0o755)).unwrap(); + assert!(SafeDir::open_directory(&directory.0).is_ok()); + assert!(SafeDir::open(&directory.0).is_err()); + let link = directory.0.with_extension("link"); + symlink(&directory.0, &link).unwrap(); + assert!(SafeDir::open_directory(&link).is_err()); + fs::remove_file(link).unwrap(); + } + + #[test] + fn bounded_reads_truncate_and_reject_non_files_and_unsafe_names() { + let directory = TestDirectory::new(); + let files = directory.open(); + fs::write(directory.0.join("input"), b"abcdef").expect("write input"); + assert_eq!( + files.bounded_read("input", 4).expect("bounded read"), + b"abcd" + ); + assert_eq!(files.bounded_read("input", 0).expect("empty read"), b""); + assert_eq!( + files + .bounded_read("input", -2) + .expect("negative bound matches Go"), + b"" + ); + assert!(files.bounded_read("../input", 3).is_err()); + assert!(files.bounded_read("./input", 3).is_err()); + symlink(directory.0.join("input"), directory.0.join("input-link")) + .expect("create input symlink"); + assert!(files.bounded_read("input-link", 3).is_err()); + assert!(files.atomic_write("nested/path", b"x", 0o600).is_err()); + assert!(files.atomic_write("bad", b"x", 0o1600).is_err()); + assert!(files.bounded_read("missing", 2).is_err()); + fs::create_dir(directory.0.join("subdir")).expect("create directory"); + assert_eq!( + files + .bounded_read("subdir", 2) + .expect_err("reject directory") + .kind(), + io::ErrorKind::InvalidData + ); + } + + #[test] + fn repository_browser_lists_without_following_links_and_bounds_file_reads() { + let directory = TestDirectory::new(); + let repo = directory.0.join("repo"); + fs::create_dir(&repo).expect("create repository root"); + fs::create_dir(repo.join("src")).expect("create source directory"); + fs::create_dir(repo.join(".git")).expect("create git metadata directory"); + fs::write(repo.join("src/main.rs"), b"hello world").expect("write source file"); + fs::write(repo.join(".secret"), b"hidden").expect("write hidden file"); + fs::write(repo.join(".git/config"), b"internal").expect("write git metadata"); + let outside = directory.0.join("outside"); + fs::create_dir(&outside).expect("create external directory"); + fs::write(outside.join("secret"), b"outside").expect("write external secret"); + symlink(&outside, repo.join("linked")).expect("create external directory symlink"); + let repository = SafeDir::open_directory(&repo).expect("open repository root"); + + let (visible, truncated) = repository + .list_directory_path("", &repo, false, 20) + .expect("list visible root entries"); + assert!(!truncated); + assert_eq!( + visible + .iter() + .map(|entry| entry.name.as_str()) + .collect::>(), + ["linked", "src"] + ); + assert_eq!(visible[0].kind, super::SafeEntryKind::Symlink); + assert_eq!(visible[1].kind, super::SafeEntryKind::Directory); + let (mismatched_path, _) = repository + .list_directory_path("", &outside, true, 20) + .expect("validate enumerated names against opened directory"); + assert!(mismatched_path.is_empty()); + + let (with_hidden, _) = repository + .list_directory_path("", &repo, true, 20) + .expect("list hidden root entries"); + assert!(with_hidden.iter().any(|entry| entry.name == ".secret")); + assert!(!with_hidden.iter().any(|entry| entry.name == ".git")); + + let (bytes, truncated) = repository + .read_regular_file_path("src/main.rs", 5) + .expect("read bounded source preview"); + assert_eq!(bytes, b"hello"); + assert!(truncated); + assert!(repository + .read_regular_file_path("linked/secret", 32) + .is_err()); + assert!(repository + .read_regular_file_path("../outside/secret", 32) + .is_err()); + assert!(repository + .list_directory_path("linked", &repo.join("linked"), true, 20) + .is_err()); + assert!(repository.list_directory_path("", &repo, true, 0).is_err()); + } + + #[test] + fn lexical_containment_uses_components_and_normalizes_parent_segments() { + assert!(is_within( + Path::new("/tmp/state"), + Path::new("/tmp/state/object") + )); + assert!(is_within( + Path::new("/tmp/state"), + Path::new("/tmp/state/nested/../object") + )); + assert!(!is_within( + Path::new("/tmp/state"), + Path::new("/tmp/state-old/file") + )); + assert!(!is_within( + Path::new("/tmp/state"), + Path::new("/tmp/elsewhere") + )); + } + + #[test] + fn opened_directory_handle_survives_path_renaming() { + let directory = TestDirectory::new(); + let files = directory.open(); + let moved = directory.0.with_extension("moved"); + fs::rename(&directory.0, &moved).expect("rename opened directory"); + files + .atomic_write("entry", b"anchored", 0o600) + .expect("write through open directory handle"); + assert_eq!( + fs::read(moved.join("entry")).expect("read anchored file"), + b"anchored" + ); + fs::rename(&moved, &directory.0).expect("restore test directory"); + } + + #[test] + fn state_root_opens_records_and_private_file_areas_across_reopen() { + let directory = TestDirectory::new(); + let state_path = directory.0.join("rover-state"); + let canonical = { + let root = StateRoot::open(&state_path).expect("open new state root"); + assert_eq!( + root.path(), + fs::canonicalize(&state_path).expect("canonical state path") + ); + root.records() + .put("task", "task_123", &serde_json::json!({"value": 7}), "") + .expect("write state record"); + let digest = root.put_blob(b"state-root blob").expect("write blob"); + assert_eq!( + root.read_blob(&digest.to_hex()).expect("read blob"), + b"state-root blob" + ); + root.tasks() + .atomic_write("stdout", b"task output", 0o600) + .expect("write task output"); + root.checks() + .atomic_write("report", b"check report", 0o600) + .expect("write check output"); + + for name in ["objects", "tasks", "checks"] { + assert_eq!( + fs::metadata(state_path.join(name)) + .expect("directory metadata") + .permissions() + .mode() + & 0o777, + 0o700 + ); + } + assert_eq!( + fs::metadata(state_path.join("rover.db")) + .expect("database metadata") + .permissions() + .mode() + & 0o777, + 0o600 + ); + root.path().to_owned() + }; + + let reopened = StateRoot::open(&state_path).expect("reopen state root"); + assert_eq!(reopened.path(), canonical); + let record = reopened + .records() + .get("task", "task_123") + .expect("read record after reopen"); + assert_eq!(record["value"], 7); + assert_eq!( + reopened + .tasks() + .bounded_read("stdout", 100) + .expect("read task output"), + b"task output" + ); + } + + #[test] + fn task_workspace_path_prepares_private_parent_but_leaves_git_leaf_absent() { + let directory = TestDirectory::new(); + let state_path = directory.0.join("state"); + let state = StateRoot::open(&state_path).expect("open state root"); + let workspace = state + .task_workspace_path("task_0123456789abcdef") + .expect("prepare task worktree parent"); + assert_eq!( + workspace, + state.path().join("tasks/task_0123456789abcdef/workspace") + ); + assert!(!workspace.exists(), "Git worktree leaf must remain absent"); + assert_eq!( + fs::metadata(workspace.parent().unwrap()) + .unwrap() + .permissions() + .mode() + & 0o777, + 0o700 + ); + assert!(state.task_workspace_path("../outside").is_err()); + } + + #[test] + fn worktree_admission_lock_is_released_when_owner_panics() { + let directory = TestDirectory::new(); + let state_path = directory.0.join("state"); + let state = Arc::new(StateRoot::open(&state_path).expect("open state root")); + let workspace = state + .task_workspace_path("task-lock-test") + .expect("prepare task workspace"); + let (locked_sender, locked_receiver) = mpsc::channel(); + let worker_state = Arc::clone(&state); + let worker_workspace = workspace.clone(); + let worker = thread::spawn(move || { + let _lock = worker_state + .lock_worktree_admission(&worker_workspace) + .expect("acquire admission lock"); + locked_sender.send(()).expect("signal lock acquisition"); + panic!("simulate owner crash while holding OS lock"); + }); + locked_receiver + .recv_timeout(std::time::Duration::from_secs(2)) + .expect("worker acquired OS lock"); + assert!(worker.join().is_err(), "fixture worker should panic"); + + let reacquired = state + .lock_worktree_admission(&workspace) + .expect("OS releases lock when owner unwinds"); + drop(reacquired); + } + + #[test] + fn state_root_refuses_unrelated_nonempty_directories_without_mutating_them() { + let directory = TestDirectory::new(); + let unrelated = directory.0.join("unrelated"); + fs::create_dir(&unrelated).expect("create unrelated directory"); + fs::set_permissions(&unrelated, fs::Permissions::from_mode(0o755)) + .expect("make unrelated directory public"); + fs::write(unrelated.join("keep.txt"), b"keep").expect("write unrelated file"); + + assert!(StateRoot::open(&unrelated).is_err()); + assert_eq!( + fs::metadata(&unrelated) + .expect("unrelated metadata") + .permissions() + .mode() + & 0o777, + 0o755 + ); + assert!(!unrelated.join("objects").exists()); + } + + #[test] + fn state_root_refuses_the_shared_temporary_root_without_changing_it() { + let temporary_root = std::env::temp_dir() + .canonicalize() + .expect("canonical temporary root"); + let old_mode = fs::metadata(&temporary_root) + .expect("temporary root metadata") + .permissions() + .mode() + & 0o777; + assert!(StateRoot::open(&temporary_root).is_err()); + assert_eq!( + fs::metadata(&temporary_root) + .expect("temporary root metadata after refusal") + .permissions() + .mode() + & 0o777, + old_mode + ); + } + + #[test] + fn state_root_rejects_symlink_components_and_database_symlinks() { + let directory = TestDirectory::new(); + let target = directory.0.join("target"); + fs::create_dir(&target).expect("create target directory"); + fs::set_permissions(&target, fs::Permissions::from_mode(0o700)) + .expect("make target private"); + let link = directory.0.join("linked-root"); + symlink(&target, &link).expect("create root symlink"); + assert!(StateRoot::open(&link).is_err()); + assert!(StateRoot::open(link.join("nested")).is_err()); + + let database_root = directory.0.join("database-link"); + fs::create_dir(&database_root).expect("create database-link root"); + fs::set_permissions(&database_root, fs::Permissions::from_mode(0o700)) + .expect("make database-link root private"); + fs::write(directory.0.join("outside.db"), b"not a database").expect("write outside db"); + symlink( + directory.0.join("outside.db"), + database_root.join("rover.db"), + ) + .expect("plant database symlink"); + assert!(StateRoot::open(&database_root).is_err()); + } + + #[test] + fn state_root_refuses_a_future_database_schema() { + let directory = TestDirectory::new(); + let state_path = directory.0.join("future-state"); + drop(StateRoot::open(&state_path).expect("create database")); + let connection = + rusqlite::Connection::open(state_path.join("rover.db")).expect("open test database"); + connection + .pragma_update(None, "user_version", 99_i64) + .expect("set future schema"); + drop(connection); + assert!(StateRoot::open(&state_path).is_err()); + } +} diff --git a/crates/rover-store/src/legacy.rs b/crates/rover-store/src/legacy.rs new file mode 100644 index 0000000..97dc833 --- /dev/null +++ b/crates/rover-store/src/legacy.rs @@ -0,0 +1,802 @@ +//! Read-only inspection and migration dry-run reporting for Go Rover state. +//! +//! The inspector never initializes, migrates, checkpoints, or writes to the +//! source. It reports database compatibility, row counts/digests, invalid JSON +//! and identifiers, and whether content-addressed objects verify. Live task and +//! check trees are called out but are not included in the import scope. + +use std::fs; +use std::io; +use std::path::{Path, PathBuf}; + +use rover_core::Id; +use rusqlite::{Connection, OpenFlags}; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; + +use crate::files::{canonical_state_path, SafeDir}; + +mod import; +pub use import::{LegacyImportError, LegacyImportOptions, LegacyImportReceipt, LegacyImportReport}; + +/// The inspected database's migration compatibility class. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum LegacySchema { + /// The Go v1 schema, which has no Rust migration ledger. + GoV1, + /// A Rust v1 database that already has the reviewed migration ledger. + RoverRustV1, + /// An empty `SQLite` file that Rust could initialize as a new state store. + EmptyUnversioned, + /// A schema that cannot safely be imported by the current Rust version. + Incompatible, +} + +/// Counts and a stable digest for one imported SQL table. +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +pub struct TableInventory { + /// Number of rows in the table. + pub rows: u64, + /// Total UTF-8 byte count of the row payload fields reported by the query. + pub payload_bytes: u64, + /// SHA-256 over the ordered, length-framed source fields. + pub digest: String, +} + +/// Counts for verified content-addressed objects in the Go `objects/` folder. +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +pub struct ObjectInventory { + /// Number of digest-named object files verified successfully. + pub verified: u64, + /// Total bytes in verified objects. + pub bytes: u64, + /// SHA-256 over the ordered object digest names and verified sizes. + pub digest: String, +} + +/// Metadata-only inventory of the live task/check filesystem tree. File bytes +/// are not read, hashed, or included in the database/object import scope. +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +pub struct RuntimeTreeInventory { + /// Regular files found without following symlinks. + pub files: u64, + /// Child directories found (the tree root itself is not counted). + pub directories: u64, + /// Bytes in regular files. + pub bytes: u64, + /// Symlinks observed and deliberately not traversed. + pub symlinks: u64, + /// Sockets, devices, and other non-regular entries observed. + pub other_entries: u64, +} + +/// Dry-run outcome. It gives prospective import counts and blockers without +/// creating a destination or touching source data. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct MigrationDryRun { + /// Whether database rows and verified objects can be imported after the + /// source has been quiesced. + pub database_and_objects_importable_after_quiesce: bool, + /// Records expected to import. + pub records: u64, + /// Events expected to import. + pub events: u64, + /// Idempotency keys expected to import. + pub request_keys: u64, + /// Content-addressed objects expected to import. + pub objects: u64, + /// Count of invalid record identifiers. + pub invalid_record_ids: u64, + /// Count of malformed record JSON payloads. + pub malformed_record_json: u64, + /// Reasons this state cannot be imported as reported. + pub blockers: Vec, + /// Important source data outside the database/object import scope. + pub warnings: Vec, + /// Combined digest of database tables and verified objects. + pub source_digest: String, +} + +/// Read-only inventory of an existing Go or Rover Rust state root. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct LegacyStateInventory { + /// Canonical absolute source directory. + pub root: PathBuf, + /// `SQLite` `user_version`. + pub user_version: i64, + /// User table names discovered in `sqlite_master`. + pub tables: Vec, + /// Compatibility assessment for the database schema. + pub schema: LegacySchema, + /// Size of the main `SQLite` database file. WAL sidecars are not included. + pub database_bytes: u64, + /// Record table counts and digest. + pub records: TableInventory, + /// Event table counts and digest. + pub events: TableInventory, + /// Request-key table counts and digest. + pub request_keys: TableInventory, + /// Content-addressed object inventory. + pub objects: ObjectInventory, + /// Metadata-only inventory of task workspaces and task outputs. + pub task_tree: RuntimeTreeInventory, + /// Metadata-only inventory of check workspaces and outputs. + pub check_tree: RuntimeTreeInventory, + /// Unrecognized top-level entries left out of the database/object import. + pub unmanaged_root_entries: Vec, + /// Prospective migration assessment. + pub dry_run: MigrationDryRun, +} + +impl LegacyStateInventory { + /// Inspect a Go/Rover state root without writing to it. + /// + /// Database access is `SQLite` read-only plus `query_only`; no migration, + /// journal checkpoint, or filesystem creation is performed. A normal + /// `SQLite` read transaction provides a consistent view of the three data + /// tables. Files in `tasks/` and `checks/` may change independently, so the + /// caller must stop Rover before relying on the dry-run as a migration + /// approval. + /// + /// # Errors + /// + /// Returns an I/O error for unsafe state paths, unreadable files, or + /// `SQLite` inspection failures. + #[allow(clippy::too_many_lines)] + pub fn inspect(root: impl AsRef) -> io::Result { + let root = canonical_state_path(root.as_ref())?; + let _root_handle = SafeDir::open(&root)?; + let database_path = root.join("rover.db"); + let database_metadata = fs::symlink_metadata(&database_path)?; + if !database_metadata.file_type().is_file() { + return Err(invalid_data("state database must be a regular file")); + } + + let mut connection = Connection::open_with_flags( + &database_path, + OpenFlags::SQLITE_OPEN_READ_ONLY + | OpenFlags::SQLITE_OPEN_NOFOLLOW + | OpenFlags::SQLITE_OPEN_FULL_MUTEX, + ) + .map_err(sqlite_io)?; + connection + .execute_batch("PRAGMA query_only=ON") + .map_err(sqlite_io)?; + let transaction = connection.transaction().map_err(sqlite_io)?; + + let user_version: i64 = transaction + .pragma_query_value(None, "user_version", |row| row.get(0)) + .map_err(sqlite_io)?; + let mut tables = transaction + .prepare( + "SELECT name FROM sqlite_master WHERE type='table' \ + AND name NOT LIKE 'sqlite_%' ORDER BY name", + ) + .map_err(sqlite_io)? + .query_map([], |row| row.get::<_, String>(0)) + .map_err(sqlite_io)? + .collect::, _>>() + .map_err(sqlite_io)?; + tables.sort(); + + let schema = match user_version { + 1 => { + let ledger = tables.iter().any(|table| table == "schema_migrations"); + if ledger { + if crate::validate_v1(&transaction).is_ok() { + LegacySchema::RoverRustV1 + } else { + LegacySchema::Incompatible + } + } else if crate::validate_v1_tables(&transaction, false).is_ok() { + LegacySchema::GoV1 + } else { + LegacySchema::Incompatible + } + } + 0 if !crate::has_user_schema_objects(&transaction).map_err(migration_io)? => { + LegacySchema::EmptyUnversioned + } + _ => LegacySchema::Incompatible, + }; + + let mut blockers = Vec::new(); + if schema == LegacySchema::Incompatible { + blockers.push(format!( + "database schema version {user_version} or its table definitions are not compatible" + )); + } + + let mut records = TableInventory::default(); + let mut events = TableInventory::default(); + let mut request_keys = TableInventory::default(); + let mut invalid_record_ids = 0; + let mut malformed_record_json = 0; + if matches!(schema, LegacySchema::GoV1 | LegacySchema::RoverRustV1) { + (records, invalid_record_ids, malformed_record_json) = + inspect_records(&transaction).map_err(sqlite_io)?; + events = inspect_events(&transaction).map_err(sqlite_io)?; + request_keys = inspect_request_keys(&transaction).map_err(sqlite_io)?; + if invalid_record_ids > 0 { + blockers.push(format!( + "{invalid_record_ids} records have invalid kind or ID values" + )); + } + if malformed_record_json > 0 { + blockers.push(format!( + "{malformed_record_json} records have malformed JSON payloads" + )); + } + } + + transaction.commit().map_err(sqlite_io)?; + + let (objects, object_blockers) = inspect_objects(&root)?; + blockers.extend(object_blockers); + let mut warnings = Vec::new(); + let task_tree = inspect_runtime_tree(&root.join("tasks"))?; + let check_tree = inspect_runtime_tree(&root.join("checks"))?; + if tree_has_content(&task_tree) { + warnings.push(format!( + "tasks/ contains {} files ({} bytes), {} directories, {} symlinks, and {} other entries; it is inventoried but excluded from this import", + task_tree.files, + task_tree.bytes, + task_tree.directories, + task_tree.symlinks, + task_tree.other_entries + )); + } + if tree_has_content(&check_tree) { + warnings.push(format!( + "checks/ contains {} files ({} bytes), {} directories, {} symlinks, and {} other entries; it is inventoried but excluded from this import", + check_tree.files, + check_tree.bytes, + check_tree.directories, + check_tree.symlinks, + check_tree.other_entries + )); + } + let unmanaged_root_entries = inspect_unmanaged_root_entries(&root)?; + if !unmanaged_root_entries.is_empty() { + warnings.push(format!( + "unmanaged root entries are preserved and excluded: {}", + unmanaged_root_entries.join(", ") + )); + } + warnings.push( + "the source must be quiesced before import; SQLite rows and filesystem objects do not share one atomic snapshot".to_owned(), + ); + + let objects_digest = objects.digest.clone(); + let source_digest = combined_digest(&[ + ("records", &records.digest), + ("events", &events.digest), + ("request_keys", &request_keys.digest), + ("objects", &objects_digest), + ]); + let database_and_objects_importable_after_quiesce = blockers.is_empty() + && matches!( + schema, + LegacySchema::GoV1 | LegacySchema::RoverRustV1 | LegacySchema::EmptyUnversioned + ); + let dry_run = MigrationDryRun { + database_and_objects_importable_after_quiesce, + records: records.rows, + events: events.rows, + request_keys: request_keys.rows, + objects: objects.verified, + invalid_record_ids, + malformed_record_json, + blockers, + warnings, + source_digest, + }; + + Ok(Self { + root, + user_version, + tables, + schema, + database_bytes: database_metadata.len(), + records, + events, + request_keys, + objects, + task_tree, + check_tree, + unmanaged_root_entries, + dry_run, + }) + } +} + +fn inspect_records(connection: &Connection) -> rusqlite::Result<(TableInventory, u64, u64)> { + let mut statement = + connection.prepare("SELECT kind,id,payload,updated FROM records ORDER BY kind,id")?; + let mut rows = statement.query([])?; + let mut inventory = TableInventory::default(); + let mut digest = Sha256::new(); + let mut invalid_ids = 0; + let mut malformed_json = 0; + while let Some(row) = rows.next()? { + let kind: String = row.get(0)?; + let id: String = row.get(1)?; + let payload: String = row.get(2)?; + let updated: String = row.get(3)?; + update_field(&mut digest, kind.as_bytes()); + update_field(&mut digest, id.as_bytes()); + update_field(&mut digest, payload.as_bytes()); + update_field(&mut digest, updated.as_bytes()); + if Id::parse(kind.as_str()).is_err() || Id::parse(id.as_str()).is_err() { + invalid_ids += 1; + } + if crate::parse_payload(&payload).is_err() { + malformed_json += 1; + } + inventory.payload_bytes += u64::try_from(payload.len()).unwrap_or(u64::MAX); + inventory.rows += 1; + } + inventory.digest = hex_digest(digest.finalize().as_slice()); + Ok((inventory, invalid_ids, malformed_json)) +} + +fn inspect_events(connection: &Connection) -> rusqlite::Result { + let mut statement = + connection.prepare("SELECT seq,kind,ref,payload,at FROM events ORDER BY seq")?; + let mut rows = statement.query([])?; + let mut inventory = TableInventory::default(); + let mut digest = Sha256::new(); + while let Some(row) = rows.next()? { + let sequence: i64 = row.get(0)?; + let kind: String = row.get(1)?; + let reference: String = row.get(2)?; + let payload: String = row.get(3)?; + let at: String = row.get(4)?; + update_field(&mut digest, &sequence.to_be_bytes()); + update_field(&mut digest, kind.as_bytes()); + update_field(&mut digest, reference.as_bytes()); + update_field(&mut digest, payload.as_bytes()); + update_field(&mut digest, at.as_bytes()); + inventory.payload_bytes += u64::try_from(payload.len()).unwrap_or(u64::MAX); + inventory.rows += 1; + } + inventory.digest = hex_digest(digest.finalize().as_slice()); + Ok(inventory) +} + +fn inspect_request_keys(connection: &Connection) -> rusqlite::Result { + let mut statement = + connection.prepare("SELECT key,digest,ref FROM request_keys ORDER BY key")?; + let mut rows = statement.query([])?; + let mut inventory = TableInventory::default(); + let mut digest = Sha256::new(); + while let Some(row) = rows.next()? { + let key: String = row.get(0)?; + let value_digest: String = row.get(1)?; + let reference: String = row.get(2)?; + update_field(&mut digest, key.as_bytes()); + update_field(&mut digest, value_digest.as_bytes()); + update_field(&mut digest, reference.as_bytes()); + inventory.payload_bytes += + u64::try_from(key.len() + value_digest.len() + reference.len()).unwrap_or(u64::MAX); + inventory.rows += 1; + } + inventory.digest = hex_digest(digest.finalize().as_slice()); + Ok(inventory) +} + +fn inspect_objects(root: &Path) -> io::Result<(ObjectInventory, Vec)> { + let path = root.join("objects"); + let metadata = match fs::symlink_metadata(&path) { + Ok(metadata) => metadata, + Err(error) if error.kind() == io::ErrorKind::NotFound => { + return Ok((ObjectInventory::default(), Vec::new())); + } + Err(error) => return Err(error), + }; + if metadata.file_type().is_symlink() || !metadata.is_dir() { + return Ok(( + ObjectInventory::default(), + vec!["objects/ is not a regular directory".to_owned()], + )); + } + let directory = match SafeDir::open(&path) { + Ok(directory) => directory, + Err(error) => { + return Ok(( + ObjectInventory::default(), + vec![format!("objects/ cannot be safely opened: {error}")], + )); + } + }; + let mut entries = fs::read_dir(&path)?.collect::, _>>()?; + entries.sort_by_key(fs::DirEntry::file_name); + let mut inventory = ObjectInventory::default(); + let mut digest = Sha256::new(); + let mut blockers = Vec::new(); + for entry in entries { + let name = if let Some(name) = entry.file_name().to_str() { + name.to_owned() + } else { + blockers.push("objects/ contains a non-UTF-8 filename".to_owned()); + continue; + }; + let entry_metadata = fs::symlink_metadata(entry.path())?; + if entry_metadata.file_type().is_symlink() || !entry_metadata.is_file() { + blockers.push(format!("objects/{name} is not a regular object file")); + continue; + } + match directory.read_blob(&name) { + Ok(bytes) => { + update_field(&mut digest, name.as_bytes()); + update_field( + &mut digest, + &u64::try_from(bytes.len()).unwrap_or(u64::MAX).to_be_bytes(), + ); + inventory.bytes += u64::try_from(bytes.len()).unwrap_or(u64::MAX); + inventory.verified += 1; + } + Err(error) => blockers.push(format!( + "objects/{name} failed digest/size validation: {error}" + )), + } + } + inventory.digest = hex_digest(digest.finalize().as_slice()); + Ok((inventory, blockers)) +} + +fn inspect_runtime_tree(root: &Path) -> io::Result { + fn visit(path: &Path, inventory: &mut RuntimeTreeInventory, is_root: bool) -> io::Result<()> { + let metadata = match fs::symlink_metadata(path) { + Ok(metadata) => metadata, + Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(()), + Err(error) => return Err(error), + }; + if metadata.file_type().is_symlink() { + inventory.symlinks += 1; + } else if metadata.is_dir() { + if !is_root { + inventory.directories += 1; + } + let mut children = fs::read_dir(path)?.collect::, _>>()?; + children.sort_by_key(fs::DirEntry::file_name); + for child in children { + visit(&child.path(), inventory, false)?; + } + } else if metadata.is_file() { + inventory.files += 1; + inventory.bytes = inventory + .bytes + .checked_add(metadata.len()) + .ok_or_else(|| invalid_data("runtime tree byte count overflow"))?; + } else { + inventory.other_entries += 1; + } + Ok(()) + } + + let mut inventory = RuntimeTreeInventory::default(); + visit(root, &mut inventory, true)?; + Ok(inventory) +} + +fn tree_has_content(tree: &RuntimeTreeInventory) -> bool { + tree.files > 0 || tree.directories > 0 || tree.symlinks > 0 || tree.other_entries > 0 +} + +fn inspect_unmanaged_root_entries(root: &Path) -> io::Result> { + let known = [ + "rover.db", + "rover.db-wal", + "rover.db-shm", + "rover.db-journal", + "objects", + "tasks", + "checks", + ]; + let mut entries = fs::read_dir(root)? + .map(|entry| entry.map(|entry| entry.file_name().to_string_lossy().into_owned())) + .collect::, _>>()?; + entries.retain(|entry| !known.contains(&entry.as_str())); + entries.sort(); + Ok(entries) +} + +fn combined_digest(datasets: &[(&str, &str)]) -> String { + let mut digest = Sha256::new(); + for (name, value) in datasets { + update_field(&mut digest, name.as_bytes()); + update_field(&mut digest, value.as_bytes()); + } + hex_digest(digest.finalize().as_slice()) +} + +fn update_field(digest: &mut Sha256, bytes: &[u8]) { + digest.update(u64::try_from(bytes.len()).unwrap_or(u64::MAX).to_be_bytes()); + digest.update(bytes); +} + +fn hex_digest(bytes: &[u8]) -> String { + let mut output = String::with_capacity(bytes.len() * 2); + for byte in bytes { + use std::fmt::Write as _; + let _ = write!(output, "{byte:02x}"); + } + output +} + +fn sqlite_io(error: rusqlite::Error) -> io::Error { + io::Error::other(error) +} + +fn migration_io(error: crate::MigrationError) -> io::Error { + io::Error::other(error) +} + +fn invalid_data(message: &str) -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, message) +} + +#[cfg(test)] +mod tests { + use std::fs; + use std::path::{Path, PathBuf}; + use std::sync::atomic::{AtomicU64, Ordering}; + use std::time::{SystemTime, UNIX_EPOCH}; + + use rusqlite::Connection; + + use super::{LegacySchema, LegacyStateInventory}; + + static TEST_SEQUENCE: AtomicU64 = AtomicU64::new(0); + + struct TestRoot(PathBuf); + + impl TestRoot { + fn new() -> Self { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("clock follows epoch") + .as_nanos(); + let path = std::env::temp_dir().join(format!( + "rover-legacy-inventory-{}-{timestamp}-{}", + std::process::id(), + TEST_SEQUENCE.fetch_add(1, Ordering::Relaxed) + )); + fs::create_dir(&path).expect("create root"); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt as _; + fs::set_permissions(&path, fs::Permissions::from_mode(0o700)) + .expect("make root private"); + } + Self(path) + } + + fn create_go_v1(&self) { + let connection = Connection::open(self.0.join("rover.db")).expect("create database"); + connection + .execute_batch( + "CREATE TABLE records (kind TEXT NOT NULL,id TEXT NOT NULL,payload TEXT NOT NULL,updated TEXT NOT NULL,PRIMARY KEY(kind,id));\ + CREATE TABLE events (seq INTEGER PRIMARY KEY AUTOINCREMENT,kind TEXT NOT NULL,ref TEXT NOT NULL,payload TEXT NOT NULL,at TEXT NOT NULL);\ + CREATE TABLE request_keys (key TEXT PRIMARY KEY,digest TEXT NOT NULL,ref TEXT NOT NULL);\ + PRAGMA user_version=1;\ + INSERT INTO records VALUES('task','task_a','{\"objective\":\"fixture\"}','2026-01-01T00:00:00Z');\ + INSERT INTO events(kind,ref,payload,at) VALUES('task.created','task_a','{\"objective\":\"fixture\"}','2026-01-01T00:00:00Z');\ + INSERT INTO request_keys VALUES('request_a','digest_a','task_a');", + ) + .expect("write Go v1 fixture"); + } + + fn snapshot(&self) -> Vec<(String, Vec)> { + fn collect(path: &Path, prefix: &str, entries: &mut Vec<(String, Vec)>) { + let mut children = fs::read_dir(path) + .expect("list source directory") + .map(|entry| entry.expect("read directory entry")) + .collect::>(); + children.sort_by_key(fs::DirEntry::file_name); + for entry in children { + let name = entry.file_name().to_string_lossy().into_owned(); + let relative = if prefix.is_empty() { + name + } else { + format!("{prefix}/{name}") + }; + let metadata = fs::symlink_metadata(entry.path()).expect("stat source entry"); + if metadata.is_dir() { + entries.push((relative.clone(), b"directory".to_vec())); + collect(&entry.path(), &relative, entries); + } else { + entries.push((relative, fs::read(entry.path()).expect("read source file"))); + } + } + } + let mut entries = Vec::new(); + collect(&self.0, "", &mut entries); + entries.sort_by(|left, right| left.0.cmp(&right.0)); + entries + } + } + + impl Drop for TestRoot { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.0); + } + } + + #[test] + fn go_state_inventory_reports_counts_and_never_changes_source() { + let root = TestRoot::new(); + root.create_go_v1(); + let objects = root.0.join("objects"); + fs::create_dir(&objects).expect("create objects"); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt as _; + fs::set_permissions(&objects, fs::Permissions::from_mode(0o700)) + .expect("make objects private"); + } + let payload = b"retained artifact"; + let digest = rover_core::Sha256Digest::of(payload).to_hex(); + fs::write(objects.join(&digest), payload).expect("write artifact"); + let task_workspace = root.0.join("tasks/task_a/workspace"); + fs::create_dir_all(&task_workspace).expect("create runtime workspace"); + fs::write(task_workspace.join("log.txt"), b"runtime output").expect("write runtime output"); + fs::write(root.0.join("operator-note.txt"), b"preserve me") + .expect("write unmanaged source file"); + + let before = root.snapshot(); + let inventory = LegacyStateInventory::inspect(&root.0).expect("inspect Go state"); + let repeated = LegacyStateInventory::inspect(&root.0).expect("repeat read-only inspection"); + let after = root.snapshot(); + + assert_eq!(before, after, "read-only inventory modified the source"); + assert_eq!(inventory.schema, LegacySchema::GoV1); + assert_eq!(inventory.user_version, 1); + assert_eq!(inventory.records.rows, 1); + assert_eq!(inventory.events.rows, 1); + assert_eq!(inventory.request_keys.rows, 1); + assert_eq!(inventory.objects.verified, 1); + assert_eq!(inventory.objects.bytes, payload.len() as u64); + assert_eq!(inventory.task_tree.files, 1); + assert_eq!(inventory.task_tree.directories, 2); + assert_eq!(inventory.task_tree.bytes, b"runtime output".len() as u64); + assert_eq!(inventory.unmanaged_root_entries, ["operator-note.txt"]); + assert!(inventory + .dry_run + .warnings + .iter() + .any(|warning| warning.contains("tasks/") && warning.contains("excluded"))); + assert!( + inventory + .dry_run + .database_and_objects_importable_after_quiesce + ); + assert_eq!(inventory.dry_run.records, 1); + assert_eq!(inventory.dry_run.objects, 1); + assert!(!inventory.dry_run.source_digest.is_empty()); + assert_eq!( + inventory.dry_run.source_digest, + repeated.dry_run.source_digest + ); + } + + #[test] + fn incompatible_schema_and_tampered_objects_block_dry_run() { + let root = TestRoot::new(); + root.create_go_v1(); + let objects = root.0.join("objects"); + fs::create_dir(&objects).expect("create objects"); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt as _; + fs::set_permissions(&objects, fs::Permissions::from_mode(0o700)) + .expect("make objects private"); + } + fs::write(objects.join("a".repeat(64)), b"tampered").expect("write invalid object"); + let inventory = LegacyStateInventory::inspect(&root.0).expect("inspect state"); + assert_eq!(inventory.schema, LegacySchema::GoV1); + assert!( + !inventory + .dry_run + .database_and_objects_importable_after_quiesce + ); + assert!(inventory + .dry_run + .blockers + .iter() + .any(|blocker| blocker.contains("digest/size validation"))); + + let database = Connection::open(root.0.join("rover.db")).expect("open database"); + database + .pragma_update(None, "user_version", 42) + .expect("set unsupported version"); + drop(database); + let inventory = LegacyStateInventory::inspect(&root.0).expect("inspect unsupported state"); + assert_eq!(inventory.schema, LegacySchema::Incompatible); + assert!( + !inventory + .dry_run + .database_and_objects_importable_after_quiesce + ); + } + + #[test] + fn empty_database_is_reported_as_initializable_without_writes() { + let root = TestRoot::new(); + drop(Connection::open(root.0.join("rover.db")).expect("create empty database")); + let before = root.snapshot(); + + let inventory = LegacyStateInventory::inspect(&root.0).expect("inspect empty database"); + + assert_eq!(root.snapshot(), before); + assert_eq!(inventory.schema, LegacySchema::EmptyUnversioned); + assert!( + inventory + .dry_run + .database_and_objects_importable_after_quiesce + ); + assert_eq!(inventory.dry_run.records, 0); + assert_eq!(inventory.dry_run.events, 0); + assert_eq!(inventory.dry_run.request_keys, 0); + } + + #[test] + fn existing_rust_v1_state_is_recognized_read_only() { + let root = TestRoot::new(); + let mut connection = Connection::open(root.0.join("rover.db")).expect("create database"); + crate::migrate(&mut connection).expect("initialize Rust schema fixture"); + drop(connection); + let before = root.snapshot(); + + let inventory = LegacyStateInventory::inspect(&root.0).expect("inspect Rust v1 state"); + + assert_eq!(root.snapshot(), before); + assert_eq!(inventory.schema, LegacySchema::RoverRustV1); + assert!( + inventory + .dry_run + .database_and_objects_importable_after_quiesce + ); + } + + #[test] + fn invalid_record_ids_and_json_are_counted_as_blockers() { + let root = TestRoot::new(); + root.create_go_v1(); + let connection = Connection::open(root.0.join("rover.db")).expect("open fixture"); + connection + .execute( + "INSERT INTO records(kind,id,payload,updated) VALUES(?1,?2,?3,?4)", + ("bad kind", "bad/id", "{", "2026-01-01T00:00:00Z"), + ) + .expect("insert malformed record"); + drop(connection); + + let inventory = LegacyStateInventory::inspect(&root.0).expect("inspect malformed state"); + + assert_eq!(inventory.dry_run.invalid_record_ids, 1); + assert_eq!(inventory.dry_run.malformed_record_json, 1); + assert!( + !inventory + .dry_run + .database_and_objects_importable_after_quiesce + ); + } + + #[test] + #[cfg(unix)] + fn public_state_root_is_refused_without_changing_it() { + use std::os::unix::fs::PermissionsExt as _; + + let root = TestRoot::new(); + root.create_go_v1(); + fs::set_permissions(&root.0, fs::Permissions::from_mode(0o755)).expect("make root public"); + let before = root.snapshot(); + + assert!(LegacyStateInventory::inspect(&root.0).is_err()); + + assert_eq!(root.snapshot(), before); + } +} diff --git a/crates/rover-store/src/legacy/import.rs b/crates/rover-store/src/legacy/import.rs new file mode 100644 index 0000000..766b944 --- /dev/null +++ b/crates/rover-store/src/legacy/import.rs @@ -0,0 +1,1348 @@ +//! Idempotent import of Go Rover state into a new Rust state root. + +use std::error::Error; +use std::fmt; +use std::fs; +use std::io; +use std::path::Path; + +use rover_core::{Id, Sha256Digest, Timestamp}; +use rusqlite::{Connection, OpenFlags, OptionalExtension, Transaction, TransactionBehavior}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use sha2::{Digest, Sha256}; + +use super::{ + canonical_state_path, combined_digest, hex_digest, inspect_events, inspect_objects, + inspect_records, inspect_request_keys, update_field, LegacySchema, LegacyStateInventory, +}; +use crate::files::{SafeDir, StateRoot}; +use crate::{insert_event_payload, put_in_transaction, StoreError}; + +const IMPORT_KIND: &str = "migration"; +const IMPORT_EVENT: &str = "migration.imported"; +const SANITIZE_EVENT: &str = "record.migration_sanitized"; +const SANITIZE_ERROR: &str = + "imported Go state; process state was not restored; no automatic execution"; + +/// Explicit assertions required before import. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct LegacyImportOptions { + /// The operator has stopped Rover and every worker using the source state. + pub source_quiesced: bool, + /// The operator accepts that task/check trees and unmanaged root files are + /// inventoried but are not copied into the new state. + pub accept_excluded_runtime_data: bool, +} + +/// Durable import receipt stored in the destination state root. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct LegacyImportReceipt { + /// Receipt schema version. + pub schema: String, + /// SHA-256 of the canonical source path, also used as the receipt ID. + pub source_root_digest: String, + /// Digest of source records, events, request keys, and verified objects. + pub source_digest: String, + /// Source records copied, excluding released leases. + pub records: u64, + /// Source events copied before migration sanitation events. + pub events: u64, + /// Source request keys copied. + pub request_keys: u64, + /// Verified content-addressed objects copied. + pub objects: u64, + /// Grant records forced to revoked in the destination. + pub grants_revoked: u64, + /// Lease rows omitted and released during import. + pub leases_released: u64, + /// Nonterminal task/workflow records marked LOST. + pub executions_marked_lost: u64, + /// Number of records sanitized or lease rows released. + pub sanitized_records: u64, + /// RFC3339 import completion timestamp. + pub imported_at: String, +} + +/// Result of one import attempt. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct LegacyImportReport { + /// Durable receipt describing the source snapshot. + pub receipt: LegacyImportReceipt, + /// True when this exact source snapshot had already been imported. + pub already_imported: bool, +} + +/// A fail-closed legacy import error. +#[derive(Debug)] +pub enum LegacyImportError { + SourceNotQuiesced, + ExcludedData(Vec), + SourceBlocked(Vec), + PathsOverlap, + DestinationNotNew, + SourceChanged, + InvalidRecord(String), + InvalidReceipt, + Io(io::Error), + Sqlite(rusqlite::Error), + Store(StoreError), + Json(serde_json::Error), + Timestamp(String), +} + +impl fmt::Display for LegacyImportError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::SourceNotQuiesced => formatter.write_str("source must be quiesced before import"), + Self::ExcludedData(items) => write!( + formatter, + "runtime or unmanaged source data requires explicit exclusion consent: {}", + items.join(", ") + ), + Self::SourceBlocked(blockers) => write!( + formatter, + "source inventory blocks import: {}", + blockers.join("; ") + ), + Self::PathsOverlap => formatter.write_str("source and destination state trees overlap"), + Self::DestinationNotNew => formatter.write_str( + "destination is not empty, resumable, or already imported from this exact source", + ), + Self::SourceChanged => formatter.write_str("source changed during migration"), + Self::InvalidRecord(reason) => write!(formatter, "unsafe legacy record: {reason}"), + Self::InvalidReceipt => formatter + .write_str("migration receipt is invalid or belongs to another source snapshot"), + Self::Io(error) => write!( + formatter, + "legacy import filesystem operation failed: {error}" + ), + Self::Sqlite(error) => { + write!(formatter, "legacy import SQLite operation failed: {error}") + } + Self::Store(error) => { + write!(formatter, "legacy import store operation failed: {error}") + } + Self::Json(error) => write!(formatter, "legacy import JSON operation failed: {error}"), + Self::Timestamp(error) => write!(formatter, "legacy import timestamp failed: {error}"), + } + } +} + +impl Error for LegacyImportError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::Io(error) => Some(error), + Self::Sqlite(error) => Some(error), + Self::Store(error) => Some(error), + Self::Json(error) => Some(error), + Self::SourceNotQuiesced + | Self::ExcludedData(_) + | Self::SourceBlocked(_) + | Self::PathsOverlap + | Self::DestinationNotNew + | Self::SourceChanged + | Self::InvalidRecord(_) + | Self::InvalidReceipt + | Self::Timestamp(_) => None, + } + } +} + +impl From for LegacyImportError { + fn from(error: io::Error) -> Self { + Self::Io(error) + } +} + +impl From for LegacyImportError { + fn from(error: rusqlite::Error) -> Self { + Self::Sqlite(error) + } +} + +impl From for LegacyImportError { + fn from(error: StoreError) -> Self { + Self::Store(error) + } +} + +impl From for LegacyImportError { + fn from(error: serde_json::Error) -> Self { + Self::Json(error) + } +} + +impl LegacyStateInventory { + /// Import source rows and verified objects into a distinct new Rust state. + /// + /// Repeating the import verifies and returns its stored receipt without + /// duplicating database rows, events, request keys, or objects. The source + /// is only opened read-only. Grants are revoked, leases released, and + /// task/workflow process metadata sanitized to match safe restore behavior. + /// + /// # Errors + /// + /// Returns an error if the operator does not assert source quiescence, + /// excluded runtime files are not acknowledged, the inventory has blockers, + /// source and destination overlap, or the destination is unrelated. + pub fn import_to( + source_root: impl AsRef, + destination_root: impl AsRef, + options: LegacyImportOptions, + ) -> Result { + if !options.source_quiesced { + return Err(LegacyImportError::SourceNotQuiesced); + } + let inventory = Self::inspect(source_root)?; + if !inventory + .dry_run + .database_and_objects_importable_after_quiesce + { + return Err(LegacyImportError::SourceBlocked( + inventory.dry_run.blockers.clone(), + )); + } + let excluded = excluded_data(&inventory); + if !excluded.is_empty() && !options.accept_excluded_runtime_data { + return Err(LegacyImportError::ExcludedData(excluded)); + } + + let source_root = inventory.root.clone(); + let destination_root = canonical_state_path(destination_root.as_ref())?; + if paths_overlap(&source_root, &destination_root) { + return Err(LegacyImportError::PathsOverlap); + } + let source_root_digest = + Sha256Digest::of(source_root.as_os_str().as_encoded_bytes()).to_hex(); + let source_database = source_root.join("rover.db"); + let mut source_connection = Connection::open_with_flags( + &source_database, + OpenFlags::SQLITE_OPEN_READ_ONLY + | OpenFlags::SQLITE_OPEN_NOFOLLOW + | OpenFlags::SQLITE_OPEN_FULL_MUTEX, + )?; + source_connection.execute_batch("PRAGMA query_only=ON")?; + let source_transaction = source_connection.transaction()?; + validate_source_snapshot(&source_transaction, &source_root, &inventory)?; + let source_objects = object_names(&source_root)?; + if let Some(receipt) = preflight_destination( + &destination_root, + &source_root_digest, + &inventory.dry_run.source_digest, + &source_objects, + &source_transaction, + &inventory, + )? { + return Ok(LegacyImportReport { + receipt, + already_imported: true, + }); + } + + let destination = StateRoot::open(&destination_root)?; + let object_count = copy_objects(&source_root, &destination, &inventory)?; + if object_count != inventory.objects.verified { + return Err(LegacyImportError::SourceChanged); + } + + let mut destination_connection = destination.records().lock()?; + let transaction = + destination_connection.transaction_with_behavior(TransactionBehavior::Immediate)?; + ensure_destination_empty(&transaction)?; + let counts = if inventory.user_version == 0 { + RecordImportCounts::default() + } else { + copy_events(&source_transaction, &transaction)?; + let counts = copy_records(&source_transaction, &transaction)?; + copy_request_keys(&source_transaction, &transaction)?; + counts + }; + + // Refuse to commit the SQL snapshot if the source changed while its + // filesystem objects were copied. + let current = Self::inspect(&source_root)?; + if current.dry_run.source_digest != inventory.dry_run.source_digest { + return Err(LegacyImportError::SourceChanged); + } + + let receipt = new_import_receipt(&source_root_digest, &inventory, object_count, &counts)?; + let receipt_key_exists: bool = transaction.query_row( + "SELECT EXISTS(SELECT 1 FROM records WHERE kind=?1 AND id=?2)", + (IMPORT_KIND, &receipt.source_root_digest), + |row| row.get(0), + )?; + if receipt_key_exists { + return Err(LegacyImportError::InvalidRecord( + "source already contains the destination receipt key".to_owned(), + )); + } + put_in_transaction( + &transaction, + IMPORT_KIND, + &receipt.source_root_digest, + &receipt, + IMPORT_EVENT, + )?; + transaction.commit()?; + source_transaction.commit()?; + Ok(LegacyImportReport { + receipt, + already_imported: false, + }) + } +} + +#[derive(Default)] +struct RecordImportCounts { + records: u64, + grants_revoked: u64, + leases_released: u64, + executions_marked_lost: u64, + sanitized_records: u64, +} + +fn new_import_receipt( + source_root_digest: &str, + inventory: &LegacyStateInventory, + object_count: u64, + counts: &RecordImportCounts, +) -> Result { + Ok(LegacyImportReceipt { + schema: "rover-migration/v1".to_owned(), + source_root_digest: source_root_digest.to_owned(), + source_digest: inventory.dry_run.source_digest.clone(), + records: counts.records, + events: inventory.events.rows, + request_keys: inventory.request_keys.rows, + objects: object_count, + grants_revoked: counts.grants_revoked, + leases_released: counts.leases_released, + executions_marked_lost: counts.executions_marked_lost, + sanitized_records: counts.sanitized_records, + imported_at: Timestamp::now() + .to_rfc3339() + .map_err(|error| LegacyImportError::Timestamp(error.to_string()))?, + }) +} + +fn excluded_data(inventory: &LegacyStateInventory) -> Vec { + let mut excluded = Vec::new(); + if tree_nonempty(&inventory.task_tree) { + excluded.push("tasks/".to_owned()); + } + if tree_nonempty(&inventory.check_tree) { + excluded.push("checks/".to_owned()); + } + excluded.extend(inventory.unmanaged_root_entries.iter().cloned()); + excluded +} + +fn tree_nonempty(tree: &super::RuntimeTreeInventory) -> bool { + tree.files > 0 || tree.directories > 0 || tree.symlinks > 0 || tree.other_entries > 0 +} + +fn paths_overlap(left: &Path, right: &Path) -> bool { + left.starts_with(right) || right.starts_with(left) +} + +fn preflight_destination( + root: &Path, + source_root_digest: &str, + source_digest: &str, + source_objects: &[String], + source: &Transaction<'_>, + inventory: &LegacyStateInventory, +) -> Result, LegacyImportError> { + let metadata = match fs::symlink_metadata(root) { + Ok(metadata) => metadata, + Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(None), + Err(error) => return Err(error.into()), + }; + if metadata.file_type().is_symlink() || !metadata.is_dir() { + return Err(LegacyImportError::DestinationNotNew); + } + let entries = fs::read_dir(root)?.collect::, _>>()?; + if entries.is_empty() { + return Ok(None); + } + let database_path = root.join("rover.db"); + match fs::symlink_metadata(&database_path) { + Ok(metadata) if metadata.file_type().is_file() => {} + _ => return Err(LegacyImportError::DestinationNotNew), + } + let _root = SafeDir::open(root)?; + let connection = Connection::open_with_flags( + &database_path, + OpenFlags::SQLITE_OPEN_READ_ONLY + | OpenFlags::SQLITE_OPEN_NOFOLLOW + | OpenFlags::SQLITE_OPEN_FULL_MUTEX, + )?; + connection.execute_batch("PRAGMA query_only=ON")?; + crate::validate_v1(&connection).map_err(|_| LegacyImportError::DestinationNotNew)?; + + let marker = connection + .query_row( + "SELECT payload FROM records WHERE kind=?1 AND id=?2", + (IMPORT_KIND, source_root_digest), + |row| row.get::<_, String>(0), + ) + .optional()?; + if let Some(marker) = marker { + let receipt: LegacyImportReceipt = serde_json::from_str(&marker)?; + if receipt.schema != "rover-migration/v1" + || receipt.source_root_digest != source_root_digest + || receipt.source_digest != source_digest + { + return Err(LegacyImportError::InvalidReceipt); + } + verify_receipt_destination( + source, + &connection, + root, + &receipt, + source_objects, + inventory, + )?; + return Ok(Some(receipt)); + } + + let (records, events, request_keys): (i64, i64, i64) = connection.query_row( + "SELECT (SELECT count(*) FROM records),(SELECT count(*) FROM events),(SELECT count(*) FROM request_keys)", + [], + |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)), + )?; + if records != 0 || events != 0 || request_keys != 0 { + return Err(LegacyImportError::DestinationNotNew); + } + + ensure_resumable_destination(root, &entries, source_objects)?; + Ok(None) +} + +fn ensure_resumable_destination( + root: &Path, + entries: &[fs::DirEntry], + source_objects: &[String], +) -> Result<(), LegacyImportError> { + for folder in ["tasks", "checks"] { + let path = root.join(folder); + match fs::symlink_metadata(&path) { + Ok(metadata) if metadata.is_dir() && !metadata.file_type().is_symlink() => { + if fs::read_dir(path)?.next().transpose()?.is_some() { + return Err(LegacyImportError::DestinationNotNew); + } + } + Ok(_) => return Err(LegacyImportError::DestinationNotNew), + Err(error) if error.kind() == io::ErrorKind::NotFound => {} + Err(error) => return Err(error.into()), + } + } + let known = [ + "rover.db", + "rover.db-wal", + "rover.db-shm", + "rover.db-journal", + "objects", + "tasks", + "checks", + ]; + if entries.iter().any(|entry| { + entry + .file_name() + .to_str() + .is_none_or(|name| !known.contains(&name)) + }) { + return Err(LegacyImportError::DestinationNotNew); + } + + match fs::symlink_metadata(root.join("objects")) { + Ok(metadata) if metadata.is_dir() && !metadata.file_type().is_symlink() => { + let objects = SafeDir::open(root.join("objects"))?; + let mut existing = + fs::read_dir(root.join("objects"))?.collect::, _>>()?; + existing.sort_by_key(fs::DirEntry::file_name); + for entry in existing { + let name = entry + .file_name() + .to_str() + .ok_or(LegacyImportError::DestinationNotNew)? + .to_owned(); + if !source_objects.iter().any(|source| source == &name) { + return Err(LegacyImportError::DestinationNotNew); + } + objects.read_blob(&name)?; + } + } + Ok(_) => return Err(LegacyImportError::DestinationNotNew), + Err(error) if error.kind() == io::ErrorKind::NotFound => {} + Err(error) => return Err(error.into()), + } + Ok(()) +} + +fn object_names(root: &Path) -> Result, LegacyImportError> { + let path = root.join("objects"); + let metadata = match fs::symlink_metadata(&path) { + Ok(metadata) => metadata, + Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(Vec::new()), + Err(error) => return Err(error.into()), + }; + if metadata.file_type().is_symlink() || !metadata.is_dir() { + return Err(LegacyImportError::SourceChanged); + } + let directory = SafeDir::open(&path)?; + let mut entries = fs::read_dir(&path)?.collect::, _>>()?; + entries.sort_by_key(fs::DirEntry::file_name); + let mut names = Vec::with_capacity(entries.len()); + for entry in entries { + let name = entry + .file_name() + .to_str() + .ok_or(LegacyImportError::SourceChanged)? + .to_owned(); + directory.read_blob(&name)?; + names.push(name); + } + Ok(names) +} + +fn validate_source_snapshot( + transaction: &Transaction<'_>, + source_root: &Path, + inventory: &LegacyStateInventory, +) -> Result<(), LegacyImportError> { + let version: i64 = transaction.pragma_query_value(None, "user_version", |row| row.get(0))?; + let schema_valid = match inventory.schema { + LegacySchema::GoV1 => crate::validate_v1_tables(transaction, false).is_ok(), + LegacySchema::RoverRustV1 => crate::validate_v1(transaction).is_ok(), + LegacySchema::EmptyUnversioned => { + version == 0 && matches!(crate::has_user_schema_objects(transaction), Ok(false)) + } + LegacySchema::Incompatible => false, + }; + if !schema_valid { + return Err(LegacyImportError::SourceChanged); + } + let (records, _, _) = if version == 0 { + (super::TableInventory::default(), 0, 0) + } else { + inspect_records(transaction).map_err(LegacyImportError::Sqlite)? + }; + let events = if version == 0 { + super::TableInventory::default() + } else { + inspect_events(transaction).map_err(LegacyImportError::Sqlite)? + }; + let request_keys = if version == 0 { + super::TableInventory::default() + } else { + inspect_request_keys(transaction).map_err(LegacyImportError::Sqlite)? + }; + let (objects, blockers) = inspect_objects(source_root)?; + if !blockers.is_empty() { + return Err(LegacyImportError::SourceChanged); + } + let source_digest = combined_digest(&[ + ("records", &records.digest), + ("events", &events.digest), + ("request_keys", &request_keys.digest), + ("objects", &objects.digest), + ]); + if source_digest != inventory.dry_run.source_digest { + return Err(LegacyImportError::SourceChanged); + } + Ok(()) +} + +fn copy_objects( + source_root: &Path, + destination: &StateRoot, + inventory: &LegacyStateInventory, +) -> Result { + let names = object_names(source_root)?; + if names.is_empty() { + if inventory.objects.verified != 0 { + return Err(LegacyImportError::SourceChanged); + } + return Ok(0); + } + let source = SafeDir::open(source_root.join("objects"))?; + let mut hash = Sha256::new(); + let mut copied = 0_u64; + for name in names { + let bytes = source.read_blob(&name)?; + let digest = destination.put_blob(&bytes)?.to_hex(); + if digest != name { + return Err(LegacyImportError::SourceChanged); + } + update_field(&mut hash, name.as_bytes()); + update_field( + &mut hash, + &u64::try_from(bytes.len()).unwrap_or(u64::MAX).to_be_bytes(), + ); + copied += 1; + } + let digest = hex_digest(hash.finalize().as_slice()); + if digest != inventory.objects.digest { + return Err(LegacyImportError::SourceChanged); + } + Ok(copied) +} + +fn ensure_destination_empty(transaction: &Transaction<'_>) -> Result<(), LegacyImportError> { + let (records, events, request_keys): (i64, i64, i64) = transaction.query_row( + "SELECT (SELECT count(*) FROM records),(SELECT count(*) FROM events),(SELECT count(*) FROM request_keys)", + [], + |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)), + )?; + if records != 0 || events != 0 || request_keys != 0 { + return Err(LegacyImportError::DestinationNotNew); + } + Ok(()) +} + +fn copy_records( + source: &Transaction<'_>, + destination: &Transaction<'_>, +) -> Result { + let mut statement = + source.prepare("SELECT kind,id,payload,updated FROM records ORDER BY kind,id")?; + let mut rows = statement.query([])?; + let mut counts = RecordImportCounts::default(); + while let Some(row) = rows.next()? { + let kind: String = row.get(0)?; + let id: String = row.get(1)?; + let payload: String = row.get(2)?; + let updated: String = row.get(3)?; + if kind == "lease" { + validate_lease(&id, &payload)?; + let released = serde_json::json!({"deleted": true, "reason": "migration"}); + let released = crate::encode_payload(&released)?; + insert_event_payload(destination, "lease.migration_released", &id, &released)?; + counts.leases_released += 1; + counts.sanitized_records += 1; + continue; + } + let (imported_payload, sanitized, revoked, marked_lost) = + sanitize_record(&kind, &id, &payload)?; + let imported_updated = if sanitized { + Timestamp::now() + .to_rfc3339() + .map_err(|error| LegacyImportError::Timestamp(error.to_string()))? + } else { + updated + }; + destination.execute( + "INSERT INTO records(kind,id,payload,updated) VALUES(?1,?2,?3,?4)", + (&kind, &id, &imported_payload, &imported_updated), + )?; + if sanitized { + insert_event_payload(destination, SANITIZE_EVENT, &id, &imported_payload)?; + counts.sanitized_records += 1; + } + counts.records += 1; + counts.grants_revoked += u64::from(revoked); + counts.executions_marked_lost += u64::from(marked_lost); + } + Ok(counts) +} + +fn validate_lease(id: &str, payload: &str) -> Result<(), LegacyImportError> { + let value = crate::parse_payload(payload)?; + let object = value + .as_value() + .as_object() + .ok_or_else(|| LegacyImportError::InvalidRecord("lease is not an object".to_owned()))?; + let owner = object + .get("owner") + .and_then(Value::as_str) + .ok_or_else(|| LegacyImportError::InvalidRecord("lease owner is missing".to_owned()))?; + let resource = object + .get("resource") + .and_then(Value::as_str) + .ok_or_else(|| LegacyImportError::InvalidRecord("lease resource is missing".to_owned()))?; + if Id::parse(owner).is_err() || resource != id { + return Err(LegacyImportError::InvalidRecord( + "lease owner or resource key does not match".to_owned(), + )); + } + Ok(()) +} + +fn sanitize_record( + kind: &str, + id: &str, + payload: &str, +) -> Result<(String, bool, bool, bool), LegacyImportError> { + if !matches!(kind, "grant" | "task" | "workflow") { + return Ok((payload.to_owned(), false, false, false)); + } + let mut value = crate::parse_payload(payload)?; + let object = value + .as_value_mut() + .as_object_mut() + .ok_or_else(|| LegacyImportError::InvalidRecord(format!("{kind} {id} is not an object")))?; + let embedded_id = object + .get("id") + .and_then(Value::as_str) + .ok_or_else(|| LegacyImportError::InvalidRecord(format!("{kind} {id} has no string ID")))?; + if embedded_id != id { + return Err(LegacyImportError::InvalidRecord(format!( + "{kind} {id} has mismatched embedded ID {embedded_id}" + ))); + } + + let mut revoked = false; + let mut marked_lost = false; + if kind == "grant" { + object.insert("revoked".to_owned(), Value::Bool(true)); + revoked = true; + } else { + let status = object + .get("status") + .and_then(Value::as_str) + .unwrap_or_default(); + if !is_terminal_status(status) && status != "CONFLICT" { + object.insert("status".to_owned(), Value::String("LOST".to_owned())); + object.insert("error".to_owned(), Value::String(SANITIZE_ERROR.to_owned())); + marked_lost = true; + } + object.insert("pid".to_owned(), Value::Number(0.into())); + object.remove("socket"); + object.remove("process_identity"); + } + let encoded = crate::encode_payload(value.as_value())?; + Ok((encoded, true, revoked, marked_lost)) +} + +fn is_terminal_status(status: &str) -> bool { + matches!( + status, + "CANDIDATE_READY" + | "REVIEW_READY" + | "CHECKS_BLOCKED" + | "FAILED" + | "ERROR" + | "CANCELLED" + | "TIMED_OUT" + | "LOST" + ) +} + +fn copy_events( + source: &Transaction<'_>, + destination: &Transaction<'_>, +) -> Result<(), LegacyImportError> { + let mut statement = + source.prepare("SELECT seq,kind,ref,payload,at FROM events ORDER BY seq")?; + let mut rows = statement.query([])?; + while let Some(row) = rows.next()? { + let sequence: i64 = row.get(0)?; + let kind: String = row.get(1)?; + let reference: String = row.get(2)?; + let payload: String = row.get(3)?; + let at: String = row.get(4)?; + destination.execute( + "INSERT INTO events(seq,kind,ref,payload,at) VALUES(?1,?2,?3,?4,?5)", + (sequence, kind, reference, payload, at), + )?; + } + Ok(()) +} + +fn copy_request_keys( + source: &Transaction<'_>, + destination: &Transaction<'_>, +) -> Result<(), LegacyImportError> { + let mut statement = source.prepare("SELECT key,digest,ref FROM request_keys ORDER BY key")?; + let mut rows = statement.query([])?; + while let Some(row) = rows.next()? { + let key: String = row.get(0)?; + let digest: String = row.get(1)?; + let reference: String = row.get(2)?; + destination.execute( + "INSERT INTO request_keys(key,digest,ref) VALUES(?1,?2,?3)", + (key, digest, reference), + )?; + } + Ok(()) +} + +fn verify_receipt_destination( + source: &Transaction<'_>, + connection: &Connection, + root: &Path, + receipt: &LegacyImportReceipt, + source_objects: &[String], + inventory: &LegacyStateInventory, +) -> Result<(), LegacyImportError> { + if receipt.events != inventory.events.rows + || receipt.request_keys != inventory.request_keys.rows + || receipt.objects != inventory.objects.verified + { + return Err(LegacyImportError::InvalidReceipt); + } + let generated_events = verify_receipt_records(source, connection, receipt, inventory)?; + verify_receipt_events(source, connection, receipt, inventory, generated_events)?; + verify_receipt_request_keys(source, connection, receipt, inventory)?; + verify_receipt_record_count(connection, receipt)?; + verify_receipt_object_set(root, receipt, source_objects) +} + +fn verify_receipt_records( + source: &Transaction<'_>, + destination: &Connection, + receipt: &LegacyImportReceipt, + inventory: &LegacyStateInventory, +) -> Result, LegacyImportError> { + let mut expected_events = Vec::new(); + let mut counts = RecordImportCounts::default(); + if inventory.user_version != 0 { + let mut statement = + source.prepare("SELECT kind,id,payload,updated FROM records ORDER BY kind,id")?; + let mut rows = statement.query([])?; + while let Some(row) = rows.next()? { + let kind: String = row.get(0)?; + let id: String = row.get(1)?; + let payload: String = row.get(2)?; + let updated: String = row.get(3)?; + if kind == "lease" { + validate_lease(&id, &payload)?; + let event_payload = crate::encode_payload( + &serde_json::json!({"deleted": true, "reason": "migration"}), + )?; + expected_events.push(("lease.migration_released".to_owned(), id, event_payload)); + counts.leases_released += 1; + counts.sanitized_records += 1; + continue; + } + let (expected_payload, sanitized, revoked, marked_lost) = + sanitize_record(&kind, &id, &payload)?; + let actual: Option<(String, String)> = destination + .query_row( + "SELECT payload,updated FROM records WHERE kind=?1 AND id=?2", + (&kind, &id), + |row| Ok((row.get(0)?, row.get(1)?)), + ) + .optional()?; + let Some((actual_payload, actual_updated)) = actual else { + return Err(LegacyImportError::InvalidReceipt); + }; + if actual_payload != expected_payload + || (!sanitized && actual_updated != updated) + || (sanitized && actual_updated.is_empty()) + { + return Err(LegacyImportError::InvalidReceipt); + } + if sanitized { + expected_events.push((SANITIZE_EVENT.to_owned(), id, expected_payload)); + counts.sanitized_records += 1; + } + counts.records += 1; + counts.grants_revoked += u64::from(revoked); + counts.executions_marked_lost += u64::from(marked_lost); + } + } + if counts.records != receipt.records + || counts.grants_revoked != receipt.grants_revoked + || counts.leases_released != receipt.leases_released + || counts.executions_marked_lost != receipt.executions_marked_lost + || counts.sanitized_records != receipt.sanitized_records + { + return Err(LegacyImportError::InvalidReceipt); + } + Ok(expected_events) +} + +fn verify_receipt_events( + source: &Transaction<'_>, + destination: &Connection, + receipt: &LegacyImportReceipt, + inventory: &LegacyStateInventory, + generated_events: Vec<(String, String, String)>, +) -> Result<(), LegacyImportError> { + let mut expected_source = Vec::new(); + if inventory.user_version != 0 { + let mut statement = + source.prepare("SELECT seq,kind,ref,payload,at FROM events ORDER BY seq")?; + let mut rows = statement.query([])?; + while let Some(row) = rows.next()? { + expected_source.push(( + row.get::<_, i64>(0)?, + row.get::<_, String>(1)?, + row.get::<_, String>(2)?, + row.get::<_, String>(3)?, + row.get::<_, String>(4)?, + )); + } + } + let mut actual = Vec::new(); + let mut statement = + destination.prepare("SELECT seq,kind,ref,payload,at FROM events ORDER BY seq")?; + let mut rows = statement.query([])?; + while let Some(row) = rows.next()? { + actual.push(( + row.get::<_, i64>(0)?, + row.get::<_, String>(1)?, + row.get::<_, String>(2)?, + row.get::<_, String>(3)?, + row.get::<_, String>(4)?, + )); + } + let expected_tail_length = generated_events + .len() + .checked_add(1) + .ok_or(LegacyImportError::InvalidReceipt)?; + let expected_event_count = expected_source + .len() + .checked_add(expected_tail_length) + .ok_or(LegacyImportError::InvalidReceipt)?; + if actual.len() != expected_event_count + || actual.get(..expected_source.len()) != Some(expected_source.as_slice()) + { + return Err(LegacyImportError::InvalidReceipt); + } + let mut expected_tail = generated_events; + expected_tail.push(( + IMPORT_EVENT.to_owned(), + receipt.source_root_digest.clone(), + crate::encode_payload(receipt)?, + )); + let actual_tail = actual + .iter() + .skip(expected_source.len()) + .map(|(_, kind, reference, payload, at)| { + if at.is_empty() { + Err(LegacyImportError::InvalidReceipt) + } else { + Ok((kind.clone(), reference.clone(), payload.clone())) + } + }) + .collect::, _>>()?; + if actual_tail != expected_tail { + return Err(LegacyImportError::InvalidReceipt); + } + Ok(()) +} + +fn verify_receipt_request_keys( + source: &Transaction<'_>, + destination: &Connection, + receipt: &LegacyImportReceipt, + inventory: &LegacyStateInventory, +) -> Result<(), LegacyImportError> { + let mut expected = Vec::new(); + if inventory.user_version != 0 { + let mut statement = + source.prepare("SELECT key,digest,ref FROM request_keys ORDER BY key")?; + let mut rows = statement.query([])?; + while let Some(row) = rows.next()? { + expected.push(( + row.get::<_, String>(0)?, + row.get::<_, String>(1)?, + row.get::<_, String>(2)?, + )); + } + } + let mut actual = Vec::new(); + let mut statement = + destination.prepare("SELECT key,digest,ref FROM request_keys ORDER BY key")?; + let mut rows = statement.query([])?; + while let Some(row) = rows.next()? { + actual.push(( + row.get::<_, String>(0)?, + row.get::<_, String>(1)?, + row.get::<_, String>(2)?, + )); + } + if actual != expected || u64::try_from(actual.len()).unwrap_or(u64::MAX) != receipt.request_keys + { + return Err(LegacyImportError::InvalidReceipt); + } + Ok(()) +} + +fn verify_receipt_record_count( + connection: &Connection, + receipt: &LegacyImportReceipt, +) -> Result<(), LegacyImportError> { + let (actual_records, actual_events, actual_keys): (i64, i64, i64) = connection.query_row( + "SELECT (SELECT count(*) FROM records),(SELECT count(*) FROM events),(SELECT count(*) FROM request_keys)", + [], + |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)), + )?; + let expected_records = receipt + .records + .checked_add(1) + .and_then(|count| i64::try_from(count).ok()) + .ok_or(LegacyImportError::InvalidReceipt)?; + let expected_events = receipt + .events + .checked_add(receipt.sanitized_records) + .and_then(|count| count.checked_add(1)) + .and_then(|count| i64::try_from(count).ok()) + .ok_or(LegacyImportError::InvalidReceipt)?; + let expected_keys = + i64::try_from(receipt.request_keys).map_err(|_| LegacyImportError::InvalidReceipt)?; + if actual_records != expected_records + || actual_events != expected_events + || actual_keys != expected_keys + { + return Err(LegacyImportError::InvalidReceipt); + } + let expected_receipt = crate::encode_payload(receipt)?; + let actual_receipt: String = connection.query_row( + "SELECT payload FROM records WHERE kind=?1 AND id=?2", + (IMPORT_KIND, &receipt.source_root_digest), + |row| row.get(0), + )?; + if actual_receipt != expected_receipt { + return Err(LegacyImportError::InvalidReceipt); + } + Ok(()) +} + +fn verify_receipt_object_set( + root: &Path, + receipt: &LegacyImportReceipt, + source_objects: &[String], +) -> Result<(), LegacyImportError> { + let objects_path = root.join("objects"); + let object_count = match fs::symlink_metadata(&objects_path) { + Ok(metadata) if metadata.is_dir() && !metadata.file_type().is_symlink() => { + let objects = SafeDir::open(&objects_path)?; + let entries = fs::read_dir(&objects_path)?.collect::, _>>()?; + let mut names = Vec::with_capacity(entries.len()); + for entry in &entries { + let filename = entry.file_name(); + let name = filename.to_str().ok_or(LegacyImportError::InvalidReceipt)?; + names.push(name.to_owned()); + objects + .read_blob(name) + .map_err(|_| LegacyImportError::InvalidReceipt)?; + } + names.sort(); + if names != source_objects { + return Err(LegacyImportError::InvalidReceipt); + } + u64::try_from(entries.len()).map_err(|_| LegacyImportError::InvalidReceipt)? + } + Err(error) if error.kind() == io::ErrorKind::NotFound => 0, + _ => return Err(LegacyImportError::InvalidReceipt), + }; + if object_count != receipt.objects { + return Err(LegacyImportError::InvalidReceipt); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::fs; + use std::path::{Path, PathBuf}; + use std::sync::atomic::{AtomicU64, Ordering}; + use std::time::{SystemTime, UNIX_EPOCH}; + + use rusqlite::Connection; + + use super::{LegacyImportError, LegacyImportOptions, LegacyStateInventory}; + + static TEST_SEQUENCE: AtomicU64 = AtomicU64::new(0); + + struct TestRoot(PathBuf); + + impl TestRoot { + fn new() -> Self { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("clock follows epoch") + .as_nanos(); + let path = std::env::temp_dir().join(format!( + "rover-legacy-import-{}-{timestamp}-{}", + std::process::id(), + TEST_SEQUENCE.fetch_add(1, Ordering::Relaxed) + )); + fs::create_dir(&path).expect("create parent"); + set_private(&path); + Self(path) + } + + fn source(&self) -> PathBuf { + let root = self.0.join("source"); + fs::create_dir(&root).expect("create source root"); + set_private(&root); + let database = Connection::open(root.join("rover.db")).expect("create database"); + database + .execute_batch( + "CREATE TABLE records (kind TEXT NOT NULL,id TEXT NOT NULL,payload TEXT NOT NULL,updated TEXT NOT NULL,PRIMARY KEY(kind,id));\ + CREATE TABLE events (seq INTEGER PRIMARY KEY AUTOINCREMENT,kind TEXT NOT NULL,ref TEXT NOT NULL,payload TEXT NOT NULL,at TEXT NOT NULL);\ + CREATE TABLE request_keys (key TEXT PRIMARY KEY,digest TEXT NOT NULL,ref TEXT NOT NULL);\ + PRAGMA user_version=1;\ + INSERT INTO records VALUES('thing','thing1','{\"name\":\"kept\"}','2026-01-01T00:00:00Z');\ + INSERT INTO records VALUES('grant','grant1','{\"id\":\"grant1\",\"revoked\":false}','2026-01-01T00:00:00Z');\ + INSERT INTO records VALUES('task','task1','{\"id\":\"task1\",\"status\":\"RUNNING\",\"pid\":55,\"socket\":\"/tmp/worker.sock\",\"process_identity\":\"worker-identity\"}','2026-01-01T00:00:00Z');\ + INSERT INTO records VALUES('workflow','flow1','{\"id\":\"flow1\",\"status\":\"RUNNING\",\"pid\":56,\"socket\":\"/tmp/flow.sock\",\"process_identity\":\"flow-identity\"}','2026-01-01T00:00:00Z');\ + INSERT INTO records VALUES('lease','resource1','{\"owner\":\"agent1\",\"resource\":\"resource1\"}','2026-01-01T00:00:00Z');\ + INSERT INTO events(kind,ref,payload,at) VALUES('thing.created','thing1','{\"name\":\"kept\"}','2026-01-01T00:00:00Z');\ + INSERT INTO request_keys VALUES('request1','request-digest','task1');", + ) + .expect("create Go v1 data"); + drop(database); + let objects = root.join("objects"); + fs::create_dir(&objects).expect("create object directory"); + set_private(&objects); + let object = b"retained migration object"; + let digest = rover_core::Sha256Digest::of(object).to_hex(); + fs::write(objects.join(digest), object).expect("write content addressed object"); + root + } + + fn snapshot(root: &Path) -> Vec<(String, Vec)> { + fn visit(root: &Path, prefix: &str, output: &mut Vec<(String, Vec)>) { + let mut entries = fs::read_dir(root) + .expect("read directory") + .map(|entry| entry.expect("directory entry")) + .collect::>(); + entries.sort_by_key(fs::DirEntry::file_name); + for entry in entries { + let name = entry.file_name().to_string_lossy().into_owned(); + let relative = if prefix.is_empty() { + name + } else { + format!("{prefix}/{name}") + }; + let metadata = fs::symlink_metadata(entry.path()).expect("stat entry"); + if metadata.is_dir() { + output.push((relative.clone(), b"directory".to_vec())); + visit(&entry.path(), &relative, output); + } else { + output.push((relative, fs::read(entry.path()).expect("read file"))); + } + } + } + let mut output = Vec::new(); + visit(root, "", &mut output); + output + } + } + + impl Drop for TestRoot { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.0); + } + } + + fn set_private(path: &Path) { + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt as _; + fs::set_permissions(path, fs::Permissions::from_mode(0o700)) + .expect("set private permissions"); + } + } + + fn options() -> LegacyImportOptions { + LegacyImportOptions { + source_quiesced: true, + accept_excluded_runtime_data: true, + } + } + + #[test] + fn import_copies_snapshot_sanitizes_runtime_state_and_is_idempotent() { + let parent = TestRoot::new(); + let source = parent.source(); + let destination = parent.0.join("destination"); + let before = TestRoot::snapshot(&source); + + let first = LegacyStateInventory::import_to(&source, &destination, options()) + .expect("import Go state"); + assert!(!first.already_imported); + assert_eq!(first.receipt.records, 4); + assert_eq!(first.receipt.events, 1); + assert_eq!(first.receipt.request_keys, 1); + assert_eq!(first.receipt.objects, 1); + assert_eq!(first.receipt.grants_revoked, 1); + assert_eq!(first.receipt.leases_released, 1); + assert_eq!(first.receipt.executions_marked_lost, 2); + assert_eq!(first.receipt.sanitized_records, 4); + assert_eq!( + TestRoot::snapshot(&source), + before, + "source tree was modified" + ); + + let connection = Connection::open(destination.join("rover.db")).expect("open result"); + let grant: String = connection + .query_row( + "SELECT payload FROM records WHERE kind='grant'", + [], + |row| row.get(0), + ) + .expect("read grant"); + assert_eq!( + serde_json::from_str::(&grant).unwrap()["revoked"], + true + ); + for (kind, id) in [("task", "task1"), ("workflow", "flow1")] { + let payload: String = connection + .query_row( + "SELECT payload FROM records WHERE kind=?1 AND id=?2", + (kind, id), + |row| row.get(0), + ) + .expect("read sanitized execution"); + let value: serde_json::Value = serde_json::from_str(&payload).unwrap(); + assert_eq!(value["status"], "LOST"); + assert_eq!(value["pid"], 0); + assert!(value.get("socket").is_none()); + assert!(value.get("process_identity").is_none()); + } + assert_eq!( + connection + .query_row( + "SELECT count(*) FROM records WHERE kind='lease'", + [], + |row| row.get::<_, i64>(0) + ) + .unwrap(), + 0 + ); + assert_eq!( + connection + .query_row( + "SELECT count(*) FROM events WHERE kind='thing.created'", + [], + |row| row.get::<_, i64>(0) + ) + .unwrap(), + 1 + ); + assert_eq!( + connection + .query_row( + "SELECT count(*) FROM request_keys WHERE key='request1'", + [], + |row| row.get::<_, i64>(0) + ) + .unwrap(), + 1 + ); + drop(connection); + let object_name = fs::read_dir(source.join("objects")) + .unwrap() + .next() + .unwrap() + .unwrap() + .file_name(); + assert_eq!( + fs::read(destination.join("objects").join(object_name)).unwrap(), + b"retained migration object" + ); + + let repeated = LegacyStateInventory::import_to(&source, &destination, options()) + .expect("repeat import"); + assert!(repeated.already_imported); + assert_eq!(repeated.receipt, first.receipt); + assert_eq!( + TestRoot::snapshot(&source), + before, + "repeat modified source" + ); + } + + #[test] + fn import_requires_quiescence_and_runtime_exclusion_acknowledgement() { + let parent = TestRoot::new(); + let source = parent.source(); + let destination = parent.0.join("destination"); + let not_quiesced = LegacyImportOptions { + source_quiesced: false, + accept_excluded_runtime_data: true, + }; + assert!(matches!( + LegacyStateInventory::import_to(&source, &destination, not_quiesced), + Err(LegacyImportError::SourceNotQuiesced) + )); + + fs::write(source.join("operator-note.txt"), b"retained in source").unwrap(); + let no_exclusion_ack = LegacyImportOptions { + source_quiesced: true, + accept_excluded_runtime_data: false, + }; + assert!(matches!( + LegacyStateInventory::import_to(&source, &destination, no_exclusion_ack), + Err(LegacyImportError::ExcludedData(_)) + )); + assert!(!destination.exists()); + } + + #[test] + fn repeat_import_rejects_same_count_record_changes_and_extra_objects() { + let parent = TestRoot::new(); + let source = parent.source(); + let destination = parent.0.join("destination"); + LegacyStateInventory::import_to(&source, &destination, options()).expect("initial import"); + + let connection = Connection::open(destination.join("rover.db")).expect("open result"); + connection + .execute( + "UPDATE records SET payload=?1 WHERE kind='thing' AND id='thing1'", + ["{\"name\":\"tampered\"}"], + ) + .expect("tamper with imported payload without changing row count"); + assert!(matches!( + LegacyStateInventory::import_to(&source, &destination, options()), + Err(LegacyImportError::InvalidReceipt) + )); + connection + .execute( + "UPDATE records SET payload=?1 WHERE kind='thing' AND id='thing1'", + ["{\"name\":\"kept\"}"], + ) + .expect("restore imported payload"); + connection + .execute( + "UPDATE events SET payload='{}' WHERE kind='thing.created'", + [], + ) + .expect("tamper with copied source event"); + assert!(matches!( + LegacyStateInventory::import_to(&source, &destination, options()), + Err(LegacyImportError::InvalidReceipt) + )); + connection + .execute( + "UPDATE events SET payload=?1 WHERE kind='thing.created'", + ["{\"name\":\"kept\"}"], + ) + .expect("restore copied event"); + connection + .execute( + "UPDATE request_keys SET digest='changed' WHERE key='request1'", + [], + ) + .expect("tamper with copied request key"); + assert!(matches!( + LegacyStateInventory::import_to(&source, &destination, options()), + Err(LegacyImportError::InvalidReceipt) + )); + connection + .execute( + "UPDATE request_keys SET digest='request-digest' WHERE key='request1'", + [], + ) + .expect("restore copied request key"); + drop(connection); + + crate::files::StateRoot::open(&destination) + .expect("open imported state root") + .put_blob(b"unexpected but valid content-addressed object") + .expect("add an independently valid object"); + assert!(matches!( + LegacyStateInventory::import_to(&source, &destination, options()), + Err(LegacyImportError::InvalidReceipt) + )); + } +} diff --git a/crates/rover-store/src/lib.rs b/crates/rover-store/src/lib.rs new file mode 100644 index 0000000..cb4ce80 --- /dev/null +++ b/crates/rover-store/src/lib.rs @@ -0,0 +1,1891 @@ +//! Rover's `SQLite` migrations and transactional local record APIs. +//! +//! This crate accepts caller-supplied connections. Filesystem state-root +//! admission, content-addressed blobs, and backup remain separate contracts. + +use std::error::Error; +use std::fmt; +use std::fmt::Write as _; +use std::io::{self, Write}; +use std::ops::Deref; +use std::sync::Mutex; +use std::time::Duration; + +use rover_core::{Id, Timestamp}; +use rusqlite::{Connection, OptionalExtension, Transaction, TransactionBehavior}; +use serde::de::DeserializeOwned; +use serde::Serialize; +use serde_json::Value; +use sha2::{Digest, Sha256}; + +pub mod budget; +#[cfg(unix)] +pub mod files; +#[cfg(unix)] +pub mod legacy; +pub mod resources; + +pub const SCHEMA_VERSION: i64 = 1; +const MIGRATION_NAME: &str = "0001_rover_v1_records_events_request_keys"; +const MIGRATION_SQL: &str = "CREATE TABLE records (kind TEXT NOT NULL,id TEXT NOT NULL,payload TEXT NOT NULL,updated TEXT NOT NULL,PRIMARY KEY(kind,id));\ +CREATE TABLE events (seq INTEGER PRIMARY KEY AUTOINCREMENT,kind TEXT NOT NULL,ref TEXT NOT NULL,payload TEXT NOT NULL,at TEXT NOT NULL);\ +CREATE TABLE request_keys (key TEXT PRIMARY KEY,digest TEXT NOT NULL,ref TEXT NOT NULL);"; +const LEDGER_SQL: &str = "CREATE TABLE schema_migrations (\ + version INTEGER PRIMARY KEY CHECK(version > 0),\ + name TEXT NOT NULL UNIQUE,\ + checksum TEXT NOT NULL CHECK(length(checksum) = 64),\ + applied_at TEXT NOT NULL\ +);"; + +const MAX_RECORD_BYTES: usize = 16 * 1024 * 1024; +const MAX_RECORD_DEPTH: usize = 10_000; +const JSON_STACK_SEGMENT_BYTES: usize = 32 * 1024 * 1024; + +/// A JSON record with iterative destruction, including records at Rover's +/// maximum nesting depth. It intentionally does not implement `Clone` or +/// recursive `Debug` formatting. +pub struct RecordValue(Value); + +impl RecordValue { + /// Wrap a JSON value so the store can safely handle its destruction even + /// when it is nested at Rover's 10,000-level limit. + #[must_use] + pub fn from_value(value: Value) -> Self { + Self(value) + } + + /// Borrow the underlying JSON value. + #[must_use] + pub const fn as_value(&self) -> &Value { + &self.0 + } + + /// Mutably borrow the wrapped JSON value. + pub fn as_value_mut(&mut self) -> &mut Value { + &mut self.0 + } + + /// Read a string nested under a sequence of object keys without exposing + /// the JSON implementation type to callers. + #[must_use] + pub fn string_at_path<'a>(&'a self, path: &[&str]) -> Option<&'a str> { + self.value_at_path(path)?.as_str() + } + + /// Read a signed integer nested under object keys. + #[must_use] + pub fn integer_at_path(&self, path: &[&str]) -> Option { + self.value_at_path(path)?.as_i64() + } + + /// Read a boolean nested under object keys. + #[must_use] + pub fn bool_at_path(&self, path: &[&str]) -> Option { + self.value_at_path(path)?.as_bool() + } + + /// Return the array length at an object path. + #[must_use] + pub fn array_len_at_path(&self, path: &[&str]) -> Option { + self.value_at_path(path)?.as_array().map(Vec::len) + } + + /// Borrow array elements at an object path without copying nested JSON values. + #[must_use] + pub fn array_values_at_path(&self, path: &[&str]) -> Option<&[Value]> { + self.value_at_path(path)?.as_array().map(Vec::as_slice) + } + + /// Return whether a JSON value exists at an object path, including null. + #[must_use] + pub fn contains_path(&self, path: &[&str]) -> bool { + self.value_at_path(path).is_some() + } +} + +impl RecordValue { + fn value_at_path<'a>(&'a self, path: &[&str]) -> Option<&'a Value> { + if path.is_empty() { + return None; + } + path.iter().try_fold(&self.0, |value, key| value.get(*key)) + } +} + +impl Deref for RecordValue { + type Target = Value; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Serialize for RecordValue { + fn serialize(&self, serializer: S) -> Result { + self.0.serialize(serializer) + } +} + +impl Drop for RecordValue { + fn drop(&mut self) { + let root = std::mem::replace(&mut self.0, Value::Null); + drop_value_iteratively(root); + } +} + +fn drop_value_iteratively(root: Value) { + let mut pending = vec![root]; + while let Some(value) = pending.pop() { + match value { + Value::Array(items) => pending.extend(items), + Value::Object(items) => pending.extend(items.into_values()), + Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => {} + } + } +} + +/// An error returned by the transactional record store. +#[derive(Debug)] +pub enum StoreError { + Sqlite(rusqlite::Error), + Migration(MigrationError), + Json(serde_json::Error), + Encoding(std::string::FromUtf8Error), + InvalidId(rover_core::InvalidId), + RecordTooLarge, + RecordTooDeep, + InvalidBound, + CollectionTooLarge, + NotFound, + IdempotencyConflict, + MutationRejected(String), + Timestamp(String), + Poisoned, +} + +impl fmt::Display for StoreError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Sqlite(error) => write!(formatter, "SQLite store failed: {error}"), + Self::Migration(error) => write!(formatter, "store migration failed: {error}"), + Self::Json(error) => write!(formatter, "record JSON failed: {error}"), + Self::Encoding(error) => write!( + formatter, + "record JSON encoding produced invalid UTF-8: {error}" + ), + Self::InvalidId(error) => write!(formatter, "invalid record key: {error}"), + Self::RecordTooLarge => formatter.write_str("record exceeds 16 MiB"), + Self::RecordTooDeep => { + formatter.write_str("record exceeds the 10000-level JSON nesting limit") + } + Self::InvalidBound => formatter.write_str("invalid explicit collection bound"), + Self::CollectionTooLarge => { + formatter.write_str("collection exceeds explicit bound; refusing partial answer") + } + Self::NotFound => formatter.write_str("record not found"), + Self::IdempotencyConflict => { + formatter.write_str("idempotency key reused with a different contract") + } + Self::MutationRejected(reason) => { + write!(formatter, "record mutation rejected: {reason}") + } + Self::Timestamp(error) => write!(formatter, "timestamp formatting failed: {error}"), + Self::Poisoned => formatter.write_str("SQLite store connection lock is poisoned"), + } + } +} + +impl Error for StoreError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::Sqlite(error) => Some(error), + Self::Migration(error) => Some(error), + Self::Json(error) => Some(error), + Self::Encoding(error) => Some(error), + Self::InvalidId(error) => Some(error), + _ => None, + } + } +} + +impl From for StoreError { + fn from(error: rusqlite::Error) -> Self { + Self::Sqlite(error) + } +} +impl From for StoreError { + fn from(error: MigrationError) -> Self { + Self::Migration(error) + } +} +impl From for StoreError { + fn from(error: serde_json::Error) -> Self { + Self::Json(error) + } +} +impl From for StoreError { + fn from(error: std::string::FromUtf8Error) -> Self { + Self::Encoding(error) + } +} +impl From for StoreError { + fn from(error: rover_core::InvalidId) -> Self { + Self::InvalidId(error) + } +} + +/// Transactional access to Rover records, events, and idempotency keys. +/// +/// The supplied connection is configured and migrated on construction. This +/// type does not open paths or establish filesystem permissions; those policies +/// belong to the state-root opener. +pub struct Store { + connection: Mutex, +} + +impl Store { + /// Configure and migrate an existing `SQLite` connection. + /// + /// # Errors + /// + /// Returns an error if pragmas cannot be applied or the schema is not supported. + pub fn from_connection(mut connection: Connection) -> Result { + connection.busy_timeout(Duration::from_secs(5))?; + connection.execute_batch( + "PRAGMA journal_mode=WAL; PRAGMA synchronous=FULL; PRAGMA foreign_keys=ON;", + )?; + migrate(&mut connection)?; + Ok(Self { + connection: Mutex::new(connection), + }) + } + + /// Insert or replace a record and optionally append the matching event atomically. + /// + /// # Errors + /// + /// Returns an error for invalid IDs, oversized or over-nested values, serialization failures, or `SQLite` errors. + pub fn put( + &self, + kind: &str, + id: &str, + value: &T, + event: &str, + ) -> Result<(), StoreError> { + let mut connection = self.lock()?; + let transaction = connection.transaction_with_behavior(TransactionBehavior::Immediate)?; + put_in_transaction(&transaction, kind, id, value, event)?; + transaction.commit()?; + Ok(()) + } + + /// Read a JSON record into the dynamic JSON representation. + /// + /// # Errors + /// + /// Returns [`StoreError::NotFound`] when the record is missing, or an error for invalid stored JSON or `SQLite` failures. + pub fn get(&self, kind: &str, id: &str) -> Result { + let connection = self.lock()?; + let payload = connection + .query_row( + "SELECT payload FROM records WHERE kind=?1 AND id=?2", + (kind, id), + |row| row.get::<_, String>(0), + ) + .optional()? + .ok_or(StoreError::NotFound)?; + parse_payload(&payload) + } + + /// Read and deserialize a record into a caller-selected Rust type. + /// + /// # Errors + /// + /// Returns [`StoreError::NotFound`] when missing, or an error when the stored JSON cannot be deserialized into `T`. + pub fn get_typed(&self, kind: &str, id: &str) -> Result { + let connection = self.lock()?; + let payload = connection + .query_row( + "SELECT payload FROM records WHERE kind=?1 AND id=?2", + (kind, id), + |row| row.get::<_, String>(0), + ) + .optional()? + .ok_or(StoreError::NotFound)?; + decode_payload(&payload) + } + + /// Return stored JSON text in update order without changing number tokens. + /// + /// # Errors + /// + /// Returns an error if the `SQLite` query fails. + pub fn list_raw(&self, kind: &str, limit: i64) -> Result, StoreError> { + let limit = if (1..=1000).contains(&limit) { + limit + } else { + 100 + }; + let connection = self.lock()?; + let mut statement = connection.prepare( + "SELECT payload FROM records WHERE kind=?1 ORDER BY updated DESC,id DESC LIMIT ?2", + )?; + let rows = statement.query_map((kind, limit), |row| row.get::<_, String>(0))?; + rows.collect::, _>>() + .map_err(StoreError::from) + } + + /// List up to `max` raw JSON payloads, refusing partial answers. + /// + /// # Errors + /// + /// Returns [`StoreError::InvalidBound`] outside 1..=100000, or [`StoreError::CollectionTooLarge`] if more records exist. + pub fn list_all_raw(&self, kind: &str, max: i64) -> Result, StoreError> { + if !(1..=100_000).contains(&max) { + return Err(StoreError::InvalidBound); + } + let connection = self.lock()?; + let mut statement = connection.prepare( + "SELECT payload FROM records WHERE kind=?1 ORDER BY updated DESC,id DESC LIMIT ?2", + )?; + let rows = statement.query_map((kind, max + 1), |row| row.get::<_, String>(0))?; + let payloads = rows.collect::, _>>()?; + if i64::try_from(payloads.len()).unwrap_or(i64::MAX) > max { + return Err(StoreError::CollectionTooLarge); + } + Ok(payloads) + } + + /// List records ordered by update time and descending ID. Invalid limits use the Go default of 100. + /// + /// # Errors + /// + /// Returns an error if stored JSON is invalid or the database query fails. + pub fn list(&self, kind: &str, limit: i64) -> Result, StoreError> { + let limit = if (1..=1000).contains(&limit) { + limit + } else { + 100 + }; + let connection = self.lock()?; + let mut statement = connection.prepare( + "SELECT payload FROM records WHERE kind=?1 ORDER BY updated DESC,id DESC LIMIT ?2", + )?; + let rows = statement.query_map((kind, limit), |row| row.get::<_, String>(0))?; + rows.map(|row| parse_payload(&row?)).collect() + } + + /// List at most `max` records, failing if more exist instead of returning a partial result. + /// + /// # Errors + /// + /// Returns [`StoreError::InvalidBound`] for a bound outside 1..=100000, [`StoreError::CollectionTooLarge`] if more records exist, or an error for invalid stored JSON or `SQLite` failures. + pub fn list_all(&self, kind: &str, max: i64) -> Result, StoreError> { + if !(1..=100_000).contains(&max) { + return Err(StoreError::InvalidBound); + } + let connection = self.lock()?; + let mut statement = connection.prepare( + "SELECT payload FROM records WHERE kind=?1 ORDER BY updated DESC,id DESC LIMIT ?2", + )?; + let rows = statement.query_map((kind, max + 1), |row| row.get::<_, String>(0))?; + let payloads = rows.collect::, _>>()?; + if i64::try_from(payloads.len()).unwrap_or(i64::MAX) > max { + return Err(StoreError::CollectionTooLarge); + } + payloads + .iter() + .map(|payload| parse_payload(payload)) + .collect() + } + + /// Read, transform, replace, and optionally append an event in one transaction. + /// The callback executes while the database lock is held and must not perform external effects. + /// + /// # Errors + /// + /// Returns [`StoreError::NotFound`] for a missing record, or an error if the callback, serialization, or `SQLite` operation fails. + pub fn mutate(&self, kind: &str, id: &str, event: &str, mutate: F) -> Result<(), StoreError> + where + F: FnOnce(&Value) -> Result, + { + self.mutate_with_event(kind, id, |old| { + mutate(old).map(|new| (new, (!event.is_empty()).then(|| event.to_owned()))) + }) + } + + /// Read and replace a record atomically while choosing whether to append + /// an event based on the value observed inside the transaction. + /// + /// This is useful when a state transition can lose a race after an + /// external observation: the callback can return the original value and + /// omit the event without exposing a check-then-write window. + /// + /// # Errors + /// + /// Returns [`StoreError::NotFound`] for a missing record, or an error from + /// the callback, serialization, or `SQLite` operation. + pub fn mutate_with_event(&self, kind: &str, id: &str, mutate: F) -> Result<(), StoreError> + where + F: FnOnce(&Value) -> Result<(Value, Option), StoreError>, + { + let mut connection = self.lock()?; + let transaction = connection.transaction_with_behavior(TransactionBehavior::Immediate)?; + let payload = transaction + .query_row( + "SELECT payload FROM records WHERE kind=?1 AND id=?2", + (kind, id), + |row| row.get::<_, String>(0), + ) + .optional()? + .ok_or(StoreError::NotFound)?; + let old_value = parse_payload(&payload)?; + let (new_value, event) = mutate(old_value.as_value())?; + let new_value = RecordValue::from_value(new_value); + put_in_transaction( + &transaction, + kind, + id, + new_value.as_value(), + event.as_deref().unwrap_or_default(), + )?; + transaction.commit()?; + Ok(()) + } + + /// Mutate using the stored JSON text directly, matching Rover's Go callback contract. + /// + /// The callback runs inside the immediate transaction while the store lock is held. + /// + /// # Errors + /// + /// Returns [`StoreError::NotFound`] if the record is missing, or an error from the callback, serialization, or `SQLite`. + pub fn mutate_raw( + &self, + kind: &str, + id: &str, + event: &str, + mutate: F, + ) -> Result<(), StoreError> + where + T: Serialize, + F: FnOnce(&str) -> Result, + { + let mut connection = self.lock()?; + let transaction = connection.transaction_with_behavior(TransactionBehavior::Immediate)?; + let payload = transaction + .query_row( + "SELECT payload FROM records WHERE kind=?1 AND id=?2", + (kind, id), + |row| row.get::<_, String>(0), + ) + .optional()? + .ok_or(StoreError::NotFound)?; + let value = mutate(&payload)?; + put_in_transaction(&transaction, kind, id, &value, event)?; + transaction.commit()?; + Ok(()) + } + + /// Atomically create a record, its `{kind}.created` event, and an optional idempotency key. + /// A matching key returns its original reference with `exists=true` and appends no event. + /// + /// # Errors + /// + /// Returns [`StoreError::IdempotencyConflict`] when a key is reused with another digest, or an error for invalid IDs, oversized values, serialization, or `SQLite` failures. + pub fn create_once( + &self, + kind: &str, + id: &str, + key: &str, + digest: &str, + value: &T, + ) -> Result<(String, bool), StoreError> { + let mut connection = self.lock()?; + let transaction = connection.transaction_with_behavior(TransactionBehavior::Immediate)?; + if !key.is_empty() { + let prior = transaction + .query_row( + "SELECT digest,ref FROM request_keys WHERE key=?1", + [key], + |row| Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?)), + ) + .optional()?; + if let Some((prior_digest, prior_ref)) = prior { + if prior_digest != digest { + return Err(StoreError::IdempotencyConflict); + } + transaction.commit()?; + return Ok((prior_ref, true)); + } + } + put_in_transaction(&transaction, kind, id, value, &format!("{kind}.created"))?; + if !key.is_empty() { + transaction.execute( + "INSERT INTO request_keys(key,digest,ref) VALUES(?1,?2,?3)", + (key, digest, id), + )?; + } + transaction.commit()?; + Ok((id.to_owned(), false)) + } + + /// Return up to 1000 event summaries for a record ID, in append order. + /// + /// # Errors + /// + /// Returns an error if the `SQLite` query fails. + pub fn events(&self, id: &str) -> Result, StoreError> { + let connection = self.lock()?; + let mut statement = connection + .prepare("SELECT seq,kind,ref,at FROM events WHERE ref=?1 ORDER BY seq LIMIT 1000")?; + let rows = statement.query_map([id], |row| { + Ok(( + row.get::<_, i64>(0)?, + row.get::<_, String>(1)?, + row.get::<_, String>(2)?, + row.get::<_, String>(3)?, + )) + })?; + rows.map(|row| { + let (sequence, event, reference, at) = row?; + Ok(serde_json::json!({"sequence": sequence.to_string(), "event": event, "ref": reference, "at": at})) + }).collect() + } + + /// Delete an existing record and optionally append a `{deleted:true}` event atomically. + /// + /// # Errors + /// + /// Returns [`StoreError::NotFound`] if no record exists, or an error if the event cannot be serialized or the `SQLite` transaction fails. + pub fn delete(&self, kind: &str, id: &str, event: &str) -> Result<(), StoreError> { + let mut connection = self.lock()?; + let transaction = connection.transaction_with_behavior(TransactionBehavior::Immediate)?; + let deleted = + transaction.execute("DELETE FROM records WHERE kind=?1 AND id=?2", (kind, id))?; + if deleted == 0 { + return Err(StoreError::NotFound); + } + if !event.is_empty() { + insert_event( + &transaction, + event, + id, + &serde_json::json!({"deleted": true}), + )?; + } + transaction.commit()?; + Ok(()) + } + + fn lock(&self) -> Result, StoreError> { + self.connection.lock().map_err(|_| StoreError::Poisoned) + } +} + +fn put_in_transaction( + transaction: &Transaction<'_>, + kind: &str, + id: &str, + value: &impl Serialize, + event: &str, +) -> Result<(), StoreError> { + Id::parse(kind.to_owned())?; + Id::parse(id.to_owned())?; + let payload = encode_payload(value)?; + let updated = Timestamp::now() + .to_rfc3339() + .map_err(|error| StoreError::Timestamp(error.to_string()))?; + transaction.execute("INSERT INTO records(kind,id,payload,updated) VALUES(?1,?2,?3,?4) ON CONFLICT(kind,id) DO UPDATE SET payload=excluded.payload,updated=excluded.updated", (kind, id, payload.as_str(), updated.as_str()))?; + if !event.is_empty() { + insert_event_payload(transaction, event, id, &payload)?; + } + Ok(()) +} + +fn insert_event( + transaction: &Transaction<'_>, + kind: &str, + id: &str, + value: &impl Serialize, +) -> Result<(), StoreError> { + let payload = encode_payload(value)?; + insert_event_payload(transaction, kind, id, &payload) +} + +fn insert_event_payload( + transaction: &Transaction<'_>, + kind: &str, + id: &str, + payload: &str, +) -> Result<(), StoreError> { + let at = Timestamp::now() + .to_rfc3339() + .map_err(|error| StoreError::Timestamp(error.to_string()))?; + transaction.execute( + "INSERT INTO events(kind,ref,payload,at) VALUES(?1,?2,?3,?4)", + (kind, id, payload, at.as_str()), + )?; + Ok(()) +} + +#[derive(Default)] +struct JsonTokenState { + in_string: bool, + escaped: bool, +} + +#[derive(Default)] +struct WriterFailure { + too_deep: bool, + too_large: bool, +} + +struct BoundedJsonWriter { + bytes: Vec, + nesting: usize, + token_state: JsonTokenState, + failure: WriterFailure, +} + +impl BoundedJsonWriter { + fn new() -> Self { + Self { + bytes: Vec::new(), + nesting: 0, + token_state: JsonTokenState::default(), + failure: WriterFailure::default(), + } + } +} + +impl Write for BoundedJsonWriter { + fn write(&mut self, buffer: &[u8]) -> io::Result { + for byte in buffer { + if self.token_state.in_string { + if self.token_state.escaped { + self.token_state.escaped = false; + } else if *byte == b'\\' { + self.token_state.escaped = true; + } else if *byte == b'"' { + self.token_state.in_string = false; + } + } else { + match byte { + b'"' => self.token_state.in_string = true, + b'{' | b'[' => { + self.nesting += 1; + if self.nesting > MAX_RECORD_DEPTH { + self.failure.too_deep = true; + return Err(io::Error::other("JSON nesting limit exceeded")); + } + } + b'}' | b']' => self.nesting = self.nesting.saturating_sub(1), + _ => {} + } + } + } + if buffer.len() > MAX_RECORD_BYTES.saturating_sub(self.bytes.len()) { + self.failure.too_large = true; + return Err(io::Error::other("JSON byte limit exceeded")); + } + self.bytes.extend_from_slice(buffer); + Ok(buffer.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +fn encode_payload(value: &impl Serialize) -> Result { + let mut writer = BoundedJsonWriter::new(); + let serialized = stacker::grow(JSON_STACK_SEGMENT_BYTES, || { + let mut serializer = serde_json::Serializer::new(&mut writer); + value.serialize(serde_stacker::Serializer::new(&mut serializer)) + }); + if let Err(error) = serialized { + if writer.failure.too_deep { + return Err(StoreError::RecordTooDeep); + } + if writer.failure.too_large { + return Err(StoreError::RecordTooLarge); + } + return Err(StoreError::Json(error)); + } + let raw = String::from_utf8(writer.bytes)?; + let mut escaped = String::with_capacity(raw.len().min(MAX_RECORD_BYTES)); + for character in raw.chars() { + match character { + '&' => escaped.push_str("\\u0026"), + '<' => escaped.push_str("\\u003c"), + '>' => escaped.push_str("\\u003e"), + '\u{2028}' => escaped.push_str("\\u2028"), + '\u{2029}' => escaped.push_str("\\u2029"), + _ => escaped.push(character), + } + if escaped.len() > MAX_RECORD_BYTES { + return Err(StoreError::RecordTooLarge); + } + } + Ok(escaped) +} + +fn json_depth(payload: &str) -> Result<(), StoreError> { + let mut nesting = 0_usize; + let mut in_string = false; + let mut escaped = false; + for byte in payload.bytes() { + if in_string { + if escaped { + escaped = false; + } else if byte == b'\\' { + escaped = true; + } else if byte == b'"' { + in_string = false; + } + } else { + match byte { + b'"' => in_string = true, + b'{' | b'[' => { + nesting += 1; + if nesting > MAX_RECORD_DEPTH { + return Err(StoreError::RecordTooDeep); + } + } + b'}' | b']' => nesting = nesting.saturating_sub(1), + _ => {} + } + } + } + Ok(()) +} + +fn decode_payload(payload: &str) -> Result { + if payload.len() > MAX_RECORD_BYTES { + return Err(StoreError::RecordTooLarge); + } + json_depth(payload)?; + let mut deserializer = serde_json::Deserializer::from_str(payload); + deserializer.disable_recursion_limit(); + let value = T::deserialize(serde_stacker::Deserializer::new(&mut deserializer))?; + deserializer.end()?; + Ok(value) +} + +fn parse_payload(payload: &str) -> Result { + decode_payload(payload).map(RecordValue::from_value) +} + +/// An unsupported or internally inconsistent Rover database schema. +#[derive(Debug)] +pub enum MigrationError { + Sqlite(rusqlite::Error), + UnsupportedVersion(i64), + UnversionedDatabaseHasObjects, + LegacySchemaMismatch(String), + LedgerMismatch, +} + +impl fmt::Display for MigrationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Sqlite(error) => write!(f, "SQLite migration failed: {error}"), + Self::UnsupportedVersion(version) => { + write!(f, "database schema version {version} is unsupported") + } + Self::UnversionedDatabaseHasObjects => { + f.write_str("unversioned database already contains user schema objects") + } + Self::LegacySchemaMismatch(reason) => { + write!(f, "legacy schema does not match Rover v1: {reason}") + } + Self::LedgerMismatch => f.write_str("schema migration ledger does not match Rover v1"), + } + } +} + +impl Error for MigrationError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::Sqlite(error) => Some(error), + _ => None, + } + } +} + +impl From for MigrationError { + fn from(error: rusqlite::Error) -> Self { + Self::Sqlite(error) + } +} + +/// Apply any pending supported migrations and validate the resulting ledger. +/// +/// This function does not configure `SQLite` pragmas or validate filesystem +/// paths; the eventual state-root opener owns those policies. +/// +/// # Errors +/// +/// Returns an error for an unsupported version, an incompatible schema, a +/// corrupted migration ledger, or a `SQLite` operation failure. +pub fn migrate(connection: &mut Connection) -> Result<(), MigrationError> { + let version: i64 = connection.pragma_query_value(None, "user_version", |row| row.get(0))?; + match version { + 0 => migrate_empty_database(connection), + SCHEMA_VERSION => adopt_or_validate_v1(connection), + future => Err(MigrationError::UnsupportedVersion(future)), + } +} + +fn migrate_empty_database(connection: &mut Connection) -> Result<(), MigrationError> { + migrate_empty_database_with(connection, insert_ledger_entry) +} + +fn migrate_empty_database_with( + connection: &mut Connection, + after_schema: impl FnOnce(&Connection) -> Result<(), MigrationError>, +) -> Result<(), MigrationError> { + if has_user_schema_objects(connection)? { + return Err(MigrationError::UnversionedDatabaseHasObjects); + } + + let transaction = connection.transaction_with_behavior(TransactionBehavior::Immediate)?; + transaction.execute_batch(MIGRATION_SQL)?; + create_ledger(&transaction)?; + after_schema(&transaction)?; + transaction.pragma_update(None, "user_version", SCHEMA_VERSION)?; + transaction.commit()?; + validate_v1(connection) +} + +fn adopt_or_validate_v1(connection: &mut Connection) -> Result<(), MigrationError> { + let ledger_exists = connection + .query_row( + "SELECT 1 FROM sqlite_master WHERE type='table' AND name='schema_migrations'", + [], + |_| Ok(()), + ) + .optional()? + .is_some(); + + validate_v1_tables(connection, ledger_exists)?; + if ledger_exists { + validate_ledger(connection)?; + return Ok(()); + } + + let transaction = connection.transaction_with_behavior(TransactionBehavior::Immediate)?; + create_ledger(&transaction)?; + insert_ledger_entry(&transaction)?; + transaction.commit()?; + validate_v1(connection) +} + +fn create_ledger(connection: &Connection) -> Result<(), MigrationError> { + connection.execute_batch(LEDGER_SQL)?; + Ok(()) +} + +fn insert_ledger_entry(connection: &Connection) -> Result<(), MigrationError> { + connection.execute( + "INSERT INTO schema_migrations(version,name,checksum,applied_at) \ + VALUES(?1,?2,?3,strftime('%Y-%m-%dT%H:%M:%fZ','now'))", + (SCHEMA_VERSION, MIGRATION_NAME, migration_checksum()), + )?; + Ok(()) +} + +fn validate_v1(connection: &Connection) -> Result<(), MigrationError> { + let version: i64 = connection.pragma_query_value(None, "user_version", |row| row.get(0))?; + if version != SCHEMA_VERSION { + return Err(MigrationError::UnsupportedVersion(version)); + } + validate_v1_tables(connection, true)?; + validate_ledger(connection) +} + +fn validate_v1_tables( + connection: &Connection, + includes_ledger: bool, +) -> Result<(), MigrationError> { + let mut expected = vec!["events", "records", "request_keys"]; + if includes_ledger { + expected.push("schema_migrations"); + } + expected.sort_unstable(); + + let mut statement = connection.prepare( + "SELECT name FROM sqlite_master \ + WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name", + )?; + let actual: Vec = statement + .query_map([], |row| row.get(0))? + .collect::>()?; + if actual != expected { + return Err(MigrationError::LegacySchemaMismatch( + "unexpected table set".to_string(), + )); + } + + validate_columns( + connection, + "records", + &[ + ("kind", "TEXT", 1, 1), + ("id", "TEXT", 1, 2), + ("payload", "TEXT", 1, 0), + ("updated", "TEXT", 1, 0), + ], + )?; + validate_columns( + connection, + "events", + &[ + ("seq", "INTEGER", 0, 1), + ("kind", "TEXT", 1, 0), + ("ref", "TEXT", 1, 0), + ("payload", "TEXT", 1, 0), + ("at", "TEXT", 1, 0), + ], + )?; + validate_columns( + connection, + "request_keys", + &[ + ("key", "TEXT", 0, 1), + ("digest", "TEXT", 1, 0), + ("ref", "TEXT", 1, 0), + ], + )?; + if includes_ledger { + validate_columns( + connection, + "schema_migrations", + &[ + ("version", "INTEGER", 0, 1), + ("name", "TEXT", 1, 0), + ("checksum", "TEXT", 1, 0), + ("applied_at", "TEXT", 1, 0), + ], + )?; + let ledger_sql: String = connection.query_row( + "SELECT sql FROM sqlite_master WHERE type='table' AND name='schema_migrations'", + [], + |row| row.get(0), + )?; + if normalize_sql(&ledger_sql) != normalize_sql(LEDGER_SQL.trim_end_matches(';')) { + return Err(MigrationError::LegacySchemaMismatch( + "migration ledger constraints mismatch".to_string(), + )); + } + } + + let events_sql: String = connection.query_row( + "SELECT sql FROM sqlite_master WHERE type='table' AND name='events'", + [], + |row| row.get(0), + )?; + if !events_sql.to_ascii_uppercase().contains("AUTOINCREMENT") { + return Err(MigrationError::LegacySchemaMismatch( + "events sequence is not AUTOINCREMENT".to_string(), + )); + } + + let unexpected_objects: i64 = connection.query_row( + "SELECT count(*) FROM sqlite_master \ + WHERE type IN ('index','trigger','view') AND name NOT LIKE 'sqlite_%'", + [], + |row| row.get(0), + )?; + if unexpected_objects != 0 { + return Err(MigrationError::LegacySchemaMismatch( + "unexpected index, trigger, or view".to_string(), + )); + } + Ok(()) +} + +fn validate_columns( + connection: &Connection, + table: &str, + expected: &[(&str, &str, i64, i64)], +) -> Result<(), MigrationError> { + let sql = format!("PRAGMA table_info({table})"); + let mut statement = connection.prepare(&sql)?; + let actual: Vec<(String, String, i64, i64)> = statement + .query_map([], |row| { + Ok((row.get(1)?, row.get(2)?, row.get(3)?, row.get(5)?)) + })? + .collect::>()?; + let expected: Vec<(String, String, i64, i64)> = expected + .iter() + .map(|(name, kind, not_null, primary_key)| { + ( + (*name).to_string(), + (*kind).to_string(), + *not_null, + *primary_key, + ) + }) + .collect(); + if actual != expected { + return Err(MigrationError::LegacySchemaMismatch(format!( + "column definition mismatch in {table}: actual={actual:?}, expected={expected:?}" + ))); + } + Ok(()) +} + +fn validate_ledger(connection: &Connection) -> Result<(), MigrationError> { + let rows: Vec<(i64, String, String, String)> = { + let mut statement = connection.prepare( + "SELECT version,name,checksum,applied_at FROM schema_migrations ORDER BY version", + )?; + let rows = statement + .query_map([], |row| { + Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)) + })? + .collect::>()?; + rows + }; + if rows.len() != 1 + || rows[0].0 != SCHEMA_VERSION + || rows[0].1 != MIGRATION_NAME + || rows[0].2 != migration_checksum() + || !looks_like_utc_timestamp(&rows[0].3) + { + return Err(MigrationError::LedgerMismatch); + } + Ok(()) +} + +fn normalize_sql(sql: &str) -> String { + sql.split_whitespace() + .collect::>() + .join(" ") + .to_ascii_uppercase() + .replace(" (", "(") + .replace(" )", ")") + .replace(" = ", "=") + .replace(" > ", ">") + .replace(", ", ",") +} + +fn has_user_schema_objects(connection: &Connection) -> Result { + let count: i64 = connection.query_row( + "SELECT count(*) FROM sqlite_master \ + WHERE name NOT LIKE 'sqlite_%' AND type IN ('table','index','trigger','view')", + [], + |row| row.get(0), + )?; + Ok(count != 0) +} + +fn migration_checksum() -> String { + let digest = Sha256::digest(MIGRATION_SQL.as_bytes()); + let mut output = String::with_capacity(64); + for byte in digest { + write!(&mut output, "{byte:02x}").expect("writing to String cannot fail"); + } + output +} + +fn looks_like_utc_timestamp(value: &str) -> bool { + let bytes = value.as_bytes(); + bytes.len() == 24 + && bytes[..4].iter().all(u8::is_ascii_digit) + && bytes[4] == b'-' + && bytes[5..7].iter().all(u8::is_ascii_digit) + && bytes[7] == b'-' + && bytes[8..10].iter().all(u8::is_ascii_digit) + && bytes[10] == b'T' + && bytes[11..13].iter().all(u8::is_ascii_digit) + && bytes[13] == b':' + && bytes[14..16].iter().all(u8::is_ascii_digit) + && bytes[16] == b':' + && bytes[17..19].iter().all(u8::is_ascii_digit) + && bytes[19] == b'.' + && bytes[20..23].iter().all(u8::is_ascii_digit) + && bytes[23] == b'Z' +} + +#[cfg(test)] +mod tests { + use super::*; + use rusqlite::params; + use std::fs; + use std::sync::atomic::{AtomicU64, Ordering}; + use std::sync::{Arc, Barrier}; + use std::thread; + + static TEMP_SEQUENCE: AtomicU64 = AtomicU64::new(0); + + struct TempDb(std::path::PathBuf); + impl TempDb { + fn new() -> Self { + let unique = TEMP_SEQUENCE.fetch_add(1, Ordering::Relaxed); + Self( + std::env::temp_dir() + .join(format!("rover-store-{}-{unique}.db", std::process::id())), + ) + } + fn store(&self) -> Store { + Store::from_connection(Connection::open(&self.0).expect("open temp DB")) + .expect("configure and migrate") + } + } + impl Drop for TempDb { + fn drop(&mut self) { + let _ = fs::remove_file(&self.0); + let _ = fs::remove_file(self.0.with_extension("db-wal")); + let _ = fs::remove_file(self.0.with_extension("db-shm")); + } + } + + fn in_memory_store() -> Store { + Store::from_connection(Connection::open_in_memory().expect("open memory DB")) + .expect("configure and migrate") + } + + #[test] + fn record_value_reads_nested_string_fields_without_json_type_exposure() { + let record = RecordValue::from_value(serde_json::json!({ + "id": "task-1", + "contract": {"objective": "inspect task"}, + "process": {"exit_code": 7, "timed_out": false}, + "attempts": [{"number": 1}, {"number": 2}] + })); + assert_eq!(record.string_at_path(&["id"]), Some("task-1")); + assert_eq!( + record.string_at_path(&["contract", "objective"]), + Some("inspect task") + ); + assert_eq!(record.string_at_path(&["contract", "missing"]), None); + assert_eq!(record.string_at_path(&[]), None); + assert_eq!(record.integer_at_path(&["process", "exit_code"]), Some(7)); + assert_eq!(record.bool_at_path(&["process", "timed_out"]), Some(false)); + assert_eq!(record.array_len_at_path(&["attempts"]), Some(2)); + assert_eq!( + record.array_values_at_path(&["attempts"]).map(<[_]>::len), + Some(2) + ); + assert!(record.contains_path(&["process", "timed_out"])); + assert!(!record.contains_path(&["process", "missing"])); + } + + #[test] + fn put_get_list_events_and_delete_match_record_contract() { + let store = in_memory_store(); + let value = serde_json::json!({"text": "<>&\u{2028}\u{2029}", "n": 3}); + store + .put("task", "one", &value, "task.created") + .expect("put"); + let stored = store.get("task", "one").expect("get"); + assert_eq!(stored.as_value(), &value); + let listed = store.list("task", 10).expect("list"); + assert_eq!(listed.len(), 1); + assert_eq!(listed[0].as_value(), &value); + let events = store.events("one").expect("events"); + assert_eq!(events.len(), 1); + assert_eq!(events[0]["sequence"], "1"); + assert_eq!(events[0]["event"], "task.created"); + assert!(events[0].get("payload").is_none()); + + store.delete("task", "one", "task.deleted").expect("delete"); + assert!(matches!( + store.get("task", "one"), + Err(StoreError::NotFound) + )); + let events = store.events("one").expect("events after delete"); + assert_eq!(events.len(), 2); + assert_eq!(events[1]["event"], "task.deleted"); + assert!(matches!( + store.delete("task", "one", ""), + Err(StoreError::NotFound) + )); + } + + #[test] + fn event_summaries_stop_at_the_go_limit_of_one_thousand() { + let store = in_memory_store(); + let mut connection = store.lock().expect("connection"); + let transaction = connection + .transaction_with_behavior(TransactionBehavior::Immediate) + .expect("transaction"); + for _ in 0..1001 { + transaction + .execute( + "INSERT INTO events(kind,ref,payload,at) VALUES('changed','many','{}','2026-09-25T00:00:00Z')", + [], + ) + .expect("event insert"); + } + transaction.commit().expect("commit events"); + drop(connection); + let summaries = store.events("many").expect("bounded event summaries"); + assert_eq!(summaries.len(), 1000); + assert_eq!(summaries[0]["sequence"], "1"); + assert_eq!(summaries[999]["sequence"], "1000"); + } + + #[test] + fn put_serializes_once_and_uses_identical_record_and_event_payloads() { + struct ChangingValue(AtomicU64); + impl Serialize for ChangingValue { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_u64(self.0.fetch_add(1, Ordering::Relaxed)) + } + } + + let store = in_memory_store(); + let value = ChangingValue(AtomicU64::new(0)); + store + .put("task", "one", &value, "task.created") + .expect("put"); + let connection = store.lock().expect("connection"); + let (record_payload, event_payload): (String, String) = connection + .query_row( + "SELECT records.payload,events.payload FROM records JOIN events ON events.ref=records.id WHERE records.id='one'", + [], + |row| Ok((row.get(0)?, row.get(1)?)), + ) + .expect("record and event payloads"); + assert_eq!(record_payload, "0"); + assert_eq!(event_payload, record_payload); + assert_eq!(value.0.load(Ordering::Relaxed), 1); + } + + #[test] + fn mutate_is_atomic_on_callback_error_and_success() { + let store = in_memory_store(); + store + .put("task", "one", &serde_json::json!({"count": 1}), "") + .expect("initial put"); + let error = store.mutate("task", "one", "task.changed", |_| { + Err(StoreError::InvalidBound) + }); + assert!(matches!(error, Err(StoreError::InvalidBound))); + assert_eq!( + store.get("task", "one").expect("unchanged record")["count"], + 1 + ); + assert!(store.events("one").expect("no partial event").is_empty()); + + store + .mutate("task", "one", "task.changed", |old| { + Ok(serde_json::json!({"count": old["count"].as_i64().unwrap() + 1})) + }) + .expect("mutate"); + assert_eq!( + store.get("task", "one").expect("updated record")["count"], + 2 + ); + assert_eq!(store.events("one").expect("event").len(), 1); + } + + #[test] + fn concurrent_mutations_do_not_lose_updates() { + let store = Arc::new(in_memory_store()); + store + .put("counter", "shared", &serde_json::json!({"count":0}), "") + .expect("initial counter"); + let workers = (0..8) + .map(|_| { + let store = Arc::clone(&store); + thread::spawn(move || { + for _ in 0..25 { + store + .mutate("counter", "shared", "", |old| { + Ok(serde_json::json!({"count": old["count"].as_i64().unwrap() + 1})) + }) + .expect("atomic increment"); + } + }) + }) + .collect::>(); + for worker in workers { + worker.join().expect("mutation worker"); + } + assert_eq!( + store.get("counter", "shared").expect("counter read")["count"], + 200 + ); + } + + #[test] + fn panic_during_mutation_rolls_back_and_reopens_cleanly() { + let database = TempDb::new(); + { + let store = database.store(); + store + .put("task", "one", &serde_json::json!({"count":1}), "") + .expect("initial record"); + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let _ = store.mutate( + "task", + "one", + "task.changed", + |_| -> Result<_, StoreError> { panic!("injected mutation panic") }, + ); + })); + assert!(result.is_err()); + assert!(matches!( + store.get("task", "one"), + Err(StoreError::Poisoned) + )); + } + let reopened = database.store(); + assert_eq!( + reopened.get("task", "one").expect("record after recovery")["count"], + 1 + ); + assert!(reopened + .events("one") + .expect("no uncommitted event") + .is_empty()); + } + + #[test] + fn malformed_stored_json_is_reported_without_repair() { + let store = in_memory_store(); + store + .lock() + .expect("connection") + .execute( + "INSERT INTO records(kind,id,payload,updated) VALUES('task','bad','{','2026-09-25T00:00:00Z')", + [], + ) + .expect("inject malformed legacy payload"); + assert!(matches!(store.get("task", "bad"), Err(StoreError::Json(_)))); + let raw: String = store + .lock() + .expect("connection") + .query_row("SELECT payload FROM records WHERE id='bad'", [], |row| { + row.get(0) + }) + .expect("payload remains untouched"); + assert_eq!(raw, "{"); + } + + #[test] + fn create_once_rejects_changed_digest_without_partial_write_and_empty_key_skips_key_table() { + let store = in_memory_store(); + assert_eq!( + store + .create_once( + "task", + "first", + "key", + "digest-a", + &serde_json::json!({"x":1}) + ) + .expect("first create"), + ("first".to_owned(), false) + ); + assert_eq!( + store + .create_once( + "task", + "second", + "key", + "digest-a", + &serde_json::json!({"x":2}) + ) + .expect("repeat create"), + ("first".to_owned(), true) + ); + assert!(matches!( + store.create_once("task", "third", "key", "digest-b", &serde_json::json!({})), + Err(StoreError::IdempotencyConflict) + )); + assert!(matches!( + store.get("task", "third"), + Err(StoreError::NotFound) + )); + assert_eq!(store.events("first").expect("single event").len(), 1); + store + .create_once("task", "no-key", "", "ignored", &serde_json::json!({})) + .expect("create without idempotency"); + let connection = store.lock().expect("connection lock"); + let keys: i64 = connection + .query_row("SELECT count(*) FROM request_keys", [], |row| row.get(0)) + .expect("key count"); + assert_eq!(keys, 1); + } + + #[test] + fn typed_reads_and_raw_mutation_preserve_caller_selected_number_semantics() { + let store = in_memory_store(); + let exact = u64::MAX; + store + .put("number", "large", &exact, "") + .expect("put unsigned integer"); + let decoded: u64 = store.get_typed("number", "large").expect("typed read"); + assert_eq!(decoded, exact); + assert_eq!( + store.list_raw("number", 10).expect("raw list"), + vec![u64::MAX.to_string()] + ); + assert_eq!( + store.list_all_raw("number", 10).expect("raw all list"), + vec![u64::MAX.to_string()] + ); + + store + .mutate_raw::("number", "large", "number.changed", |payload| { + let old: u64 = serde_json::from_str(payload)?; + Ok(old - 1) + }) + .expect("raw typed mutation"); + let updated: u64 = store + .get_typed("number", "large") + .expect("updated typed read"); + assert_eq!(updated, exact - 1); + } + + #[test] + fn create_once_is_idempotent_across_independent_connections() { + let database = TempDb::new(); + let first = Arc::new(database.store()); + let second = Arc::new(database.store()); + let barrier = Arc::new(Barrier::new(3)); + let workers = [first, second] + .into_iter() + .enumerate() + .map(|(index, store)| { + let barrier = Arc::clone(&barrier); + thread::spawn(move || { + barrier.wait(); + store + .create_once( + "task", + &format!("id-{index}"), + "shared-key", + "same-digest", + &serde_json::json!({"index":index}), + ) + .expect("create once") + }) + }) + .collect::>(); + barrier.wait(); + let results = workers + .into_iter() + .map(|worker| worker.join().expect("worker")) + .collect::>(); + assert_eq!(results[0].0, results[1].0); + assert_eq!(results.iter().filter(|(_, exists)| !exists).count(), 1); + assert_eq!( + database + .store() + .events(&results[0].0) + .expect("one durable event") + .len(), + 1 + ); + } + + #[test] + fn record_event_and_idempotency_key_roll_back_as_one_unit() { + let store = in_memory_store(); + store + .lock() + .expect("connection") + .execute_batch( + "CREATE TRIGGER reject_event BEFORE INSERT ON events BEGIN SELECT RAISE(ABORT,'injected event failure'); END;", + ) + .expect("install event failure trigger"); + assert!(matches!( + store.create_once( + "task", + "atomic", + "atomic-key", + "digest", + &serde_json::json!({}) + ), + Err(StoreError::Sqlite(_)) + )); + store + .lock() + .expect("connection") + .execute_batch("DROP TRIGGER reject_event;") + .expect("remove failure trigger"); + assert!(matches!( + store.get("task", "atomic"), + Err(StoreError::NotFound) + )); + let connection = store.lock().expect("connection"); + let (event_count, key_count): (i64, i64) = connection + .query_row( + "SELECT (SELECT count(*) FROM events),(SELECT count(*) FROM request_keys)", + [], + |row| Ok((row.get(0)?, row.get(1)?)), + ) + .expect("read rolled back rows"); + assert_eq!((event_count, key_count), (0, 0)); + drop(connection); + + store + .put("task", "delete-atomic", &serde_json::json!({}), "") + .expect("record before delete"); + store + .lock() + .expect("connection") + .execute_batch( + "CREATE TRIGGER reject_delete_event BEFORE INSERT ON events BEGIN SELECT RAISE(ABORT,'injected delete event failure'); END;", + ) + .expect("install delete failure trigger"); + assert!(matches!( + store.delete("task", "delete-atomic", "task.deleted"), + Err(StoreError::Sqlite(_)) + )); + store + .lock() + .expect("connection") + .execute_batch("DROP TRIGGER reject_delete_event;") + .expect("remove delete failure trigger"); + assert!(store.get("task", "delete-atomic").is_ok()); + } + + #[test] + fn records_survive_connection_close_and_reopen() { + let database = TempDb::new(); + { + let store = database.store(); + store + .put( + "task", + "persisted", + &serde_json::json!({"ok":true}), + "task.created", + ) + .expect("persist"); + } + let reopened = database.store(); + assert_eq!( + reopened + .get("task", "persisted") + .expect("read after reopen")["ok"], + true + ); + assert_eq!( + reopened + .events("persisted") + .expect("event after reopen") + .len(), + 1 + ); + } + + #[test] + fn connection_pragmas_match_go_store_contract() { + let database = TempDb::new(); + let store = database.store(); + let connection = store.lock().expect("connection"); + let (journal_mode, synchronous, foreign_keys, busy_timeout): (String, i64, i64, i64) = + connection + .query_row( + "SELECT (SELECT * FROM pragma_journal_mode),(SELECT * FROM pragma_synchronous),(SELECT * FROM pragma_foreign_keys),(SELECT * FROM pragma_busy_timeout)", + [], + |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)), + ) + .expect("read configured pragmas"); + assert_eq!(journal_mode.to_ascii_lowercase(), "wal"); + assert_eq!((synchronous, foreign_keys, busy_timeout), (2, 1, 5000)); + } + + #[test] + fn payload_json_uses_go_escaping_and_enforces_encoded_size_limit() { + let encoded = encode_payload(&serde_json::json!("<>&\u{2028}\u{2029}")).expect("encode"); + assert!(encoded.contains("\\u003c\\u003e\\u0026\\u2028\\u2029")); + let object = serde_json::json!({"z": 1, "a": 2}); + assert_eq!( + encode_payload(&object).expect("sorted object keys"), + r#"{"a":2,"z":1}"# + ); + let too_large = serde_json::json!("x".repeat(MAX_RECORD_BYTES)); + assert!(matches!( + encode_payload(&too_large), + Err(StoreError::RecordTooLarge) + )); + + let make_nested = |levels: usize| { + let mut value = serde_json::Value::Null; + for _ in 0..levels { + value = serde_json::Value::Array(vec![value]); + } + RecordValue::from_value(value) + }; + let at_limit = make_nested(MAX_RECORD_DEPTH); + let encoded_at_limit = encode_payload(at_limit.as_value()).expect("Go maximum depth"); + let decoded_at_limit = parse_payload(&encoded_at_limit).expect("parse Go maximum depth"); + let mut leaf = decoded_at_limit.as_value(); + for _ in 0..MAX_RECORD_DEPTH { + leaf = &leaf[0]; + } + assert!(leaf.is_null()); + + let mut nested_object_value = serde_json::Value::Null; + for _ in 0..MAX_RECORD_DEPTH { + let mut object = serde_json::Map::new(); + object.insert("x".to_owned(), nested_object_value); + nested_object_value = serde_json::Value::Object(object); + } + let nested_object_value = RecordValue::from_value(nested_object_value); + let encoded_object = encode_payload(nested_object_value.as_value()) + .expect("serialize deeply nested objects"); + assert!(encoded_object.starts_with(r#"{"x":{"x":"#)); + + let deep_object_json = format!( + "{}0{}", + r#"{"x":"#.repeat(MAX_RECORD_DEPTH), + "}".repeat(MAX_RECORD_DEPTH) + ); + let decoded_object = parse_payload(&deep_object_json).expect("parse deeply nested objects"); + let mut leaf = decoded_object.as_value(); + for _ in 0..MAX_RECORD_DEPTH { + leaf = &leaf["x"]; + } + assert!(leaf.is_number()); + + let too_deep = make_nested(MAX_RECORD_DEPTH + 1); + assert!(matches!( + encode_payload(too_deep.as_value()), + Err(StoreError::RecordTooDeep) + )); + let too_deep_json = format!( + "{}0{}", + "[".repeat(MAX_RECORD_DEPTH + 1), + "]".repeat(MAX_RECORD_DEPTH + 1) + ); + assert!(matches!( + parse_payload(&too_deep_json), + Err(StoreError::RecordTooDeep) + )); + } + + #[test] + fn invalid_ids_and_explicit_list_bounds_fail_closed() { + let store = in_memory_store(); + assert!(matches!( + store.put("bad kind", "one", &serde_json::json!({}), ""), + Err(StoreError::InvalidId(_)) + )); + assert!(matches!( + store.list_all("task", 0), + Err(StoreError::InvalidBound) + )); + store + .put("task", "a", &serde_json::json!({}), "") + .expect("record a"); + store + .put("task", "b", &serde_json::json!({}), "") + .expect("record b"); + assert!(matches!( + store.list_all("task", 1), + Err(StoreError::CollectionTooLarge) + )); + assert_eq!(store.list("task", 0).expect("default list limit").len(), 2); + } + + fn empty_database() -> Connection { + Connection::open_in_memory().expect("open in-memory database") + } + + fn create_legacy_v1(connection: &Connection) { + connection + .execute_batch( + "CREATE TABLE IF NOT EXISTS records (kind TEXT NOT NULL,id TEXT NOT NULL,payload TEXT NOT NULL,updated TEXT NOT NULL,PRIMARY KEY(kind,id));\ + CREATE TABLE IF NOT EXISTS events (seq INTEGER PRIMARY KEY AUTOINCREMENT,kind TEXT NOT NULL,ref TEXT NOT NULL,payload TEXT NOT NULL,at TEXT NOT NULL);\ + CREATE TABLE IF NOT EXISTS request_keys (key TEXT PRIMARY KEY,digest TEXT NOT NULL,ref TEXT NOT NULL);", + ) + .expect("create schema from Go v1 store"); + connection + .pragma_update(None, "user_version", SCHEMA_VERSION) + .expect("set legacy schema version"); + } + + #[test] + fn fresh_database_gets_v1_tables_and_a_hashed_ledger_entry() { + let mut connection = empty_database(); + migrate(&mut connection).expect("initial migration"); + validate_v1(&connection).expect("valid resulting schema"); + + let ledger: (i64, String, String, String) = connection + .query_row( + "SELECT version,name,checksum,applied_at FROM schema_migrations", + [], + |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)), + ) + .expect("ledger row"); + assert_eq!(ledger.0, 1); + assert_eq!(ledger.1, MIGRATION_NAME); + assert_eq!(ledger.2, migration_checksum()); + assert!(looks_like_utc_timestamp(&ledger.3)); + } + + #[test] + fn running_migrations_again_is_idempotent_and_preserves_records() { + let mut connection = empty_database(); + migrate(&mut connection).expect("initial migration"); + connection + .execute( + "INSERT INTO records(kind,id,payload,updated) VALUES('task','one','{}','2026-09-25T00:00:00Z')", + [], + ) + .expect("insert representative record"); + + migrate(&mut connection).expect("repeat migration"); + + let (record_count, ledger_count): (i64, i64) = connection + .query_row( + "SELECT (SELECT count(*) FROM records),(SELECT count(*) FROM schema_migrations)", + [], + |row| Ok((row.get(0)?, row.get(1)?)), + ) + .expect("read row counts"); + assert_eq!((record_count, ledger_count), (1, 1)); + } + + #[test] + fn legacy_v1_database_is_adopted_without_changing_its_data() { + let mut connection = empty_database(); + create_legacy_v1(&connection); + connection + .execute( + "INSERT INTO records(kind,id,payload,updated) VALUES(?1,?2,?3,?4)", + params![ + "investigation", + "legacy-1", + "{\"kept\":true}", + "2026-09-25T00:00:00Z" + ], + ) + .expect("insert legacy record"); + connection + .execute( + "INSERT INTO events(kind,ref,payload,at) VALUES(?1,?2,?3,?4)", + params!["created", "legacy-1", "{}", "2026-09-25T00:00:00Z"], + ) + .expect("insert legacy event"); + connection + .execute( + "INSERT INTO request_keys(key,digest,ref) VALUES(?1,?2,?3)", + params!["request-1", "digest-1", "legacy-1"], + ) + .expect("insert legacy idempotency key"); + + migrate(&mut connection).expect("adopt compatible legacy database"); + + let record: String = connection + .query_row( + "SELECT payload FROM records WHERE id='legacy-1'", + [], + |row| row.get(0), + ) + .expect("legacy record remains"); + let (events, request_keys, ledger): (i64, i64, i64) = connection + .query_row( + "SELECT (SELECT count(*) FROM events),(SELECT count(*) FROM request_keys),(SELECT count(*) FROM schema_migrations)", + [], + |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)), + ) + .expect("legacy event, key, and new ledger remain"); + assert_eq!(record, "{\"kept\":true}"); + assert_eq!((events, request_keys, ledger), (1, 1, 1)); + } + + #[test] + fn newer_schema_is_rejected_without_modification() { + let mut connection = empty_database(); + connection + .pragma_update(None, "user_version", SCHEMA_VERSION + 1) + .expect("set future version"); + let error = migrate(&mut connection).expect_err("future schema is unsupported"); + assert!(matches!(error, MigrationError::UnsupportedVersion(2))); + let version: i64 = connection + .pragma_query_value(None, "user_version", |row| row.get(0)) + .expect("read unchanged version"); + assert_eq!(version, 2); + } + + #[test] + fn nonempty_unversioned_database_is_refused_without_table_changes() { + let mut connection = empty_database(); + connection + .execute_batch("CREATE TABLE unrelated(value TEXT);") + .expect("create unversioned table"); + let error = migrate(&mut connection).expect_err("unversioned data is ambiguous"); + assert!(matches!( + error, + MigrationError::UnversionedDatabaseHasObjects + )); + let exists: i64 = connection + .query_row( + "SELECT count(*) FROM sqlite_master WHERE type='table' AND name='unrelated'", + [], + |row| row.get(0), + ) + .expect("query unchanged table"); + assert_eq!(exists, 1); + } + + #[test] + fn failed_initial_migration_rolls_back_all_schema_changes() { + let mut connection = empty_database(); + let result = migrate_empty_database_with(&mut connection, |transaction| { + transaction.execute_batch("THIS IS NOT SQL")?; + Ok(()) + }); + assert!(result.is_err()); + + let version: i64 = connection + .pragma_query_value(None, "user_version", |row| row.get(0)) + .expect("schema version remains readable"); + assert_eq!(version, 0); + assert!(!has_user_schema_objects(&connection).expect("query rolled-back schema")); + } + + #[test] + fn v1_database_with_wrong_ledger_checksum_is_refused() { + let mut connection = empty_database(); + create_legacy_v1(&connection); + create_ledger(&connection).expect("create migration ledger"); + connection + .execute( + "INSERT INTO schema_migrations(version,name,checksum,applied_at) VALUES(1,?1,?2,'2026-09-25T00:00:00.000Z')", + params![MIGRATION_NAME, "0".repeat(64)], + ) + .expect("insert invalid ledger entry"); + + let error = migrate(&mut connection).expect_err("changed migration checksum is unsafe"); + assert!(matches!(error, MigrationError::LedgerMismatch)); + } + + #[test] + fn legacy_v1_schema_with_changed_event_sequence_is_refused() { + let mut connection = empty_database(); + connection + .execute_batch( + "CREATE TABLE records (kind TEXT NOT NULL,id TEXT NOT NULL,payload TEXT NOT NULL,updated TEXT NOT NULL,PRIMARY KEY(kind,id));\ + CREATE TABLE events (seq INTEGER PRIMARY KEY,kind TEXT NOT NULL,ref TEXT NOT NULL,payload TEXT NOT NULL,at TEXT NOT NULL);\ + CREATE TABLE request_keys (key TEXT PRIMARY KEY,digest TEXT NOT NULL,ref TEXT NOT NULL);\ + PRAGMA user_version=1;", + ) + .expect("create altered legacy schema"); + + let error = + migrate(&mut connection).expect_err("changed sequence semantics are incompatible"); + + assert!(matches!(error, MigrationError::LegacySchemaMismatch(_))); + let ledger_exists: i64 = connection + .query_row( + "SELECT count(*) FROM sqlite_master WHERE type='table' AND name='schema_migrations'", + [], + |row| row.get(0), + ) + .expect("check that failed adoption wrote no ledger"); + assert_eq!(ledger_exists, 0); + } +} diff --git a/crates/rover-store/src/resources.rs b/crates/rover-store/src/resources.rs new file mode 100644 index 0000000..4494323 --- /dev/null +++ b/crates/rover-store/src/resources.rs @@ -0,0 +1,409 @@ +//! Persistent coordination reservations for agents and named resources. +//! +//! These rows coordinate Rover-owned work. They are not operating-system locks, +//! port bindings, database access controls, or a mechanism for safely taking +//! over an owner that may still be running. + +use std::error::Error; +use std::fmt; + +use rover_core::{now, Id}; +use rusqlite::TransactionBehavior; +use serde::de::Deserializer; +use serde::{Deserialize, Serialize}; + +use crate::{put_in_transaction, Store, StoreError}; + +const LEASE_KIND: &str = "lease"; +const DEFAULT_CAPACITY: i64 = 4; +const MAX_CAPACITY: i64 = 128; +const MAX_RESOURCE_BYTES: usize = 96; + +fn deserialize_max_agents<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + Ok(Option::::deserialize(deserializer)?.unwrap_or_default()) +} + +/// A persistent reservation for one resource slot or named resource. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct Lease { + /// Task or workflow that owns this reservation. + pub owner: String, + /// Stable record key for the slot or named resource. + pub resource: String, + /// Timestamp at which this reservation was last acquired. + pub acquired_at: String, +} + +/// A resource reservation failure. +#[derive(Debug)] +pub enum ResourceError { + /// Owner, capacity, or resource names violate the Go storage contract. + InvalidRequest, + /// Another owner holds the agent slot or one of the named resources. + Busy, + /// The backing store failed. + Store(StoreError), +} + +impl fmt::Display for ResourceError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidRequest => formatter.write_str("invalid resource request"), + Self::Busy => formatter.write_str("resources busy"), + Self::Store(error) => write!(formatter, "resource store failed: {error}"), + } + } +} + +impl Error for ResourceError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::Store(error) => Some(error), + Self::InvalidRequest | Self::Busy => None, + } + } +} + +impl Store { + /// Atomically reserve an agent slot and all named resources for one owner. + /// + /// A repeated acquisition by the same owner is idempotent and refreshes + /// acquisition timestamps. No lease expires automatically: Rover must not + /// transfer a reservation while the original process could still run. + /// + /// # Errors + /// + /// Returns [`ResourceError::InvalidRequest`] for invalid owners, resource + /// names, or capacities outside 1..=128; [`ResourceError::Busy`] if the + /// capacity or a named resource is held by another owner; or + /// [`ResourceError::Store`] for database failures. + pub fn acquire_resources( + &self, + owner: &str, + resources: &[String], + capacity: i64, + ) -> Result<(), ResourceError> { + validate_request(owner, resources, capacity)?; + + let mut connection = self.lock().map_err(ResourceError::Store)?; + let transaction = connection + .transaction_with_behavior(TransactionBehavior::Immediate) + .map_err(StoreError::from) + .map_err(ResourceError::Store)?; + + let mut statement = transaction + .prepare("SELECT id,payload FROM records WHERE kind='lease'") + .map_err(StoreError::from) + .map_err(ResourceError::Store)?; + let rows = statement + .query_map([], |row| { + Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?)) + }) + .map_err(StoreError::from) + .map_err(ResourceError::Store)? + .collect::, _>>() + .map_err(StoreError::from) + .map_err(ResourceError::Store)?; + drop(statement); + + let mut owners = std::collections::HashSet::new(); + let mut occupied = std::collections::HashMap::new(); + for (id, payload) in rows { + let lease: Lease = serde_json::from_str(&payload) + .map_err(StoreError::from) + .map_err(ResourceError::Store)?; + owners.insert(lease.owner.clone()); + occupied.insert(id, lease.owner); + } + + if !owners.contains(owner) && i64::try_from(owners.len()).unwrap_or(i64::MAX) >= capacity { + return Err(ResourceError::Busy); + } + + let mut keys = Vec::with_capacity(resources.len() + 1); + keys.push(format!("slot.{owner}")); + keys.extend( + resources + .iter() + .map(|resource| format!("resource.{resource}")), + ); + if keys + .iter() + .any(|key| occupied.get(key).is_some_and(|held_by| held_by != owner)) + { + return Err(ResourceError::Busy); + } + + for key in keys { + let acquired_at = now() + .to_rfc3339() + .map_err(|error| StoreError::Timestamp(error.to_string())) + .map_err(ResourceError::Store)?; + let lease = Lease { + owner: owner.to_owned(), + resource: key.clone(), + acquired_at, + }; + put_in_transaction(&transaction, LEASE_KIND, &key, &lease, "") + .map_err(ResourceError::Store)?; + } + + transaction + .commit() + .map_err(StoreError::from) + .map_err(ResourceError::Store) + } + + /// Release every slot and named resource owned by exactly `owner`. + /// + /// The delete is transactional and cannot release another owner's rows. + /// It intentionally does not infer that an owner is dead or perform + /// timeout-based takeover. + /// + /// # Errors + /// + /// Returns [`StoreError`] if the database operation fails. + pub fn release_resources(&self, owner: &str) -> Result<(), StoreError> { + let mut connection = self.lock()?; + let transaction = connection.transaction_with_behavior(TransactionBehavior::Immediate)?; + let mut statement = + transaction.prepare("SELECT id,payload FROM records WHERE kind='lease'")?; + let rows = statement + .query_map([], |row| { + Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?)) + })? + .collect::, _>>()?; + drop(statement); + + for (id, payload) in rows { + let lease: Lease = serde_json::from_str(&payload)?; + if lease.owner == owner { + transaction.execute( + "DELETE FROM records WHERE kind=?1 AND id=?2", + (LEASE_KIND, id), + )?; + } + } + transaction.commit()?; + Ok(()) + } + + /// Read the configured agent capacity, using Rover's default of four. + /// + /// # Errors + /// + /// Returns [`ResourceError::InvalidRequest`] if a stored capacity is + /// outside 1..=128, or [`ResourceError::Store`] for malformed settings or + /// database failures. + pub fn agent_capacity(&self) -> Result { + #[derive(Deserialize)] + struct RuntimeSettings { + #[serde(default, deserialize_with = "deserialize_max_agents")] + max_agents: i64, + } + + match self.get_typed::("settings", "runtime") { + Ok(settings) if (1..=MAX_CAPACITY).contains(&settings.max_agents) => { + Ok(settings.max_agents) + } + Ok(_) => Err(ResourceError::InvalidRequest), + Err(StoreError::NotFound) => Ok(DEFAULT_CAPACITY), + Err(error) => Err(ResourceError::Store(error)), + } + } +} + +fn validate_request(owner: &str, resources: &[String], capacity: i64) -> Result<(), ResourceError> { + if Id::parse(owner).is_err() || !(1..=MAX_CAPACITY).contains(&capacity) { + return Err(ResourceError::InvalidRequest); + } + for resource in resources { + if Id::parse(resource.as_str()).is_err() || resource.len() > MAX_RESOURCE_BYTES { + return Err(ResourceError::InvalidRequest); + } + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::fs; + use std::path::PathBuf; + use std::sync::atomic::{AtomicU64, Ordering}; + use std::sync::{Arc, Barrier}; + use std::thread; + use std::time::{SystemTime, UNIX_EPOCH}; + + use rusqlite::Connection; + + use super::{Lease, ResourceError}; + use crate::Store; + + static TEST_SEQUENCE: AtomicU64 = AtomicU64::new(0); + + fn store() -> Store { + Store::from_connection(Connection::open_in_memory().expect("in-memory SQLite")) + .expect("migrate in-memory SQLite") + } + + struct TestDatabase(PathBuf); + + impl TestDatabase { + fn new() -> Self { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("clock follows epoch") + .as_nanos(); + let path = std::env::temp_dir().join(format!( + "rover-reservations-{}-{timestamp}-{}", + std::process::id(), + TEST_SEQUENCE.fetch_add(1, Ordering::Relaxed) + )); + Self(path) + } + + fn open(&self) -> Store { + Store::from_connection(Connection::open(&self.0).expect("open shared SQLite file")) + .expect("migrate shared SQLite file") + } + } + + impl Drop for TestDatabase { + fn drop(&mut self) { + let _ = fs::remove_file(&self.0); + for suffix in ["-wal", "-shm"] { + let mut sidecar = self.0.as_os_str().to_os_string(); + sidecar.push(suffix); + let _ = fs::remove_file(PathBuf::from(sidecar)); + } + } + } + + #[test] + fn named_resources_capacity_and_owner_cleanup_match_go_contract() { + let store = store(); + let shared = vec!["database".to_owned(), "port-8080".to_owned()]; + store + .acquire_resources("owner_a", &shared, 2) + .expect("first owner reserves resources"); + assert!(matches!( + store.acquire_resources("owner_b", &["database".to_owned()], 2), + Err(ResourceError::Busy) + )); + store + .acquire_resources("owner_a", &["database".to_owned()], 2) + .expect("owner can re-acquire its own resource"); + store + .acquire_resources("owner_b", &["other".to_owned()], 2) + .expect("second owner uses a distinct resource"); + assert!(matches!( + store.acquire_resources("owner_c", &[], 2), + Err(ResourceError::Busy) + )); + + store + .release_resources("owner_a") + .expect("owner releases its own reservations"); + let remaining = store + .list_all_raw("lease", 10) + .expect("read lease rows") + .iter() + .map(|row| serde_json::from_str::(row).expect("decode lease")) + .collect::>(); + assert_eq!(remaining.len(), 2); + assert!(remaining.iter().all(|lease| lease.owner == "owner_b")); + store + .acquire_resources("owner_c", &["database".to_owned()], 2) + .expect("released resource can be acquired"); + } + + #[test] + fn request_validation_and_capacity_defaults_match_go_contract() { + let store = store(); + assert_eq!(store.agent_capacity().expect("default capacity"), 4); + for (owner, resources, capacity) in [ + ("", vec![], 4), + ("owner", vec!["bad/resource".to_owned()], 4), + ("owner", vec!["x".repeat(97)], 4), + ("owner", vec![], 0), + ("owner", vec![], 129), + ] { + assert!(matches!( + store.acquire_resources(owner, &resources, capacity), + Err(ResourceError::InvalidRequest) + )); + } + + store + .put( + "settings", + "runtime", + &serde_json::json!({"max_agents": 7}), + "", + ) + .expect("configure capacity"); + assert_eq!(store.agent_capacity().expect("configured capacity"), 7); + store + .put( + "settings", + "runtime", + &serde_json::json!({"max_agents": 0}), + "", + ) + .expect("configure invalid capacity"); + assert!(matches!( + store.agent_capacity(), + Err(ResourceError::InvalidRequest) + )); + for missing_or_null in [ + serde_json::json!({}), + serde_json::json!({"max_agents": null}), + ] { + store + .put("settings", "runtime", &missing_or_null, "") + .expect("configure absent capacity"); + assert!(matches!( + store.agent_capacity(), + Err(ResourceError::InvalidRequest) + )); + } + } + + #[test] + fn separate_connections_cannot_acquire_the_same_resource() { + let database = TestDatabase::new(); + let first = Arc::new(database.open()); + let second = Arc::new(database.open()); + let barrier = Arc::new(Barrier::new(3)); + let workers = [ + (Arc::clone(&first), "owner_a"), + (Arc::clone(&second), "owner_b"), + ] + .into_iter() + .map(|(store, owner)| { + let barrier = Arc::clone(&barrier); + thread::spawn(move || { + barrier.wait(); + store.acquire_resources(owner, &["port-8080".to_owned()], 4) + }) + }) + .collect::>(); + barrier.wait(); + + let results = workers + .into_iter() + .map(|worker| worker.join().expect("reservation worker did not panic")) + .collect::>(); + assert_eq!(results.iter().filter(|result| result.is_ok()).count(), 1); + assert_eq!( + results + .iter() + .filter(|result| matches!(result, Err(ResourceError::Busy))) + .count(), + 1 + ); + } +} diff --git a/crates/rover-tasks/Cargo.toml b/crates/rover-tasks/Cargo.toml new file mode 100644 index 0000000..b7f029b --- /dev/null +++ b/crates/rover-tasks/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "rover-tasks" +version = "0.1.0" +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[dependencies] +rover-core = { path = "../rover-core" } +rover-store = { path = "../rover-store" } +serde = { version = "=1.0.229", default-features = false, features = ["std", "derive"] } +serde_json = { version = "=1.0.151", default-features = false, features = ["std"] } + +[dev-dependencies] +rusqlite = { version = "=0.40.2", default-features = false, features = ["bundled"] } + +[lints] +workspace = true diff --git a/crates/rover-tasks/src/lib.rs b/crates/rover-tasks/src/lib.rs new file mode 100644 index 0000000..506c688 --- /dev/null +++ b/crates/rover-tasks/src/lib.rs @@ -0,0 +1,834 @@ +//! Project-bound durable task plans, separate from executable task runs. +//! +//! Plans preserve user intent and dependencies. They are not dispatched and +//! do not claim execution, agent selection, or quality-gate results. + +use rover_core::{ + validate_task_graph, Id, RepositoryIdentity, Sha256Digest, TaskBrief, TaskGraphError, + TaskGraphNode, +}; +use rover_store::{RecordValue, Store, StoreError}; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; +use std::fmt; + +const RECORD_KIND: &str = "task_plan"; +const RECORD_SCHEMA: &str = "rover/task-plan/v1"; +const MAX_TASK_PLANS: i64 = 10_000; +const MAX_NOTE_BYTES: usize = 4096; +const MAX_TASK_ATTEMPTS: usize = 10; + +/// Durable task-plan lifecycle. A ready plan still needs an explicit start. +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum TaskPlanStatus { + Ready, + Waiting, + Running, + Succeeded, + Failed, + Cancelled, +} + +impl TaskPlanStatus { + /// Stable serialized status spelling. + #[must_use] + pub const fn as_str(self) -> &'static str { + match self { + Self::Ready => "READY", + Self::Waiting => "WAITING", + Self::Running => "RUNNING", + Self::Succeeded => "SUCCEEDED", + Self::Failed => "FAILED", + Self::Cancelled => "CANCELLED", + } + } +} + +/// Attempt manually recorded through the plan service. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct TaskPlanAttempt { + pub number: u32, + pub started_at: String, + pub finished_at: Option, + pub outcome: Option, + pub note: Option, + pub authority: String, +} + +/// Durable intent bound to one canonical project path and digest. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct TaskPlan { + pub schema: String, + pub id: Id, + pub project_id: String, + pub repository: String, + pub brief: TaskBrief, + pub dependencies: Vec, + pub status: TaskPlanStatus, + pub attempts: Vec, + pub created_at: String, + pub updated_at: String, +} + +/// Derived state of a plan's prerequisites. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum TaskReadiness { + Ready, + Waiting, + Blocked, +} + +/// Task-plan service error. +#[derive(Debug)] +pub enum TaskPlanError { + Store(StoreError), + Brief(rover_core::TaskBriefError), + Graph(TaskGraphError), + InvalidRecord, + InvalidProjectPath, + MissingDependency(Id), + DependencyFailed(Id), + NotFound, + ProjectMismatch, + InvalidTransition, + InvalidNote, +} + +impl fmt::Display for TaskPlanError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Store(error) => write!(formatter, "task-plan storage failed: {error}"), + Self::Brief(error) => write!(formatter, "invalid task brief: {error}"), + Self::Graph(error) => write!(formatter, "invalid task graph: {error}"), + Self::InvalidRecord => formatter.write_str("stored task plan is invalid"), + Self::InvalidProjectPath => formatter.write_str("repository path is not valid UTF-8"), + Self::MissingDependency(id) => write!( + formatter, + "task dependency {id} is missing from this project" + ), + Self::DependencyFailed(id) => write!(formatter, "task dependency {id} did not succeed"), + Self::NotFound => formatter.write_str("task plan was not found in this project"), + Self::ProjectMismatch => formatter.write_str("task plan belongs to another project"), + Self::InvalidTransition => formatter.write_str("task-plan transition is not allowed"), + Self::InvalidNote => formatter.write_str("task note is empty, oversized, or unsafe"), + } + } +} + +impl std::error::Error for TaskPlanError {} + +impl From for TaskPlanError { + fn from(error: StoreError) -> Self { + Self::Store(error) + } +} + +impl From for TaskPlanError { + fn from(error: rover_core::TaskBriefError) -> Self { + Self::Brief(error) + } +} + +impl From for TaskPlanError { + fn from(error: TaskGraphError) -> Self { + Self::Graph(error) + } +} + +/// Service for creating and manually tracking project-bound task plans. +pub struct TaskPlanService<'a> { + store: &'a Store, +} + +impl<'a> TaskPlanService<'a> { + /// Bind the service to an already-open Rover store. + #[must_use] + pub const fn new(store: &'a Store) -> Self { + Self { store } + } + + /// Validate and durably create a plan. Dependencies must already exist in + /// the same project. This operation never dispatches a process or agent. + /// + /// # Errors + /// + /// Returns an error for invalid fields, missing dependencies, malformed + /// stored task plans, unsupported repository paths, or store failures. + pub fn create( + &self, + project: &RepositoryIdentity, + brief: TaskBrief, + ) -> Result { + brief.validate()?; + let repository = project + .path() + .to_str() + .ok_or(TaskPlanError::InvalidProjectPath)? + .to_owned(); + let project_id = project.project_id().to_hex(); + let current = self.list(project)?; + let known = current + .iter() + .map(|plan| (plan.id.clone(), plan)) + .collect::>(); + let dependencies = parse_dependencies(&brief)?; + for dependency in &dependencies { + if !known.contains_key(dependency) { + return Err(TaskPlanError::MissingDependency(dependency.clone())); + } + } + + let id = Id::generate("plan").map_err(|_| TaskPlanError::InvalidRecord)?; + let graph = current + .iter() + .map(|plan| TaskGraphNode { + id: plan.id.clone(), + dependencies: plan.dependencies.clone(), + }) + .chain(std::iter::once(TaskGraphNode { + id: id.clone(), + dependencies: dependencies.clone(), + })) + .collect::>(); + validate_task_graph(&graph)?; + + let initial_readiness = + dependencies + .iter() + .try_fold(TaskReadiness::Ready, |state, dependency| { + let prerequisite = known + .get(dependency) + .ok_or_else(|| TaskPlanError::MissingDependency(dependency.clone()))?; + match prerequisite.status { + TaskPlanStatus::Succeeded => Ok(state), + TaskPlanStatus::Failed | TaskPlanStatus::Cancelled => { + Err(TaskPlanError::DependencyFailed(dependency.clone())) + } + _ => Ok(TaskReadiness::Waiting), + } + })?; + let now = rover_core::now().to_string(); + let plan = TaskPlan { + schema: RECORD_SCHEMA.to_owned(), + id: id.clone(), + project_id: project_id.clone(), + repository, + brief, + dependencies, + status: match initial_readiness { + TaskReadiness::Ready => TaskPlanStatus::Ready, + _ => TaskPlanStatus::Waiting, + }, + attempts: Vec::new(), + created_at: now.clone(), + updated_at: now, + }; + let digest = + Sha256Digest::of(&serde_json::to_vec(&plan).map_err(|_| TaskPlanError::InvalidRecord)?) + .to_hex(); + let request_key = format!("task-plan:create:{project_id}:{id}"); + self.store + .create_once(RECORD_KIND, id.as_str(), &request_key, &digest, &plan)?; + Ok(plan) + } + + /// List this project's plans. Corrupt records fail closed; results are + /// stable by creation time and then task ID. + /// + /// # Errors + /// + /// Returns an error on store access or malformed records. + pub fn list(&self, project: &RepositoryIdentity) -> Result, TaskPlanError> { + let repository = project + .path() + .to_str() + .ok_or(TaskPlanError::InvalidProjectPath)?; + let project_id = project.project_id().to_hex(); + let records = self.store.list_all(RECORD_KIND, MAX_TASK_PLANS)?; + let mut plans = records + .iter() + .map(decode_plan) + .collect::, _>>()? + .into_iter() + .filter(|plan| plan.project_id == project_id && plan.repository == repository) + .collect::>(); + validate_task_graph( + &plans + .iter() + .map(|plan| TaskGraphNode { + id: plan.id.clone(), + dependencies: plan.dependencies.clone(), + }) + .collect::>(), + )?; + plans.sort_by(|left, right| { + left.created_at + .cmp(&right.created_at) + .then_with(|| left.id.cmp(&right.id)) + }); + Ok(plans) + } + + /// List plans with readiness derived from one consistent in-memory + /// snapshot of the returned project plans. + /// + /// # Errors + /// + /// Returns an error on malformed task graphs or store access failure. + pub fn list_with_readiness( + &self, + project: &RepositoryIdentity, + ) -> Result, TaskPlanError> { + let plans = self.list(project)?; + let readiness = readiness_snapshot(&plans)?; + plans + .iter() + .map(|plan| { + readiness + .get(&plan.id) + .copied() + .map(|state| (plan.clone(), state)) + .ok_or(TaskPlanError::InvalidRecord) + }) + .collect() + } + + /// Load a plan only when both its canonical path and project digest match. + /// + /// # Errors + /// + /// Returns `NotFound`, `ProjectMismatch`, or an error for corrupt storage. + pub fn get(&self, project: &RepositoryIdentity, id: &Id) -> Result { + let plan: TaskPlan = self + .store + .get_typed(RECORD_KIND, id.as_str()) + .map_err(|error| { + if matches!(error, StoreError::NotFound) { + TaskPlanError::NotFound + } else { + TaskPlanError::Store(error) + } + })?; + validate_record(&plan)?; + if project.path().to_str() != Some(plan.repository.as_str()) + || project.project_id().to_hex() != plan.project_id + { + return Err(TaskPlanError::ProjectMismatch); + } + Ok(plan) + } + + /// Compute dependency readiness from current durable plan states. + /// + /// # Errors + /// + /// Returns an error when the plan or a prerequisite is unavailable. + pub fn readiness( + &self, + project: &RepositoryIdentity, + id: &Id, + ) -> Result { + let plans = self.list(project)?; + readiness_snapshot(&plans)? + .get(id) + .copied() + .ok_or(TaskPlanError::NotFound) + } + + /// Start a ready plan as a manually tracked attempt; no process is started. + /// + /// # Errors + /// + /// Returns an error if the project, status, dependency state, or store is invalid. + pub fn start_manual( + &self, + project: &RepositoryIdentity, + id: &Id, + ) -> Result { + let current = self.get(project, id)?; + if !matches!( + current.status, + TaskPlanStatus::Ready | TaskPlanStatus::Waiting + ) || current.attempts.len() >= MAX_TASK_ATTEMPTS + || self.readiness(project, id)? != TaskReadiness::Ready + { + return Err(TaskPlanError::InvalidTransition); + } + self.store.mutate( + RECORD_KIND, + id.as_str(), + "task_plan.started_manual", + |value| { + let mut plan: TaskPlan = serde_json::from_value(value.clone()) + .map_err(|_| StoreError::MutationRejected("invalid task plan".into()))?; + if !matches!(plan.status, TaskPlanStatus::Ready | TaskPlanStatus::Waiting) { + return Err(StoreError::MutationRejected( + "task plan is not startable".into(), + )); + } + let now = rover_core::now().to_string(); + let number = u32::try_from(plan.attempts.len() + 1) + .map_err(|_| StoreError::MutationRejected("attempt count overflow".into()))?; + plan.attempts.push(TaskPlanAttempt { + number, + started_at: now.clone(), + finished_at: None, + outcome: None, + note: None, + authority: "manual".into(), + }); + plan.status = TaskPlanStatus::Running; + plan.updated_at = now; + serde_json::to_value(plan) + .map_err(|_| StoreError::MutationRejected("task plan encoding failed".into())) + }, + )?; + self.get(project, id) + } + + /// Return a failed task to ready after its dependencies have succeeded. + /// + /// # Errors + /// + /// Returns an error unless the task is failed and every prerequisite succeeded. + pub fn retry_manual( + &self, + project: &RepositoryIdentity, + id: &Id, + ) -> Result { + let current = self.get(project, id)?; + if current.status != TaskPlanStatus::Failed + || current.attempts.len() >= MAX_TASK_ATTEMPTS + || self.dependencies_readiness(project, ¤t)? != TaskReadiness::Ready + { + return Err(TaskPlanError::InvalidTransition); + } + self.store.mutate( + RECORD_KIND, + id.as_str(), + "task_plan.retried_manual", + |value| { + let mut plan: TaskPlan = serde_json::from_value(value.clone()) + .map_err(|_| StoreError::MutationRejected("invalid task plan".into()))?; + if plan.status != TaskPlanStatus::Failed { + return Err(StoreError::MutationRejected( + "task plan is not failed".into(), + )); + } + plan.status = TaskPlanStatus::Ready; + plan.updated_at = rover_core::now().to_string(); + serde_json::to_value(plan) + .map_err(|_| StoreError::MutationRejected("task plan encoding failed".into())) + }, + )?; + self.get(project, id) + } + + /// Record a manual outcome. The note is retained as a user assertion and + /// is never presented as process or quality-gate evidence. + /// + /// # Errors + /// + /// Returns an error for an invalid outcome, note, lifecycle state, or store. + pub fn finish_manual( + &self, + project: &RepositoryIdentity, + id: &Id, + outcome: TaskPlanStatus, + note: &str, + ) -> Result { + if !matches!( + outcome, + TaskPlanStatus::Succeeded | TaskPlanStatus::Failed | TaskPlanStatus::Cancelled + ) || note.trim().is_empty() + || note.len() > MAX_NOTE_BYTES + || note + .chars() + .any(|ch| ch.is_control() && ch != '\n' || is_bidi_format(ch)) + { + return Err(TaskPlanError::InvalidNote); + } + self.get(project, id)?; + self.store.mutate( + RECORD_KIND, + id.as_str(), + "task_plan.finished_manual", + |value| { + let mut plan: TaskPlan = serde_json::from_value(value.clone()) + .map_err(|_| StoreError::MutationRejected("invalid task plan".into()))?; + if plan.status != TaskPlanStatus::Running { + return Err(StoreError::MutationRejected( + "task plan is not running".into(), + )); + } + let attempt = plan.attempts.last_mut().ok_or_else(|| { + StoreError::MutationRejected("running plan has no attempt".into()) + })?; + if attempt.finished_at.is_some() { + return Err(StoreError::MutationRejected( + "attempt already finished".into(), + )); + } + let now = rover_core::now().to_string(); + attempt.finished_at = Some(now.clone()); + attempt.outcome = Some(status_name(outcome).to_owned()); + attempt.note = Some(note.to_owned()); + plan.status = outcome; + plan.updated_at = now; + serde_json::to_value(plan) + .map_err(|_| StoreError::MutationRejected("task plan encoding failed".into())) + }, + )?; + self.get(project, id) + } + + fn dependencies_readiness( + &self, + project: &RepositoryIdentity, + plan: &TaskPlan, + ) -> Result { + let plans = self.list(project)?; + let by_id = plans + .iter() + .map(|candidate| (candidate.id.clone(), candidate)) + .collect::>(); + let readiness = readiness_snapshot(&plans)?; + dependency_readiness(plan, &by_id, &readiness) + } +} + +fn parse_dependencies(brief: &TaskBrief) -> Result, TaskPlanError> { + brief + .dependencies + .lines() + .filter(|line| !line.trim().is_empty()) + .map(|line| Id::parse(line.to_owned()).map_err(|_| TaskPlanError::InvalidRecord)) + .collect() +} + +fn decode_plan(value: &RecordValue) -> Result { + let plan = serde_json::from_value(value.as_value().clone()) + .map_err(|_| TaskPlanError::InvalidRecord)?; + validate_record(&plan)?; + Ok(plan) +} + +fn validate_record(plan: &TaskPlan) -> Result<(), TaskPlanError> { + if plan.schema != RECORD_SCHEMA + || plan.brief.validate().is_err() + || plan.attempts.len() > MAX_TASK_ATTEMPTS + { + return Err(TaskPlanError::InvalidRecord); + } + let project_id = + Sha256Digest::parse_hex(&plan.project_id).map_err(|_| TaskPlanError::InvalidRecord)?; + let repository = + RepositoryIdentity::resolve(&plan.repository).map_err(|_| TaskPlanError::InvalidRecord)?; + if project_id.to_hex() != plan.project_id + || repository.project_id() != project_id + || repository.path().to_str() != Some(plan.repository.as_str()) + { + return Err(TaskPlanError::InvalidRecord); + } + if parse_dependencies(&plan.brief)? != plan.dependencies { + return Err(TaskPlanError::InvalidRecord); + } + let mut unfinished_attempts = 0; + for (index, attempt) in plan.attempts.iter().enumerate() { + if usize::try_from(attempt.number).ok() != Some(index + 1) + || attempt.authority != "manual" + || attempt.note.as_ref().is_some_and(|note| { + note.is_empty() + || note.len() > MAX_NOTE_BYTES + || note + .chars() + .any(|ch| (ch.is_control() && ch != '\n') || is_bidi_format(ch)) + }) + || attempt + .outcome + .as_deref() + .is_some_and(|outcome| !matches!(outcome, "SUCCEEDED" | "FAILED" | "CANCELLED")) + || attempt.finished_at.is_some() != attempt.outcome.is_some() + || attempt.finished_at.is_some() != attempt.note.is_some() + { + return Err(TaskPlanError::InvalidRecord); + } + if attempt.finished_at.is_none() { + unfinished_attempts += 1; + if index + 1 != plan.attempts.len() { + return Err(TaskPlanError::InvalidRecord); + } + } + } + match plan.status { + TaskPlanStatus::Running if unfinished_attempts == 1 => {} + TaskPlanStatus::Running => return Err(TaskPlanError::InvalidRecord), + TaskPlanStatus::Succeeded | TaskPlanStatus::Failed | TaskPlanStatus::Cancelled => { + if unfinished_attempts != 0 + || plan + .attempts + .last() + .and_then(|attempt| attempt.outcome.as_deref()) + != Some(status_name(plan.status)) + { + return Err(TaskPlanError::InvalidRecord); + } + } + TaskPlanStatus::Ready | TaskPlanStatus::Waiting if unfinished_attempts != 0 => { + return Err(TaskPlanError::InvalidRecord) + } + _ => {} + } + Ok(()) +} + +fn readiness_snapshot(plans: &[TaskPlan]) -> Result, TaskPlanError> { + let by_id = plans + .iter() + .map(|plan| (plan.id.clone(), plan)) + .collect::>(); + let graph = plans + .iter() + .map(|plan| TaskGraphNode { + id: plan.id.clone(), + dependencies: plan.dependencies.clone(), + }) + .collect::>(); + let mut readiness = BTreeMap::new(); + for id in validate_task_graph(&graph)? { + let plan = by_id.get(&id).ok_or(TaskPlanError::InvalidRecord)?; + let state = if matches!( + plan.status, + TaskPlanStatus::Succeeded | TaskPlanStatus::Failed | TaskPlanStatus::Cancelled + ) { + TaskReadiness::Blocked + } else { + dependency_readiness(plan, &by_id, &readiness)? + }; + readiness.insert(id, state); + } + Ok(readiness) +} + +fn dependency_readiness( + plan: &TaskPlan, + by_id: &BTreeMap, + readiness: &BTreeMap, +) -> Result { + let mut result = TaskReadiness::Ready; + for id in &plan.dependencies { + let dependency = by_id.get(id).ok_or_else(|| { + TaskPlanError::Graph(TaskGraphError::MissingDependency { + task: plan.id.clone(), + prerequisite: id.clone(), + }) + })?; + match dependency.status { + TaskPlanStatus::Succeeded => {} + TaskPlanStatus::Failed | TaskPlanStatus::Cancelled => { + return Ok(TaskReadiness::Blocked) + } + _ if readiness.get(id) == Some(&TaskReadiness::Blocked) => { + return Ok(TaskReadiness::Blocked) + } + _ => result = TaskReadiness::Waiting, + } + } + Ok(result) +} + +fn status_name(status: TaskPlanStatus) -> &'static str { + status.as_str() +} + +fn is_bidi_format(ch: char) -> bool { + matches!(ch, '\u{061c}' | '\u{200e}'..='\u{200f}' | '\u{202a}'..='\u{202e}' | '\u{2066}'..='\u{2069}' | '\u{206a}'..='\u{206f}') +} + +#[cfg(test)] +mod tests { + use super::{TaskPlanError, TaskPlanService, TaskPlanStatus, TaskReadiness, MAX_TASK_ATTEMPTS}; + use rover_core::{RepositoryIdentity, TaskBrief}; + use rover_store::Store; + use rusqlite::Connection; + + fn store() -> Store { + Store::from_connection(Connection::open_in_memory().expect("open memory SQLite")) + .expect("store") + } + fn brief(title: &str, dependencies: &str) -> TaskBrief { + TaskBrief { + title: title.into(), + paths: "src/**".into(), + dependencies: dependencies.into(), + quality_gate: "manual review".into(), + prompt: format!("Implement {title}"), + } + } + + #[test] + fn creates_project_bound_plans_and_derives_readiness() { + let store = store(); + let service = TaskPlanService::new(&store); + let project = RepositoryIdentity::resolve("/tmp/rover-plan-a").unwrap(); + let first = service.create(&project, brief("First", "")).unwrap(); + let second = service + .create(&project, brief("Second", first.id.as_str())) + .unwrap(); + assert_eq!(first.status, TaskPlanStatus::Ready); + assert_eq!(second.status, TaskPlanStatus::Waiting); + assert_eq!( + service.readiness(&project, &second.id).unwrap(), + TaskReadiness::Waiting + ); + assert_eq!(service.list(&project).unwrap().len(), 2); + let other = RepositoryIdentity::resolve("/tmp/rover-plan-b").unwrap(); + assert!(service.list(&other).unwrap().is_empty()); + assert!(matches!( + service.get(&other, &first.id), + Err(TaskPlanError::ProjectMismatch) + )); + } + + #[test] + fn manual_lifecycle_records_attempt_history_and_retry() { + let store = store(); + let service = TaskPlanService::new(&store); + let project = RepositoryIdentity::resolve("/tmp/rover-plan").unwrap(); + let plan = service.create(&project, brief("Review", "")).unwrap(); + assert_eq!( + service.start_manual(&project, &plan.id).unwrap().status, + TaskPlanStatus::Running + ); + service + .finish_manual( + &project, + &plan.id, + TaskPlanStatus::Failed, + "Needs another pass", + ) + .unwrap(); + service.retry_manual(&project, &plan.id).unwrap(); + assert_eq!( + service + .start_manual(&project, &plan.id) + .unwrap() + .attempts + .len(), + 2 + ); + let finished = service + .finish_manual( + &project, + &plan.id, + TaskPlanStatus::Succeeded, + "Reviewed manually", + ) + .unwrap(); + assert_eq!(finished.status, TaskPlanStatus::Succeeded); + assert_eq!(finished.attempts.len(), 2); + assert!(service.start_manual(&project, &plan.id).is_err()); + } + + #[test] + fn dependencies_gate_start_and_missing_ids_fail() { + let store = store(); + let service = TaskPlanService::new(&store); + let project = RepositoryIdentity::resolve("/tmp/rover-plan").unwrap(); + assert!(matches!( + service.create(&project, brief("Child", "plan_missing")), + Err(TaskPlanError::MissingDependency(_)) + )); + let parent = service.create(&project, brief("Parent", "")).unwrap(); + let child = service + .create(&project, brief("Child", parent.id.as_str())) + .unwrap(); + let grandchild = service + .create(&project, brief("Grandchild", child.id.as_str())) + .unwrap(); + assert!(matches!( + service.start_manual(&project, &child.id), + Err(TaskPlanError::InvalidTransition) + )); + service.start_manual(&project, &parent.id).unwrap(); + service + .finish_manual( + &project, + &parent.id, + TaskPlanStatus::Succeeded, + "Approved manually", + ) + .unwrap(); + assert_eq!( + service.readiness(&project, &child.id).unwrap(), + TaskReadiness::Ready + ); + assert_eq!( + service.readiness(&project, &grandchild.id).unwrap(), + TaskReadiness::Waiting + ); + assert_eq!( + service.start_manual(&project, &child.id).unwrap().status, + TaskPlanStatus::Running + ); + } + + #[test] + fn cancelled_or_failed_prerequisites_block_children_and_project_checks_hold() { + let store = store(); + let service = TaskPlanService::new(&store); + let project = RepositoryIdentity::resolve("/tmp/rover-plan").unwrap(); + let parent = service.create(&project, brief("Parent", "")).unwrap(); + let child = service + .create(&project, brief("Child", parent.id.as_str())) + .unwrap(); + let grandchild = service + .create(&project, brief("Grandchild", child.id.as_str())) + .unwrap(); + service.start_manual(&project, &parent.id).unwrap(); + service + .finish_manual( + &project, + &parent.id, + TaskPlanStatus::Cancelled, + "Cancelled manually", + ) + .unwrap(); + assert_eq!( + service.readiness(&project, &child.id).unwrap(), + TaskReadiness::Blocked + ); + assert_eq!( + service.readiness(&project, &grandchild.id).unwrap(), + TaskReadiness::Blocked + ); + assert!(service + .finish_manual(&project, &child.id, TaskPlanStatus::Succeeded, "too soon") + .is_err()); + } + + #[test] + fn manual_attempt_history_is_bounded() { + let store = store(); + let service = TaskPlanService::new(&store); + let project = RepositoryIdentity::resolve("/tmp/rover-plan").unwrap(); + let mut plan = service + .create(&project, brief("Bounded retry", "")) + .unwrap(); + for attempt in 1..=MAX_TASK_ATTEMPTS { + plan = service.start_manual(&project, &plan.id).unwrap(); + plan = service + .finish_manual(&project, &plan.id, TaskPlanStatus::Failed, "retry fixture") + .unwrap(); + assert_eq!(plan.attempts.len(), attempt); + if attempt < MAX_TASK_ATTEMPTS { + plan = service.retry_manual(&project, &plan.id).unwrap(); + } + } + assert!(matches!( + service.retry_manual(&project, &plan.id), + Err(TaskPlanError::InvalidTransition) + )); + } +} diff --git a/crates/rover-tui/Cargo.toml b/crates/rover-tui/Cargo.toml new file mode 100644 index 0000000..b00f803 --- /dev/null +++ b/crates/rover-tui/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "rover-tui" +version = "0.1.0" +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[dependencies] +crossterm = { version = "=0.29.0", default-features = false, features = ["bracketed-paste", "events", "windows"] } +ratatui = { version = "=0.30.2", default-features = false, features = ["crossterm"] } +rover-core = { path = "../rover-core" } +rover-agents = { path = "../rover-agents" } +rover-execution = { path = "../rover-execution" } +rover-source = { path = "../rover-source" } +rover-store = { path = "../rover-store" } + +[lints] +workspace = true diff --git a/crates/rover-tui/src/lib.rs b/crates/rover-tui/src/lib.rs new file mode 100644 index 0000000..ca01f2e --- /dev/null +++ b/crates/rover-tui/src/lib.rs @@ -0,0 +1,9168 @@ +//! Interactive terminal presentation and input for Rover workspaces. +//! +//! This increment renders validated layout metadata. It intentionally does +//! not render PTY output; session screens must pass through Rover's terminal +//! emulator before they can be shown. + +use std::io::{self, Write}; +#[cfg(unix)] +use std::sync::mpsc::{self, Receiver, SyncSender}; +#[cfg(unix)] +use std::thread; +use std::time::{Duration, Instant}; + +use crossterm::cursor::{Hide, Show}; +use crossterm::event::{ + self, DisableBracketedPaste, EnableBracketedPaste, Event, KeyCode, KeyEvent, KeyEventKind, + KeyModifiers, +}; +use crossterm::execute; +use crossterm::terminal::{ + disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, +}; +use ratatui::backend::CrosstermBackend; +#[cfg(test)] +use ratatui::backend::TestBackend; +use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect}; +use ratatui::style::{Color, Modifier, Style}; +use ratatui::text::{Line, Span}; +use ratatui::widgets::{Block, Borders, Clear, Paragraph, Wrap}; +use ratatui::{Frame, Terminal}; +#[cfg(unix)] +use rover_agents::AgentSessionBinding; +use rover_agents::{ + bundled_manifest_for_executable, detect_agent, evaluate_herdr_manifest, + identify_foreground_process_group, AgentState, Confidence, DetectionEvidence, DetectionRequest, + DetectionResult, DetectionVisibility, ProcessArgvSample, ProcessSample, +}; +use rover_execution::layout::{Pane, PaneKind, PaneNode, Split, SplitAxis, Workspace}; +#[cfg(unix)] +use rover_execution::sessions::{SessionClient, SessionFrame, SessionReader, SessionWriter}; +use rover_execution::terminal::TerminalScreen; +#[cfg(unix)] +use rover_execution::terminal::TerminalSize; +#[cfg(unix)] +use rover_source::{ + BrowserEntry, BrowserEntryKind, BrowserGitStatus, BrowserListing, MAX_BROWSER_ENTRIES, +}; +#[cfg(unix)] +use rover_store::files::SafeDir; + +const MAX_SCREEN_TEXT_BYTES: usize = 8 * 1024 * 1024; +const MAX_TASK_BOARD_ITEMS: usize = 12; +const MAX_TASK_LOG_BYTES: usize = 64 * 1024; +const MAX_TASK_EVIDENCE_ITEMS: usize = 256; +const MAX_TASK_DIFF_ITEMS: usize = 256; +const MAX_TASK_DIFF_RECORDS: usize = 20_000; +const MAX_TASK_FILTER_BYTES: usize = 128; +#[cfg(unix)] +const MAX_PALETTE_QUERY_BYTES: usize = 96; +#[cfg(unix)] +/// Maximum UTF-8 bytes stored in one repository note. +pub const MAX_WORKSPACE_NOTES_BYTES: usize = 32 * 1024; +#[cfg(unix)] +/// Maximum number of notes stored for one repository. +pub const MAX_WORKSPACE_NOTES_COUNT: usize = 32; +#[cfg(unix)] +/// Maximum combined UTF-8 bytes stored across repository notes. +pub const MAX_WORKSPACE_NOTES_TOTAL_BYTES: usize = 128 * 1024; +#[cfg(unix)] +/// Maximum saved shell commands per repository. +pub const MAX_SAVED_COMMANDS: usize = 32; +#[cfg(unix)] +/// Maximum UTF-8 bytes in a saved command name. +pub const MAX_SAVED_COMMAND_NAME_BYTES: usize = 80; +#[cfg(unix)] +/// Maximum UTF-8 bytes in a saved shell command line. +pub const MAX_SAVED_COMMAND_LINE_BYTES: usize = 256; +#[cfg(unix)] +/// Maximum saved task briefing drafts for one repository. +pub const MAX_TASK_DRAFTS: usize = 32; +#[cfg(unix)] +/// Maximum UTF-8 bytes in a task draft title. +pub const MAX_TASK_DRAFT_TITLE_BYTES: usize = 120; +#[cfg(unix)] +/// Maximum UTF-8 bytes in task draft path globs. +pub const MAX_TASK_DRAFT_PATHS_BYTES: usize = 8 * 1024; +#[cfg(unix)] +/// Maximum UTF-8 bytes in task draft dependencies. +pub const MAX_TASK_DRAFT_DEPENDENCIES_BYTES: usize = 4 * 1024; +#[cfg(unix)] +/// Maximum UTF-8 bytes in a task draft quality-gate command. +pub const MAX_TASK_DRAFT_GATE_BYTES: usize = 1024; +#[cfg(unix)] +/// Maximum UTF-8 bytes in a task draft prompt. +pub const MAX_TASK_DRAFT_PROMPT_BYTES: usize = 16 * 1024; +#[cfg(unix)] +const MAX_FILE_BROWSER_ROWS: usize = 48; +#[cfg(unix)] +const MAX_FILE_QUERY_BYTES: usize = 128; +#[cfg(unix)] +const MAX_FILE_EDIT_BYTES: usize = 8 * 1024 * 1024; + +type TaskRefresh<'a> = dyn FnMut() -> io::Result> + 'a; +type TaskBlobLoader<'a> = dyn FnMut(&str) -> io::Result> + 'a; +type TaskCanceler<'a> = dyn FnMut(&str) -> io::Result<()> + 'a; +type TaskEvidenceLoader<'a> = + dyn FnMut(&TaskSummary) -> io::Result> + 'a; +type TaskDiffLoader<'a> = dyn FnMut(&TaskSummary) -> io::Result> + 'a; +type TaskViewPreferenceSaver<'a> = dyn FnMut(&TaskViewPreferences) -> io::Result<()> + 'a; +#[cfg(unix)] +type WorkspaceNotesSaver<'a> = dyn FnMut(&[String]) -> io::Result<()> + 'a; +#[cfg(unix)] +type SavedCommandsSaver<'a> = dyn FnMut(&[SavedCommand]) -> io::Result<()> + 'a; +#[cfg(unix)] +type TaskDraftsSaver<'a> = dyn FnMut(&[TaskDraft]) -> io::Result<()> + 'a; +#[cfg(unix)] +type TaskPlansLoader<'a> = dyn FnMut() -> io::Result> + 'a; +#[cfg(unix)] +type TaskPlanCreator<'a> = dyn FnMut(&TaskDraft) -> io::Result + 'a; +#[cfg(unix)] +type AgentSessionClearer<'a> = dyn FnMut(&str) -> io::Result<()> + 'a; + +#[cfg(unix)] +struct TaskPlanCallbacks<'a> { + summaries: Vec, + load: Box>, + create: Box>, +} + +#[cfg(unix)] +impl TaskPlanCallbacks<'_> { + fn unavailable() -> Self { + TaskPlanCallbacks { + summaries: Vec::new(), + load: Box::new(|| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "task plans are unavailable", + )) + }), + create: Box::new(|_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "task plan creation is unavailable", + )) + }), + } + } +} +#[cfg(unix)] +type FileBrowserLoader<'a> = dyn FnMut(&str, bool) -> io::Result + 'a; +#[cfg(unix)] +type QuickOpenLoader<'a> = dyn FnMut() -> io::Result + 'a; +#[cfg(unix)] +type FilePreviewLoader<'a> = dyn FnMut(&str) -> io::Result<(Vec, bool)> + 'a; +#[cfg(unix)] +type FileEditLoader<'a> = dyn FnMut(&str) -> io::Result> + 'a; +#[cfg(unix)] +type FileSaver<'a> = dyn FnMut(&str, &str, &[u8]) -> io::Result<()> + 'a; +#[cfg(unix)] +type ExternalFileEditor<'a> = dyn FnMut(&str, &[u8]) -> io::Result> + 'a; + +#[derive(Clone, Copy)] +struct TaskBoardView<'a> { + preferences: &'a TaskViewPreferences, + search_draft: Option<&'a str>, +} + +/// Persisted TUI task-board search and ordering preferences. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct TaskViewPreferences { + /// Case-insensitive substring matched against task ID, status, objective, + /// candidate, and task error. Limited to 128 UTF-8 bytes. + pub query: String, + /// Stable task-board ordering mode. + pub sort: TaskSortOrder, +} + +/// A user-authored, single-line shell command stored for one repository. +#[cfg(unix)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct SavedCommand { + /// User-visible command name. + pub name: String, + /// Exact shell input line sent after explicit run confirmation. + pub command: String, +} + +/// A local task briefing draft that has not been dispatched to a worker. +#[cfg(unix)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TaskDraft { + /// Short title shown in the draft list. + pub title: String, + /// Newline-separated repository-relative path globs. + pub paths: String, + /// Newline-separated IDs of prerequisite tasks. + pub dependencies: String, + /// Optional quality-gate command, stored as text and never run by draft handling. + pub quality_gate: String, + /// Multiline task briefing text. + pub prompt: String, +} + +/// Bounded display data for a durable task plan that has not been dispatched. +#[cfg(unix)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TaskPlanSummary { + pub id: String, + pub title: String, + pub status: String, + pub readiness: String, + pub dependency_count: usize, + pub attempt_count: usize, + pub updated_at: String, +} + +/// Validated, pane-bound native session offered for explicit TUI resume. +#[cfg(unix)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AgentSessionSummary { + /// Pane associated with the exact saved native session. + pub pane_id: String, + /// Native agent profile used to resume this session. + pub adapter: String, + /// Exact provider session ID. + pub session_id: String, + /// Origin recorded for the session identity. + pub source: String, + /// Interactive native command as argv; never shell-parsed here. + argv: Vec, +} + +#[cfg(unix)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +enum AgentSessionPanel { + #[default] + Closed, + Details, + ConfirmResume, + ConfirmClear, +} + +/// Deterministic task-board sort choices. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub enum TaskSortOrder { + /// Newest recorded update first, then task ID. + #[default] + Updated, + /// Status, then most recently updated, then task ID. + Status, + /// Objective, then task ID. + Objective, +} + +impl TaskSortOrder { + /// Parse the stable persisted spelling for this sort mode. + #[must_use] + pub fn parse(value: &str) -> Option { + match value { + "updated" => Some(Self::Updated), + "status" => Some(Self::Status), + "objective" => Some(Self::Objective), + _ => None, + } + } + + /// Return the stable persisted spelling for this sort mode. + #[must_use] + pub const fn as_str(self) -> &'static str { + match self { + Self::Updated => "updated", + Self::Status => "status", + Self::Objective => "objective", + } + } + + fn next(self) -> Self { + match self { + Self::Updated => Self::Status, + Self::Status => Self::Objective, + Self::Objective => Self::Updated, + } + } +} + +/// Read-only task data prepared from Rover's durable task records. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TaskSummary { + /// Stable Rover task ID. + pub id: String, + /// User-authored objective. + pub objective: String, + /// Persisted Rover lifecycle status. + pub status: String, + /// Last durable update timestamp. + pub updated_at: String, + /// Optional persisted task error. + pub error: Option, + /// Candidate snapshot or commit reference, when one exists. + pub candidate: Option, + /// Frozen base snapshot used to compare a task candidate. + pub base_snapshot: Option, + /// Investigation record linked by the task, if verification has started. + pub investigation_id: Option, + /// Number of durable execution attempts recorded. + pub attempt_count: usize, + /// Bounded process evidence from the most recent task execution. + pub process: Option, +} + +/// Stored investigation summary loaded lazily from the task's linked record. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TaskInvestigationEvidence { + /// Stable ID of the persisted investigation. + pub id: String, + /// Candidate snapshot that the recorded assessment covers. + pub candidate: String, + /// Persisted policy decision, such as `BLOCKED` or `READY`. + pub decision: String, + /// Human-readable reason stored with the decision. + pub decision_reason: String, + /// Individual checks recorded by the investigation. + pub checks: Vec, + /// Unresolved conditions recorded by the investigation. + pub unknowns: Vec, + /// Findings recorded by the investigation. + pub findings: Vec, +} + +/// One persisted check outcome; it is displayed as recorded evidence. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TaskCheckEvidence { + /// Stable check identifier. + pub id: String, + /// Recorded outcome, not a fresh check run. + pub outcome: String, + /// Explanation recorded for the check. + pub meaning: String, + /// Whether the check was required by the stored policy. + pub required: bool, + /// Number of tests reported by the check. + pub tests: i64, + /// Number of skipped tests reported by the check. + pub skipped: i64, +} + +/// A persisted finding associated with an investigation. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TaskEvidenceFinding { + /// Optional repository-relative finding path. + pub path: String, + /// Finding text from the stored investigation. + pub message: String, + /// Optional recorded severity label. + pub severity: Option, +} + +/// Bounded path-level changes between a task's retained base and candidate. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TaskDiffEvidence { + /// Stable base snapshot identity. + pub base_snapshot: String, + /// Stable candidate snapshot identity. + pub candidate: String, + /// Path-sorted changes from Rover's source comparison. + pub changes: Vec, +} + +/// One path-level status in a verified candidate comparison. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TaskDiffChange { + /// Repository-relative path. + pub path: String, + /// Added, deleted, or modified. + pub status: String, + /// Rover's filename-based risk category. + pub category: String, +} + +/// Task data and callbacks for the attached task workbench. +pub struct TaskProvider<'a> { + tasks: Vec, + refresh: Box>, + load_blob: Box>, + cancel_task: Box>, + load_evidence: Box>, + load_diff: Box>, + save_view_preferences: Box>, + view_preferences: TaskViewPreferences, + #[cfg(unix)] + workspace_notes: Vec, + #[cfg(unix)] + save_workspace_notes: Box>, + #[cfg(unix)] + saved_commands: Vec, + #[cfg(unix)] + save_saved_commands: Box>, + #[cfg(unix)] + task_drafts: Vec, + #[cfg(unix)] + save_task_drafts: Box>, + #[cfg(unix)] + task_plans: TaskPlanCallbacks<'a>, + #[cfg(unix)] + agent_session: Option, + #[cfg(unix)] + clear_agent_session: Box>, + #[cfg(unix)] + load_directory: Box>, + #[cfg(unix)] + load_quick_open: Box>, + #[cfg(unix)] + load_file_preview: Box>, + #[cfg(unix)] + load_file_for_edit: Box>, + #[cfg(unix)] + save_file: Box>, + #[cfg(unix)] + external_file_editor: Box>, + capabilities: TaskCapabilities, +} + +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +struct TaskCapabilities(u16); + +impl TaskCapabilities { + const REFRESH: u16 = 1 << 0; + const LOGS: u16 = 1 << 1; + const CANCEL: u16 = 1 << 2; + const EVIDENCE: u16 = 1 << 3; + const DIFF: u16 = 1 << 4; + const FILES: u16 = 1 << 5; + const FILE_EDIT: u16 = 1 << 6; + #[cfg(unix)] + const NOTES: u16 = 1 << 7; + #[cfg(unix)] + const SAVED_COMMANDS: u16 = 1 << 8; + #[cfg(unix)] + const TASK_DRAFTS: u16 = 1 << 9; + #[cfg(unix)] + const TASK_PLANS: u16 = 1 << 10; + #[cfg(unix)] + const AGENT_SESSION: u16 = 1 << 11; + + const ALL: Self = Self(Self::REFRESH | Self::LOGS | Self::CANCEL | Self::EVIDENCE | Self::DIFF); + + fn read_only(logs: bool) -> Self { + Self(if logs { Self::LOGS } else { 0 }) + } + + fn supports(self, capability: u16) -> bool { + self.0 & capability != 0 + } +} + +impl<'a> TaskProvider<'a> { + /// Create a task snapshot provider with refresh, blob, mutation, evidence, and diff callbacks. + pub fn new( + tasks: Vec, + refresh: impl FnMut() -> io::Result> + 'a, + load_blob: impl FnMut(&str) -> io::Result> + 'a, + cancel_task: impl FnMut(&str) -> io::Result<()> + 'a, + load_evidence: impl FnMut(&TaskSummary) -> io::Result> + 'a, + load_diff: impl FnMut(&TaskSummary) -> io::Result> + 'a, + ) -> Self { + Self { + tasks, + refresh: Box::new(refresh), + load_blob: Box::new(load_blob), + cancel_task: Box::new(cancel_task), + load_evidence: Box::new(load_evidence), + load_diff: Box::new(load_diff), + save_view_preferences: Box::new(|_| Ok(())), + view_preferences: TaskViewPreferences::default(), + #[cfg(unix)] + workspace_notes: Vec::new(), + #[cfg(unix)] + save_workspace_notes: Box::new(|_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "workspace notes are unavailable", + )) + }), + #[cfg(unix)] + saved_commands: Vec::new(), + #[cfg(unix)] + save_saved_commands: Box::new(|_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "saved commands are unavailable", + )) + }), + #[cfg(unix)] + task_drafts: Vec::new(), + #[cfg(unix)] + save_task_drafts: Box::new(|_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "task drafts are unavailable", + )) + }), + #[cfg(unix)] + task_plans: TaskPlanCallbacks::unavailable(), + #[cfg(unix)] + agent_session: None, + #[cfg(unix)] + clear_agent_session: Box::new(|_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "agent session removal is unavailable", + )) + }), + #[cfg(unix)] + load_directory: Box::new(|_, _| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "file browser unavailable", + )) + }), + #[cfg(unix)] + load_quick_open: Box::new(|| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "quick open unavailable", + )) + }), + #[cfg(unix)] + load_file_preview: Box::new(|_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "file preview unavailable", + )) + }), + #[cfg(unix)] + load_file_for_edit: Box::new(|_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "file editing unavailable", + )) + }), + #[cfg(unix)] + save_file: Box::new(|_, _, _| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "file saving unavailable", + )) + }), + #[cfg(unix)] + external_file_editor: Box::new(|_, _| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "external editor unavailable", + )) + }), + capabilities: TaskCapabilities::ALL, + } + } + + /// Install this repository's saved task-board view and its durable saver. + pub fn configure_view_preferences( + &mut self, + preferences: &TaskViewPreferences, + save: impl FnMut(&TaskViewPreferences) -> io::Result<()> + 'a, + ) { + self.view_preferences = TaskViewPreferences { + query: truncate_utf8(&preferences.query, MAX_TASK_FILTER_BYTES), + sort: preferences.sort, + }; + self.save_view_preferences = Box::new(save); + } + + /// Install a bounded repository-scoped note collection and its durable saver. + #[cfg(unix)] + pub fn configure_workspace_notes( + &mut self, + notes: &[String], + save: impl FnMut(&[String]) -> io::Result<()> + 'a, + ) { + self.workspace_notes = bounded_workspace_notes(notes); + self.save_workspace_notes = Box::new(save); + self.capabilities.0 |= TaskCapabilities::NOTES; + } + + /// Install bounded repository-scoped shell commands and their durable saver. + #[cfg(unix)] + pub fn configure_saved_commands( + &mut self, + commands: &[SavedCommand], + save: impl FnMut(&[SavedCommand]) -> io::Result<()> + 'a, + ) { + self.saved_commands = bounded_saved_commands(commands); + self.save_saved_commands = Box::new(save); + self.capabilities.0 |= TaskCapabilities::SAVED_COMMANDS; + } + + /// Install this repository's saved task briefing drafts and durable saver. + #[cfg(unix)] + pub fn configure_task_drafts( + &mut self, + drafts: &[TaskDraft], + save: impl FnMut(&[TaskDraft]) -> io::Result<()> + 'a, + ) { + self.task_drafts = bounded_task_drafts(drafts); + self.save_task_drafts = Box::new(save); + self.capabilities.0 |= TaskCapabilities::TASK_DRAFTS; + } + + /// Install this repository's plan snapshot and draft-to-plan creator. + #[cfg(unix)] + pub fn configure_task_plans( + &mut self, + plans: &[TaskPlanSummary], + load: impl FnMut() -> io::Result> + 'a, + create: impl FnMut(&TaskDraft) -> io::Result + 'a, + ) { + self.task_plans = TaskPlanCallbacks { + summaries: bounded_task_plan_summaries(plans), + load: Box::new(load), + create: Box::new(create), + }; + self.capabilities.0 |= TaskCapabilities::TASK_PLANS; + } + + /// Offer one exact native session in the TUI with explicit resume/clear confirmations. + /// + /// # Errors + /// + /// Returns an error if the binding cannot produce a reviewed interactive + /// resume command for its adapter. + #[cfg(unix)] + pub fn configure_agent_session( + &mut self, + binding: &AgentSessionBinding, + clear: impl FnMut(&str) -> io::Result<()> + 'a, + ) -> io::Result<()> { + let argv = binding + .resume_tui_argv() + .map_err(|error| io::Error::new(io::ErrorKind::InvalidInput, error.to_string()))?; + self.agent_session = Some(AgentSessionSummary { + pane_id: binding.pane_id().to_owned(), + adapter: binding.adapter().to_owned(), + session_id: binding.session_id().to_owned(), + source: binding.source().as_str().to_owned(), + argv, + }); + self.clear_agent_session = Box::new(clear); + self.capabilities.0 |= TaskCapabilities::AGENT_SESSION; + Ok(()) + } + + /// Install read-only repository file-tree, quick-open, and preview loaders. + #[cfg(unix)] + pub fn configure_file_browser( + &mut self, + load_directory: impl FnMut(&str, bool) -> io::Result + 'a, + load_quick_open: impl FnMut() -> io::Result + 'a, + load_file_preview: impl FnMut(&str) -> io::Result<(Vec, bool)> + 'a, + ) { + self.load_directory = Box::new(load_directory); + self.load_quick_open = Box::new(load_quick_open); + self.load_file_preview = Box::new(load_file_preview); + self.capabilities.0 |= TaskCapabilities::FILES; + } + + /// Install bounded inline-editor readers and the repository's safe saver. + #[cfg(unix)] + pub fn configure_file_editor( + &mut self, + load_file: impl FnMut(&str) -> io::Result> + 'a, + save_file: impl FnMut(&str, &str, &[u8]) -> io::Result<()> + 'a, + external_editor: impl FnMut(&str, &[u8]) -> io::Result> + 'a, + ) { + self.load_file_for_edit = Box::new(load_file); + self.save_file = Box::new(save_file); + self.external_file_editor = Box::new(external_editor); + self.capabilities.0 |= TaskCapabilities::FILE_EDIT; + } + + #[allow(clippy::too_many_lines)] // Keep the unsupported read-only callback wiring explicit. + fn read_only_snapshot( + tasks: Vec, + load_blob: impl FnMut(&str) -> io::Result> + 'a, + logs: bool, + ) -> Self { + Self { + tasks, + refresh: Box::new(|| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "task refresh is unavailable", + )) + }), + load_blob: Box::new(load_blob), + cancel_task: Box::new(|_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "task cancellation is unavailable", + )) + }), + load_evidence: Box::new(|_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "task evidence is unavailable", + )) + }), + load_diff: Box::new(|_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "task diff is unavailable", + )) + }), + save_view_preferences: Box::new(|_| Ok(())), + view_preferences: TaskViewPreferences::default(), + #[cfg(unix)] + workspace_notes: Vec::new(), + #[cfg(unix)] + save_workspace_notes: Box::new(|_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "workspace notes are unavailable", + )) + }), + #[cfg(unix)] + saved_commands: Vec::new(), + #[cfg(unix)] + save_saved_commands: Box::new(|_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "saved commands are unavailable", + )) + }), + #[cfg(unix)] + task_drafts: Vec::new(), + #[cfg(unix)] + save_task_drafts: Box::new(|_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "task drafts are unavailable", + )) + }), + #[cfg(unix)] + task_plans: TaskPlanCallbacks::unavailable(), + #[cfg(unix)] + agent_session: None, + #[cfg(unix)] + clear_agent_session: Box::new(|_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "agent session removal is unavailable", + )) + }), + #[cfg(unix)] + load_directory: Box::new(|_, _| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "file browser unavailable", + )) + }), + #[cfg(unix)] + load_quick_open: Box::new(|| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "quick open unavailable", + )) + }), + #[cfg(unix)] + load_file_preview: Box::new(|_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "file preview unavailable", + )) + }), + #[cfg(unix)] + load_file_for_edit: Box::new(|_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "file editing unavailable", + )) + }), + #[cfg(unix)] + save_file: Box::new(|_, _, _| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "file saving unavailable", + )) + }), + #[cfg(unix)] + external_file_editor: Box::new(|_, _| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "external editor unavailable", + )) + }), + capabilities: TaskCapabilities::read_only(logs), + } + } +} + +/// Process result metadata and output hashes retained with one task. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TaskProcessEvidence { + /// Child exit code. + pub exit_code: i64, + /// Optional execution error. + pub error: Option, + /// Whether the process exceeded its time limit. + pub timed_out: bool, + /// Whether Rover cancelled the process. + pub cancelled: bool, + /// Whether captured output exceeded Rover's retained output cap. + pub truncated: bool, + /// Total observed stdout bytes. + pub stdout_bytes: i64, + /// Total observed stderr bytes. + pub stderr_bytes: i64, + /// SHA-256 digest recorded by Rover for captured stdout. + pub stdout_sha256: String, + /// SHA-256 digest recorded by Rover for captured stderr. + pub stderr_sha256: String, +} + +/// Filtered screen text associated with a pane. +/// +/// Construct values from [`TerminalScreen`]; raw PTY bytes do not have a +/// constructor in this crate and must be processed by Rover's emulator first. +pub struct ScreenText { + pane_id: String, + text: String, +} + +impl ScreenText { + /// Capture bounded, control-filtered screen text for this pane. + #[must_use] + pub fn from_terminal_screen(pane: &Pane, screen: &TerminalScreen) -> Self { + Self { + pane_id: pane.id().to_owned(), + text: screen.text(), + } + } +} + +/// Render one frame from validated workspace metadata. +pub fn render_workspace(frame: &mut Frame<'_>, workspace: &Workspace) { + render_workspace_with_screens(frame, workspace, &[]); +} + +/// Render workspace metadata and screens previously parsed by Rover's +/// bounded terminal emulator. +pub fn render_workspace_with_screens( + frame: &mut Frame<'_>, + workspace: &Workspace, + screens: &[ScreenText], +) { + render_workspace_mode(frame, workspace, screens, false, false); +} + +fn render_workspace_mode( + frame: &mut Frame<'_>, + workspace: &Workspace, + screens: &[ScreenText], + attached: bool, + help_open: bool, +) { + let task_capabilities = attached.then_some(TaskCapabilities::ALL); + render_workspace_mode_for_provider( + frame, + workspace, + screens, + attached, + help_open, + task_capabilities, + None, + ); +} + +fn render_workspace_mode_for_provider( + frame: &mut Frame<'_>, + workspace: &Workspace, + screens: &[ScreenText], + attached: bool, + help_open: bool, + task_capabilities: Option, + agent_detection: Option<&DetectionResult>, +) { + let area = frame.area(); + let sections = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Length(1), + Constraint::Min(1), + Constraint::Length(1), + ]) + .split(area); + + render_tabs(frame, sections[0], workspace); + render_node( + frame, + workspace.active_tab().render_root(), + sections[1], + workspace.active_tab().active_pane(), + screens, + ); + let footer = if attached { + let mut spans = vec![ + Span::styled("Ctrl-]", Style::default().fg(Color::Yellow)), + Span::raw(" detach "), + Span::styled("Ctrl-B", Style::default().fg(Color::Yellow)), + Span::raw(" then q quit, t tasks, ? help"), + ]; + if let Some(detection) = agent_detection { + spans.push(Span::raw(" Agent: ")); + spans.push(Span::styled( + detection.agent.as_deref().unwrap_or("unknown"), + Style::default().fg(Color::Cyan), + )); + spans.push(Span::raw(format!( + " · {}", + agent_state_label(detection.state) + ))); + } + Paragraph::new(Line::from(spans)) + } else { + Paragraph::new(Line::from(vec![ + Span::styled("q", Style::default().fg(Color::Yellow)), + Span::raw(" quit "), + Span::styled("Tab", Style::default().fg(Color::Yellow)), + Span::raw(" next pane "), + Span::styled("n", Style::default().fg(Color::Yellow)), + Span::raw(" new tab "), + Span::styled("| / -", Style::default().fg(Color::Yellow)), + Span::raw(" split "), + Span::styled("z", Style::default().fg(Color::Yellow)), + Span::raw(" zoom "), + Span::styled("x", Style::default().fg(Color::Yellow)), + Span::raw(" close pane "), + Span::styled("?", Style::default().fg(Color::Yellow)), + Span::raw(" help"), + ])) + }; + frame.render_widget(footer, sections[2]); + + render_pane_popup(frame, area, workspace.active_tab().popup_pane().is_some()); + if help_open && area.width >= 20 && area.height >= 8 { + let popup = centered_rect(76, 76, area); + frame.render_widget(Clear, popup); + let mut lines = vec![ + Line::from("Workspace"), + Line::from("Tab / Shift-Tab focus next / previous pane"), + Line::from("n create a scratch tab"), + Line::from("[ / ] previous / next tab"), + Line::from("| / - split horizontally / vertically"), + Line::from("z toggle pane zoom"), + Line::from("p toggle pane popup"), + Line::from("x close focused pane"), + Line::from("q or Ctrl-C quit Rover TUI"), + Line::from("? or Esc close this help"), + ]; + if attached { + lines.extend(attached_help_lines(task_capabilities)); + } + frame.render_widget( + Paragraph::new(lines).block( + Block::default() + .title(" Rover keyboard help ") + .borders(Borders::ALL), + ), + popup, + ); + } +} + +fn attached_help_lines(capabilities: Option) -> Vec> { + let mut lines = vec![ + Line::from("Attached session"), + Line::from("Ctrl-B then t open the task board"), + Line::from("Ctrl-B then p open the command palette"), + Line::from("Ctrl-] detach and leave the session running"), + Line::from("Ctrl-B then key run a Rover workspace shortcut"), + ]; + if let Some(capabilities) = capabilities { + lines.push(Line::from("Enter inspect selected task")); + #[cfg(unix)] + if capabilities.supports(TaskCapabilities::NOTES) { + lines.push(Line::from("Ctrl-B then n open repository notes")); + } + #[cfg(unix)] + if capabilities.supports(TaskCapabilities::SAVED_COMMANDS) { + lines.push(Line::from("Ctrl-B then k manage saved shell commands")); + } + #[cfg(unix)] + if capabilities.supports(TaskCapabilities::TASK_DRAFTS) { + lines.push(Line::from( + "Ctrl-B then d manage local task briefing drafts", + )); + } + #[cfg(unix)] + if capabilities.supports(TaskCapabilities::AGENT_SESSION) { + lines.push(Line::from( + "Ctrl-B then a inspect or resume saved agent session", + )); + } + if capabilities.supports(TaskCapabilities::FILES) { + lines.extend([ + Line::from("Ctrl-B then f open the repository file tree"), + Line::from("Ctrl-B then . open hidden-file quick open"), + Line::from("In files: h toggles hidden · . quick open · r refresh"), + ]); + } + if capabilities.supports(TaskCapabilities::FILE_EDIT) { + lines.push(Line::from( + "File preview: e edit · Ctrl-S save · Ctrl-D diff · Ctrl-E external editor", + )); + } + if capabilities.supports(TaskCapabilities::REFRESH) { + lines.push(Line::from("r refresh project tasks")); + } + if capabilities.supports(TaskCapabilities::CANCEL) { + lines.push(Line::from("c cancel task (confirmation)")); + } + if capabilities.supports(TaskCapabilities::EVIDENCE) { + lines.push(Line::from("i inspect recorded investigation")); + } + if capabilities.supports(TaskCapabilities::DIFF) { + lines.push(Line::from( + "d inspect verified candidate changes", + )); + } + if capabilities.supports(TaskCapabilities::LOGS) { + lines.extend([ + Line::from("o / e open verified stdout / stderr"), + Line::from("↑/↓, PgUp/PgDn scroll task output"), + ]); + } + } + lines +} + +fn render_pane_popup(frame: &mut Frame<'_>, area: Rect, open: bool) { + if !open || area.width < 8 || area.height < 5 { + return; + } + let popup = centered_rect(40, 20, area); + frame.render_widget(Clear, popup); + frame.render_widget( + Paragraph::new("Pane actions are controlled by the keyboard") + .alignment(Alignment::Center) + .block(Block::default().title(" Pane ").borders(Borders::ALL)), + popup, + ); +} + +#[cfg(test)] +fn render_task_board( + frame: &mut Frame<'_>, + tasks: &[TaskSummary], + selected: usize, + detail_open: bool, +) { + render_task_board_with_refresh( + frame, + tasks, + selected, + detail_open, + None, + TaskCapabilities::ALL, + TaskBoardView { + preferences: &TaskViewPreferences::default(), + search_draft: None, + }, + ); +} + +fn render_task_board_with_refresh( + frame: &mut Frame<'_>, + tasks: &[TaskSummary], + selected: usize, + detail_open: bool, + refresh_message: Option<&str>, + capabilities: TaskCapabilities, + view: TaskBoardView<'_>, +) { + let area = frame.area(); + if area.width < 24 || area.height < 8 { + return; + } + let popup = centered_rect(86, 82, area); + frame.render_widget(Clear, popup); + if detail_open { + render_task_detail(frame, popup, tasks.get(selected), capabilities); + if let Some(message) = refresh_message { + render_task_refresh_message(frame, popup, message); + } + return; + } + render_task_list( + frame, + popup, + tasks, + selected, + refresh_message, + capabilities, + view, + ); +} + +fn render_task_list( + frame: &mut Frame<'_>, + popup: Rect, + tasks: &[TaskSummary], + selected: usize, + refresh_message: Option<&str>, + capabilities: TaskCapabilities, + view: TaskBoardView<'_>, +) { + let preferences = view.preferences; + let visible_indices = task_visible_indices(tasks, preferences); + let reserved_rows = 6 + u16::from(view.search_draft.is_some()); + let rows_available = usize::from(popup.height.saturating_sub(reserved_rows)).max(1); + let visible = visible_indices + .len() + .min(MAX_TASK_BOARD_ITEMS) + .min(rows_available); + let selected_position = visible_indices + .iter() + .position(|index| *index == selected) + .unwrap_or(0); + let start = if visible_indices.len() > visible { + selected_position + .saturating_sub(visible / 2) + .min(visible_indices.len().saturating_sub(visible)) + } else { + 0 + }; + let mut lines = vec![Line::from(format!( + "{} project tasks · sort: {} · search: {}", + visible_indices.len(), + preferences.sort.as_str(), + if preferences.query.is_empty() { + "all" + } else { + &preferences.query + } + ))]; + if let Some(draft) = view.search_draft { + lines.push(Line::from(format!("Search: {draft}_"))); + } + if tasks.is_empty() { + lines.push(Line::from("No task records for this project.")); + } else if visible_indices.is_empty() { + lines.push(Line::from("No tasks match this search.")); + } else { + for index in visible_indices.iter().copied().skip(start).take(visible) { + let task = &tasks[index]; + let marker = if index == selected { "> " } else { " " }; + let id = bounded_display_text(&task.id, 24); + let status = bounded_display_text(&task.status, 18); + let objective = bounded_display_text(&task.objective, 72); + let style = if index == selected { + Style::default() + .fg(Color::Black) + .bg(Color::Cyan) + .add_modifier(Modifier::BOLD) + } else { + Style::default() + }; + lines.push(Line::from(vec![ + Span::styled(marker, style), + Span::styled(format!("{status:<18}"), style), + Span::styled(format!("{id:<24}"), style), + Span::styled(objective, style), + ])); + } + if visible_indices.len() > visible { + lines.push(Line::from(format!( + "Showing {}–{} of {} tasks.", + start + 1, + start + visible, + visible_indices.len() + ))); + } + if let Some(task) = tasks.get(selected) { + lines.push(Line::from(format!( + "Updated: {}", + bounded_display_text(&task.updated_at, 64) + ))); + } + } + if let Some(message) = refresh_message { + lines.push(Line::from(bounded_display_text(message, 100))); + } + let mut footer = String::from("↑/↓ select / search s sort"); + if capabilities.supports(TaskCapabilities::REFRESH) { + footer.push_str(" r refresh"); + } + if capabilities.supports(TaskCapabilities::CANCEL) { + footer.push_str(" c cancel"); + } + footer.push_str(" Esc or t close"); + lines.push(Line::from(footer)); + frame.render_widget( + Paragraph::new(lines).block( + Block::default() + .title(" Rover task board · read-only snapshot ") + .borders(Borders::ALL), + ), + popup, + ); +} + +fn render_task_refresh_message(frame: &mut Frame<'_>, popup: Rect, message: &str) { + let footer_area = Rect::new( + popup.x.saturating_add(2), + popup.y.saturating_add(popup.height.saturating_sub(2)), + popup.width.saturating_sub(4), + 1, + ); + frame.render_widget( + Paragraph::new(bounded_display_text(message, 100)) + .style(Style::default().fg(Color::Yellow)), + footer_area, + ); +} + +fn render_task_detail( + frame: &mut Frame<'_>, + popup: Rect, + task: Option<&TaskSummary>, + capabilities: TaskCapabilities, +) { + let Some(task) = task else { + frame.render_widget( + Paragraph::new("No task is selected.").block( + Block::default() + .title(" Rover task detail ") + .borders(Borders::ALL), + ), + popup, + ); + return; + }; + let mut lines = vec![ + Line::from(format!("ID: {}", bounded_display_text(&task.id, 80))), + Line::from(format!( + "Status: {}", + bounded_display_text(&task.status, 48) + )), + Line::from(format!( + "Updated: {}", + bounded_display_text(&task.updated_at, 80) + )), + Line::from(format!("Attempts: {}", task.attempt_count)), + Line::from(format!( + "Objective: {}", + bounded_display_text(&task.objective, 240) + )), + ]; + if let Some(candidate) = &task.candidate { + lines.push(Line::from(format!( + "Candidate: {}", + bounded_display_text(candidate, 120) + ))); + } + if let Some(error) = &task.error { + lines.push(Line::from(format!( + "Task error: {}", + bounded_display_text(error, 240) + ))); + } + if let Some(process) = &task.process { + lines.extend([ + Line::from(format!( + "Process: exit {} · timed out: {} · cancelled: {} · truncated: {}", + process.exit_code, process.timed_out, process.cancelled, process.truncated + )), + Line::from(format!( + "Stdout observed: {} bytes · recorded SHA-256 {}", + process.stdout_bytes, + bounded_display_text(&process.stdout_sha256, 64) + )), + Line::from(format!( + "Stderr observed: {} bytes · recorded SHA-256 {}", + process.stderr_bytes, + bounded_display_text(&process.stderr_sha256, 64) + )), + ]); + if let Some(error) = &process.error { + lines.push(Line::from(format!( + "Process error: {}", + bounded_display_text(error, 240) + ))); + } + } else { + lines.push(Line::from("No process result is recorded.")); + } + let mut footer = Vec::new(); + if capabilities.supports(TaskCapabilities::LOGS) { + footer.push("o stdout · e stderr"); + } + if capabilities.supports(TaskCapabilities::REFRESH) { + footer.push("r refresh"); + } + if capabilities.supports(TaskCapabilities::CANCEL) { + footer.push("c cancel"); + } + if capabilities.supports(TaskCapabilities::EVIDENCE) { + footer.push("i evidence"); + } + if capabilities.supports(TaskCapabilities::DIFF) { + footer.push("d diff"); + } + footer.push("Esc or Enter returns"); + lines.push(Line::from(footer.join(" · "))); + frame.render_widget( + Paragraph::new(lines).wrap(Wrap { trim: true }).block( + Block::default() + .title(" Rover task detail · recorded metadata ") + .borders(Borders::ALL), + ), + popup, + ); +} + +fn prepare_task_log(bytes: &[u8], expected_digest: &str) -> Result<(String, bool), String> { + let expected = rover_core::Sha256Digest::parse_hex(expected_digest) + .map_err(|_| "Recorded output digest is invalid.".to_owned())?; + if rover_core::Sha256Digest::of(bytes) != expected { + return Err("Output content does not match its recorded SHA-256.".to_owned()); + } + let clipped = bytes.len() > MAX_TASK_LOG_BYTES; + let shown = &bytes[..bytes.len().min(MAX_TASK_LOG_BYTES)]; + let mut sanitized = sanitize_screen_text(&String::from_utf8_lossy(shown)); + if clipped { + sanitized.push_str("\n\n[Output clipped at 64 KiB for TUI display.]\n"); + } + Ok((sanitized, true)) +} + +fn render_task_log(frame: &mut Frame<'_>, stream: &str, text: &str, scroll: u16, verified: bool) { + let title = if verified { + format!(" Rover task {stream} · SHA-256 verified ") + } else { + format!(" Rover task {stream} · unavailable ") + }; + let content = if text.is_empty() { + "(empty output)" + } else { + text + }; + frame.render_widget( + Paragraph::new(content) + .wrap(Wrap { trim: false }) + .scroll((scroll, 0)) + .block(Block::default().title(title).borders(Borders::ALL)), + centered_rect(86, 82, frame.area()), + ); +} + +fn bounded_display_text(value: &str, max_chars: usize) -> String { + let mut output = String::new(); + for character in value.chars() { + if output.chars().count() >= max_chars { + output.push('…'); + break; + } + if !character.is_control() && !is_bidi_format(character) { + output.push(character); + } + } + output +} + +fn truncate_utf8(value: &str, max_bytes: usize) -> String { + let mut end = value.len().min(max_bytes); + while !value.is_char_boundary(end) { + end -= 1; + } + value[..end].to_owned() +} + +#[cfg(unix)] +fn bounded_workspace_notes(notes: &[String]) -> Vec { + let mut total = 0usize; + notes + .iter() + .take(MAX_WORKSPACE_NOTES_COUNT) + .map(|note| { + truncate_utf8( + &sanitize_screen_text(&truncate_utf8(note, MAX_WORKSPACE_NOTES_BYTES)), + MAX_WORKSPACE_NOTES_BYTES, + ) + }) + .take_while(|note| { + let next = total.saturating_add(note.len()); + if next > MAX_WORKSPACE_NOTES_TOTAL_BYTES { + false + } else { + total = next; + true + } + }) + .collect() +} + +#[cfg(unix)] +fn saved_command_is_valid(command: &SavedCommand) -> bool { + !command.name.trim().is_empty() + && command.name.len() <= MAX_SAVED_COMMAND_NAME_BYTES + && command.command.len() <= MAX_SAVED_COMMAND_LINE_BYTES + && !command.command.trim().is_empty() + && command + .name + .chars() + .chain(command.command.chars()) + .all(|character| !character.is_control() && !is_bidi_format(character)) + && !command.command.contains('\n') + && !command.command.contains('\r') +} + +#[cfg(unix)] +fn bounded_saved_commands(commands: &[SavedCommand]) -> Vec { + let mut bounded = Vec::new(); + for command in commands { + if bounded.len() == MAX_SAVED_COMMANDS { + break; + } + if saved_command_is_valid(command) + && !bounded + .iter() + .any(|saved: &SavedCommand| saved.name == command.name) + { + bounded.push(command.clone()); + } + } + bounded +} + +#[cfg(unix)] +fn task_draft_is_valid(draft: &TaskDraft) -> bool { + draft.title.len() <= MAX_TASK_DRAFT_TITLE_BYTES + && draft.paths.len() <= MAX_TASK_DRAFT_PATHS_BYTES + && draft.dependencies.len() <= MAX_TASK_DRAFT_DEPENDENCIES_BYTES + && draft.quality_gate.len() <= MAX_TASK_DRAFT_GATE_BYTES + && draft.prompt.len() <= MAX_TASK_DRAFT_PROMPT_BYTES + && draft + .title + .chars() + .all(|character| !character.is_control() && !is_bidi_format(character)) + && [ + draft.paths.as_str(), + draft.dependencies.as_str(), + draft.quality_gate.as_str(), + draft.prompt.as_str(), + ] + .into_iter() + .all(|value| { + value.chars().all(|character| { + (character == '\n' || !character.is_control()) && !is_bidi_format(character) + }) + }) +} + +#[cfg(unix)] +fn bounded_task_drafts(drafts: &[TaskDraft]) -> Vec { + drafts + .iter() + .take(MAX_TASK_DRAFTS) + .filter(|draft| task_draft_is_valid(draft)) + .cloned() + .collect() +} + +#[cfg(unix)] +fn bounded_task_plan_summaries(plans: &[TaskPlanSummary]) -> Vec { + plans + .iter() + .take(1_000) + .filter(|plan| { + !plan.id.is_empty() + && plan.id.len() <= 128 + && plan.title.len() <= MAX_TASK_DRAFT_TITLE_BYTES + && plan.status.len() <= 24 + && plan.readiness.len() <= 24 + && plan.updated_at.len() <= 64 + && [ + plan.id.as_str(), + plan.title.as_str(), + plan.status.as_str(), + plan.readiness.as_str(), + plan.updated_at.as_str(), + ] + .into_iter() + .all(|value| { + value + .chars() + .all(|character| !character.is_control() && !is_bidi_format(character)) + }) + }) + .cloned() + .collect() +} + +fn render_tabs(frame: &mut Frame<'_>, area: Rect, workspace: &Workspace) { + let active_id = workspace.active_tab().id(); + let mut spans = vec![ + Span::styled( + format!(" {} ", workspace.title()), + Style::default() + .fg(Color::White) + .add_modifier(Modifier::BOLD), + ), + Span::raw("│ "), + ]; + spans.extend(workspace.tabs().iter().flat_map(|tab| { + let style = if tab.id() == active_id { + Style::default() + .fg(Color::Cyan) + .add_modifier(Modifier::BOLD) + } else { + Style::default().fg(Color::Gray) + }; + [ + Span::styled(format!(" {} ", tab.title()), style), + Span::raw(" "), + ] + })); + frame.render_widget(Paragraph::new(Line::from(spans)), area); +} + +fn render_node( + frame: &mut Frame<'_>, + node: &PaneNode, + area: Rect, + active_pane: &str, + screens: &[ScreenText], +) { + match node { + PaneNode::Leaf(pane) => render_pane( + frame, + pane, + area, + active_pane, + screens + .iter() + .find(|screen| screen.pane_id == pane.id()) + .map(|screen| screen.text.as_str()), + ), + PaneNode::Split(split) => render_split(frame, split, area, active_pane, screens), + } +} + +fn render_split( + frame: &mut Frame<'_>, + split: &Split, + area: Rect, + active_pane: &str, + screens: &[ScreenText], +) { + let direction = match split.axis() { + SplitAxis::Horizontal => Direction::Horizontal, + SplitAxis::Vertical => Direction::Vertical, + }; + let first_ratio = u32::from(split.first_basis_points()); + let children = Layout::default() + .direction(direction) + .constraints([ + Constraint::Ratio(first_ratio, 10_000), + Constraint::Ratio(10_000 - first_ratio, 10_000), + ]) + .split(area); + render_node(frame, split.first(), children[0], active_pane, screens); + render_node(frame, split.second(), children[1], active_pane, screens); +} + +fn render_pane( + frame: &mut Frame<'_>, + pane: &Pane, + area: Rect, + active_pane: &str, + screen_text: Option<&str>, +) { + let title = match pane.kind() { + PaneKind::Session { name } => format!(" Session: {name} "), + PaneKind::Scratch => " Scratch ".to_owned(), + }; + let active = pane.id() == active_pane; + let border = if active { Color::Cyan } else { Color::DarkGray }; + let content = screen_text.map_or_else( + || { + if matches!(pane.kind(), PaneKind::Session { .. }) { + "Waiting for a terminal screen".to_owned() + } else { + "Scratch pane".to_owned() + } + }, + sanitize_screen_text, + ); + let visible_lines = usize::from(area.height.saturating_sub(2)); + let lines = content + .lines() + .skip(content.lines().count().saturating_sub(visible_lines)) + .map(|line| Line::raw(line.to_owned())) + .collect::>(); + frame.render_widget( + Paragraph::new(lines).block( + Block::default() + .title(title) + .borders(Borders::ALL) + .border_style(Style::default().fg(border)), + ), + area, + ); +} + +fn sanitize_screen_text(text: &str) -> String { + let mut output = String::with_capacity(text.len().min(MAX_SCREEN_TEXT_BYTES)); + for character in text.chars() { + let replacement = + if (character.is_control() && character != '\n') || is_bidi_format(character) { + '\u{fffd}' + } else { + character + }; + if output.len() + replacement.len_utf8() > MAX_SCREEN_TEXT_BYTES { + break; + } + output.push(replacement); + } + output +} + +fn is_bidi_format(character: char) -> bool { + matches!( + character, + '\u{061c}' + | '\u{200e}' + | '\u{200f}' + | '\u{202a}'..='\u{202e}' + | '\u{2066}'..='\u{2069}' + | '\u{feff}' + ) +} + +fn centered_rect(width_percent: u16, height_percent: u16, area: Rect) -> Rect { + let vertical = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Percentage((100 - height_percent) / 2), + Constraint::Percentage(height_percent), + Constraint::Percentage((100 - height_percent) / 2), + ]) + .split(area); + Layout::default() + .direction(Direction::Horizontal) + .constraints([ + Constraint::Percentage((100 - width_percent) / 2), + Constraint::Percentage(width_percent), + Constraint::Percentage((100 - width_percent) / 2), + ]) + .split(vertical[1])[1] +} + +/// Apply a key press. Returns `true` to leave the interactive loop. +pub fn handle_key(workspace: &mut Workspace, key: KeyEvent) -> bool { + if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat { + return false; + } + if key.code == KeyCode::Char('q') + || (key.code == KeyCode::Char('c') && key.modifiers.contains(KeyModifiers::CONTROL)) + { + return true; + } + + match key.code { + KeyCode::Esc => { + if workspace.active_tab().popup_pane().is_some() { + workspace.close_popup(); + } + } + KeyCode::Tab => focus_next_pane(workspace, true), + KeyCode::BackTab => focus_next_pane(workspace, false), + KeyCode::Char('n') => { + let number = workspace.tabs().len() + 1; + let _ = workspace.add_tab( + format!("scratch-{number}"), + rover_execution::layout::PaneSpec::scratch(), + ); + } + KeyCode::Char('[') => focus_neighbor_tab(workspace, false), + KeyCode::Char(']') => focus_neighbor_tab(workspace, true), + KeyCode::Char('|') => { + let _ = workspace.split_active( + SplitAxis::Horizontal, + 5_000, + rover_execution::layout::PaneSpec::scratch(), + ); + } + KeyCode::Char('-') => { + let _ = workspace.split_active( + SplitAxis::Vertical, + 5_000, + rover_execution::layout::PaneSpec::scratch(), + ); + } + KeyCode::Char('x') => { + let pane = workspace.active_tab().active_pane().to_owned(); + let _ = workspace.close_pane(&pane); + } + KeyCode::Char('z') => workspace.toggle_zoom(), + KeyCode::Char('p') => { + if workspace.active_tab().popup_pane().is_some() { + workspace.close_popup(); + } else { + workspace.open_popup(); + } + } + _ => {} + } + false +} + +fn handle_help_key(help_open: &mut bool, key: KeyEvent) -> bool { + if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat { + return false; + } + if *help_open { + if matches!(key.code, KeyCode::Char('?') | KeyCode::Esc) { + *help_open = false; + } + return true; + } + if key.code == KeyCode::Char('?') { + *help_open = true; + return true; + } + false +} + +#[cfg(unix)] +fn attached_help_key(help_open: &mut bool, prefix: &mut bool, key: KeyEvent) -> bool { + if *help_open { + return handle_help_key(help_open, key); + } + if *prefix && key.kind != KeyEventKind::Release && key.code == KeyCode::Char('?') { + *prefix = false; + *help_open = true; + return true; + } + false +} + +fn focus_next_pane(workspace: &mut Workspace, forward: bool) { + let active = workspace.active_tab(); + let mut ids = Vec::new(); + collect_pane_ids(active.root(), &mut ids); + if ids.len() < 2 { + return; + } + let current = ids + .iter() + .position(|id| id == active.active_pane()) + .unwrap_or(0); + let next = if forward { + (current + 1) % ids.len() + } else { + (current + ids.len() - 1) % ids.len() + }; + let _ = workspace.focus_pane(&ids[next]); +} + +fn focus_neighbor_tab(workspace: &mut Workspace, forward: bool) { + let tabs = workspace.tabs(); + if tabs.len() < 2 { + return; + } + let current = tabs + .iter() + .position(|tab| tab.id() == workspace.active_tab().id()) + .unwrap_or(0); + let next = if forward { + (current + 1) % tabs.len() + } else { + (current + tabs.len() - 1) % tabs.len() + }; + let id = tabs[next].id().to_owned(); + let _ = workspace.focus_tab(&id); +} + +fn collect_pane_ids(node: &PaneNode, ids: &mut Vec) { + match node { + PaneNode::Leaf(pane) => ids.push(pane.id().to_owned()), + PaneNode::Split(split) => { + collect_pane_ids(split.first(), ids); + collect_pane_ids(split.second(), ids); + } + } +} + +/// Run Rover's interactive workspace shell and restore terminal state on exit. +/// +/// # Errors +/// +/// Returns an I/O error if raw mode, terminal setup, event polling, rendering, +/// or input reading fails. Terminal state restoration is attempted on return. +pub fn run(workspace: Workspace) -> io::Result<()> { + run_with_change_hook(workspace, |_| Ok(())) +} + +/// Load a workspace if present and persist every keyboard-driven state change. +/// +/// The caller supplies an admitted private state directory and a safe file +/// basename. Only `NotFound` selects the provided initial workspace; malformed +/// or unsafe saved state fails closed. +/// +/// # Errors +/// +/// Returns an I/O error for unsafe or malformed saved state, terminal errors, +/// or failed atomic persistence. +#[cfg(unix)] +pub fn run_persistent( + initial_workspace: Workspace, + directory: &SafeDir, + name: &str, +) -> io::Result<()> { + let workspace = load_workspace_or_initial(initial_workspace, directory, name)?; + workspace.save_to(directory, name)?; + run_with_change_hook(workspace, |workspace| workspace.save_to(directory, name)) +} + +#[cfg(unix)] +fn load_workspace_or_initial( + initial_workspace: Workspace, + directory: &SafeDir, + name: &str, +) -> io::Result { + match Workspace::load_from(directory, name) { + Ok(workspace) => Ok(workspace), + Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(initial_workspace), + Err(error) => Err(error), + } +} + +fn run_with_change_hook( + mut workspace: Workspace, + mut on_change: impl FnMut(&Workspace) -> io::Result<()>, +) -> io::Result<()> { + let _guard = TerminalGuard::enter()?; + let backend = CrosstermBackend::new(io::stdout()); + let mut terminal = Terminal::new(backend)?; + let mut help_open = false; + loop { + terminal.draw(|frame| { + render_workspace_mode(frame, &workspace, &[], false, help_open); + })?; + if event::poll(Duration::from_millis(100))? { + if let Event::Key(key) = event::read()? { + let quit = if handle_help_key(&mut help_open, key) { + false + } else { + handle_key(&mut workspace, key) + }; + on_change(&workspace)?; + if quit { + return Ok(()); + } + } + } + } +} + +/// Attach the active workspace pane to one already connected Rover session. +/// +/// Session output is read on a dedicated thread into a bounded queue and is +/// parsed by Rover's terminal emulator before rendering. Ctrl-] detaches; +/// Ctrl-B prefixes workspace commands while all other keys go to the child. +/// +/// # Errors +/// +/// Returns an I/O error when terminal setup, session I/O, event polling, or +/// terminal parsing fails. The session writer is detached on return. +#[cfg(unix)] +pub fn run_attached_session( + workspace: Workspace, + pane_id: &str, + client: SessionClient, +) -> io::Result<()> { + run_attached_session_with_hook( + workspace, + pane_id, + client, + TaskProvider::read_only_snapshot( + Vec::new(), + |_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "task output is unavailable", + )) + }, + false, + ), + |_| Ok(()), + ) +} + +/// Attach to a named session and persist workspace changes under a safe state directory. +/// +/// A saved workspace is loaded when present; malformed state fails closed. +/// +/// # Errors +/// +/// Returns an I/O error for invalid saved state, terminal/session failures, or +/// failed layout persistence. +#[cfg(unix)] +pub fn run_attached_session_persistent( + initial_workspace: Workspace, + pane_id: &str, + client: SessionClient, + directory: &SafeDir, + name: &str, +) -> io::Result<()> { + run_attached_session_persistent_with_tasks( + initial_workspace, + pane_id, + client, + directory, + name, + &[], + ) +} + +/// Attach persistently and show the supplied project task summaries in the read-only task board. +/// +/// # Errors +/// +/// Returns an I/O error for invalid layout state, terminal/session failures, or +/// failed layout persistence. +#[cfg(unix)] +pub fn run_attached_session_persistent_with_tasks( + initial_workspace: Workspace, + pane_id: &str, + client: SessionClient, + directory: &SafeDir, + name: &str, + tasks: &[TaskSummary], +) -> io::Result<()> { + run_attached_session_persistent_with_task_provider( + initial_workspace, + pane_id, + client, + directory, + name, + TaskProvider::read_only_snapshot( + tasks.to_vec(), + |_| { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "task output is unavailable", + )) + }, + false, + ), + ) +} + +/// Attach persistently and load task output lazily by its recorded content digest. +/// +/// The TUI verifies returned bytes against the task's recorded SHA-256 before +/// displaying them. Callers should retrieve content from Rover's bounded blob store. +/// +/// # Errors +/// +/// Returns an I/O error for invalid layout state, terminal/session failures, or +/// failed layout persistence. Blob read errors are rendered in the output panel. +#[cfg(unix)] +pub fn run_attached_session_persistent_with_task_logs( + initial_workspace: Workspace, + pane_id: &str, + client: SessionClient, + directory: &SafeDir, + name: &str, + tasks: &[TaskSummary], + load_blob: impl FnMut(&str) -> io::Result>, +) -> io::Result<()> { + run_attached_session_persistent_with_task_provider( + initial_workspace, + pane_id, + client, + directory, + name, + TaskProvider::read_only_snapshot(tasks.to_vec(), load_blob, true), + ) +} + +/// Attach persistently with a reloadable project task snapshot and lazy output access. +/// +/// Press `r` while the task list or detail panel is open to replace the snapshot. +/// The selected task remains selected when it still exists in the refreshed data. +/// Press `c`, then `y`, to invoke the provider's cancellation callback for the +/// selected task; callers own its durable authorization and state update. +/// +/// # Errors +/// +/// Returns an I/O error for invalid layout state, terminal/session failures, or +/// failed layout persistence. Task refresh and blob read errors are rendered in the TUI. +#[cfg(unix)] +pub fn run_attached_session_persistent_with_task_provider( + initial_workspace: Workspace, + pane_id: &str, + client: SessionClient, + directory: &SafeDir, + name: &str, + task_provider: TaskProvider<'_>, +) -> io::Result<()> { + let workspace = load_workspace_or_initial(initial_workspace, directory, name)?; + workspace.save_to(directory, name)?; + run_attached_session_with_hook(workspace, pane_id, client, task_provider, |workspace| { + workspace.save_to(directory, name) + }) +} + +#[cfg(unix)] +fn run_attached_session_with_hook( + mut workspace: Workspace, + pane_id: &str, + client: SessionClient, + task_provider: TaskProvider<'_>, + mut on_change: impl FnMut(&Workspace) -> io::Result<()>, +) -> io::Result<()> { + workspace.focus_pane(pane_id)?; + let pane = find_pane_ref(workspace.active_tab().root(), pane_id) + .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "workspace pane not found"))?; + if !matches!(pane.kind(), PaneKind::Session { .. }) { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "session can only attach to a session pane", + )); + } + + let (mut reader, mut writer) = client.into_parts(); + let (sender, receiver) = mpsc::sync_channel(64); + let reader_thread = thread::spawn(move || read_session_frames(&mut reader, sender)); + let result = run_attached_loop( + &mut workspace, + pane_id, + &mut writer, + &receiver, + task_provider, + &mut on_change, + ); + drop(receiver); + let _ = writer.detach(); + let _ = reader_thread.join(); + result +} + +#[cfg(unix)] +fn read_session_frames(reader: &mut SessionReader, sender: SyncSender>) { + loop { + let frame = reader.read_frame(); + let terminal = frame.is_err() + || frame + .as_ref() + .is_ok_and(|frame| matches!(frame.kind.as_str(), "exit" | "error")); + if sender.send(frame).is_err() || terminal { + break; + } + } + drop(sender); +} + +#[cfg(unix)] +fn run_attached_loop( + workspace: &mut Workspace, + pane_id: &str, + writer: &mut SessionWriter, + receiver: &Receiver>, + mut task_provider: TaskProvider<'_>, + on_change: &mut impl FnMut(&Workspace) -> io::Result<()>, +) -> io::Result<()> { + let _guard = TerminalGuard::enter()?; + let backend = CrosstermBackend::new(io::stdout()); + let mut terminal = Terminal::new(backend)?; + let terminal_size = terminal.size()?; + let area = Rect::new(0, 0, terminal_size.width, terminal_size.height); + let initial_size = pane_terminal_size(workspace, area, pane_id)?; + let mut screen = TerminalScreen::new(initial_size, 1_000)?; + send_resize(writer, initial_size)?; + let mut input_state = AttachedInputState::default(); + let mut last_agent_sample_request = Instant::now(); + + loop { + for message in receiver.try_iter() { + let frame = message?; + match frame.kind.as_str() { + "output" => screen.process_output(&frame.data)?, + "foreground_sample" => { + apply_agent_detection( + &mut input_state.agent_detection, + detect_foreground_group_sample( + &frame.data, + &frame.processes, + &frame.process_details, + &screen, + ), + ); + } + "exit" => return Ok(()), + "error" => { + return Err(io::Error::other( + frame + .error + .unwrap_or_else(|| "session server error".to_owned()), + )); + } + _ => {} + } + } + + if last_agent_sample_request.elapsed() >= Duration::from_secs(1) { + writer.request_foreground_sample()?; + last_agent_sample_request = Instant::now(); + } + + if screen.take_bell() && pane_is_inactive(workspace, pane_id) { + let mut stdout = io::stdout(); + stdout.write_all(b"\x07")?; + stdout.flush()?; + } + + let pane = find_pane_ref(workspace.active_tab().root(), pane_id); + if pane.is_none() { + return Ok(()); + } + let terminal_size = terminal.size()?; + let area = Rect::new(0, 0, terminal_size.width, terminal_size.height); + if let Ok(size) = pane_terminal_size(workspace, area, pane_id) { + if screen.size() != size { + screen.resize(size)?; + send_resize(writer, size)?; + } + } + let screen_text = pane.map(|pane| ScreenText::from_terminal_screen(pane, &screen)); + draw_attached_frame( + &mut terminal, + workspace, + screen_text.as_ref(), + &input_state, + &task_provider, + )?; + + if event::poll(Duration::from_millis(50))? { + match event::read()? { + Event::Resize(columns, rows) => { + let area = Rect::new(0, 0, columns, rows); + if let Ok(size) = pane_terminal_size(workspace, area, pane_id) { + if screen.size() != size { + screen.resize(size)?; + send_resize(writer, size)?; + } + } + } + Event::Key(key) + if key.kind == KeyEventKind::Press || key.kind == KeyEventKind::Repeat => + { + if handle_attached_key( + workspace, + writer, + &mut task_provider, + &mut input_state, + key, + on_change, + )? { + return Ok(()); + } + if input_state.terminal_handoff == TerminalHandoffState::NeedsRedraw { + terminal.clear()?; + input_state.terminal_handoff = TerminalHandoffState::Idle; + } + refresh_task_snapshot_if_requested(&mut input_state, &mut task_provider); + } + _ => {} + } + } + } +} + +#[cfg(unix)] +fn detect_foreground_sample(data: &[u8], screen: &TerminalScreen) -> DetectionResult { + let foreground = std::str::from_utf8(data) + .ok() + .filter(|name| !name.is_empty()) + .map(|name| ProcessSample { + executable: name.to_owned(), + }); + if let Some(process) = foreground.as_ref() { + if let Ok(Some(manifest)) = bundled_manifest_for_executable(&process.executable) { + let mut result = evaluate_herdr_manifest( + manifest, + &screen.visible_text(), + &screen.osc_title().unwrap_or_default(), + screen.osc_progress().unwrap_or_default(), + ); + let value = process + .executable + .rsplit(['/', '\\']) + .next() + .unwrap_or_default(); + result.evidence.insert( + 0, + DetectionEvidence { + source: "foreground_process", + manifest_source: None, + manifest_version: None, + rule_id: None, + value: value.to_owned(), + }, + ); + return result; + } + } + detect_agent(&DetectionRequest { + foreground, + screen: Some(screen.visible_text()), + ..DetectionRequest::default() + }) +} + +#[cfg(unix)] +fn detect_foreground_group_sample( + leader: &[u8], + processes: &[String], + details: &[rover_execution::pty::ForegroundProcessDetails], + screen: &TerminalScreen, +) -> DetectionResult { + let leader_name = std::str::from_utf8(leader) + .ok() + .filter(|name| !name.is_empty()); + let mut sampled = details + .iter() + .map(|process| ProcessArgvSample { + executable: process.name.clone(), + argv: process.argv.clone(), + }) + .collect::>(); + if sampled.is_empty() { + sampled.extend( + processes + .iter() + .cloned() + .map(|executable| ProcessArgvSample { + executable, + argv: None, + }), + ); + } + let leader_sample = leader_name.map(|executable| { + sampled + .iter() + .find(|process| process.executable == executable) + .cloned() + .unwrap_or_else(|| ProcessArgvSample { + executable: executable.to_owned(), + argv: None, + }) + }); + if let Some(identity) = identify_foreground_process_group(leader_sample.as_ref(), &sampled) { + if identity.ambiguous { + // One evidence entry per equally ranked candidate, so the operator + // can see which agents disagreed rather than only that they did. + let evidence = if identity.conflicting.is_empty() { + vec![DetectionEvidence { + source: "foreground_process", + manifest_source: None, + manifest_version: None, + rule_id: None, + value: "conflicting equally ranked process identities".to_owned(), + }] + } else { + identity + .conflicting + .iter() + .map(|candidate| DetectionEvidence { + source: "foreground_process", + manifest_source: None, + manifest_version: None, + rule_id: None, + value: candidate.clone(), + }) + .collect() + }; + return DetectionResult { + agent: None, + state: AgentState::Unknown, + confidence: Confidence::None, + reason: "ambiguous_foreground_process_group", + evidence, + visibility: DetectionVisibility::default(), + skip_state_update: false, + }; + } + if bundled_manifest_for_executable(&identity.executable) + .is_ok_and(|manifest| manifest.is_some()) + { + return detect_foreground_sample(identity.executable.as_bytes(), screen); + } + return DetectionResult { + agent: Some(identity.executable.clone()), + state: AgentState::Unknown, + confidence: Confidence::None, + reason: "agent_manifest_unavailable", + evidence: vec![DetectionEvidence { + source: "foreground_process", + manifest_source: None, + manifest_version: None, + rule_id: None, + value: identity.executable, + }], + visibility: DetectionVisibility::default(), + skip_state_update: false, + }; + } + detect_foreground_sample(leader, screen) +} + +#[cfg(unix)] +fn apply_agent_detection(current: &mut Option, next: DetectionResult) { + let Some(previous) = current.as_ref() else { + *current = Some(next); + return; + }; + if !next.skip_state_update || previous.agent != next.agent { + *current = Some(next); + return; + } + + let mut preserved = previous.clone(); + preserved.skip_state_update = true; + preserved.reason = "screen_state_preserved_for_skip_update"; + for evidence in next.evidence { + if !preserved.evidence.iter().any(|previous| { + previous.source == evidence.source + && previous.rule_id == evidence.rule_id + && previous.value == evidence.value + }) { + preserved.evidence.push(evidence); + } + } + *current = Some(preserved); +} + +#[cfg(unix)] +fn pane_is_inactive(workspace: &Workspace, pane_id: &str) -> bool { + workspace.active_tab().active_pane() != pane_id +} + +#[cfg(unix)] +fn draw_attached_frame( + terminal: &mut Terminal>, + workspace: &Workspace, + screen_text: Option<&ScreenText>, + input_state: &AttachedInputState, + task_provider: &TaskProvider<'_>, +) -> io::Result<()> { + terminal.draw(|frame| { + let screens = screen_text.map_or(&[] as &[ScreenText], std::slice::from_ref); + render_workspace_mode_for_provider( + frame, + workspace, + screens, + true, + input_state.help_open, + Some(task_provider.capabilities), + input_state.agent_detection.as_ref(), + ); + match input_state.task_panel { + TaskPanelState::Closed => {} + TaskPanelState::List | TaskPanelState::Detail => render_task_board_with_refresh( + frame, + &task_provider.tasks, + input_state.selected_task, + input_state.task_panel == TaskPanelState::Detail, + input_state.task_refresh_message.as_deref(), + task_provider.capabilities, + TaskBoardView { + preferences: &task_provider.view_preferences, + search_draft: input_state.task_search_draft.as_deref(), + }, + ), + TaskPanelState::Stdout => render_task_log( + frame, + "stdout", + &input_state.task_log, + input_state.task_log_scroll, + input_state.task_log_verification == TaskLogVerification::Verified, + ), + TaskPanelState::Stderr => render_task_log( + frame, + "stderr", + &input_state.task_log, + input_state.task_log_scroll, + input_state.task_log_verification == TaskLogVerification::Verified, + ), + TaskPanelState::Evidence => render_task_evidence( + frame, + input_state.task_evidence.as_ref(), + input_state.task_evidence_message.as_deref(), + input_state.task_evidence_scroll, + ), + TaskPanelState::Diff => render_task_diff( + frame, + input_state.task_diff.as_ref(), + input_state.task_diff_message.as_deref(), + input_state.task_diff_scroll, + ), + } + match input_state.file_panel { + FilePanelState::Closed => {} + FilePanelState::Tree => render_file_tree(frame, input_state), + FilePanelState::QuickOpen => render_quick_open(frame, input_state), + FilePanelState::Viewer => render_file_preview(frame, input_state), + FilePanelState::Editing => render_file_editor(frame, input_state), + FilePanelState::DiffPreview => render_file_diff(frame, input_state), + } + if let Some(task_id) = input_state.pending_cancel.as_deref() { + render_task_cancel_confirmation(frame, task_id); + } + if let Some(query) = input_state.command_palette_query.as_deref() { + render_command_palette( + frame, + query, + input_state.command_palette_selected, + task_provider.capabilities, + ); + } + if let Some(draft) = input_state.workspace_notes_draft.as_deref() { + render_workspace_notes( + frame, + draft, + input_state.workspace_notes_cursor, + input_state.workspace_notes_state == WorkspaceNotesState::ConfirmDiscard, + input_state.workspace_notes_message.as_deref(), + ); + } else if input_state.workspace_notes_panel == WorkspaceNotesPanel::List { + render_workspace_notes_list( + frame, + &task_provider.workspace_notes, + input_state.selected_workspace_note, + input_state.workspace_notes_state == WorkspaceNotesState::ConfirmDelete, + input_state.workspace_notes_message.as_deref(), + ); + } + render_saved_commands_overlay(frame, input_state, task_provider); + render_task_drafts_overlay(frame, input_state, task_provider); + render_agent_session_panel( + frame, + task_provider.agent_session.as_ref(), + input_state.agent_session_panel, + input_state.agent_session_message.as_deref(), + ); + })?; + Ok(()) +} + +#[cfg(unix)] +fn render_agent_session_panel( + frame: &mut Frame<'_>, + session: Option<&AgentSessionSummary>, + panel: AgentSessionPanel, + message: Option<&str>, +) { + if panel == AgentSessionPanel::Closed { + return; + } + let area = frame.area(); + if area.width < 36 || area.height < 10 { + return; + } + let popup = centered_rect(76, 58, area); + frame.render_widget(Clear, popup); + let mut lines = Vec::new(); + let title = match panel { + AgentSessionPanel::Closed => return, + AgentSessionPanel::Details => " Saved agent session ", + AgentSessionPanel::ConfirmResume => " Confirm agent resume ", + AgentSessionPanel::ConfirmClear => " Confirm binding removal ", + }; + if let Some(session) = session { + lines.extend([ + Line::from(format!("Pane: {}", session.pane_id)), + Line::from(format!("Agent: {}", session.adapter)), + Line::from(format!("Session ID: {}", session.session_id)), + Line::from(format!("Identity source: {}", session.source)), + ]); + match panel { + AgentSessionPanel::Details => { + lines.push(Line::from("")); + lines.push(Line::from( + "r resume interactively · d forget saved binding · Esc close", + )); + } + AgentSessionPanel::ConfirmResume => { + let command = agent_resume_display(&session.argv); + lines.push(Line::from("")); + lines.push(Line::from(format!("Command: {command}"))); + lines.push(Line::from("This runs in the attached project shell using the agent's configured permissions.")); + lines.push(Line::from( + "Rover does not mediate the agent's permission or sandbox settings.", + )); + lines.push(Line::from( + "Press y to send this command · n or Esc to cancel", + )); + } + AgentSessionPanel::ConfirmClear => { + lines.push(Line::from("")); + lines.push(Line::from( + "Remove only Rover's saved association? A running agent is unaffected.", + )); + lines.push(Line::from( + "Press y to remove the binding · n or Esc to cancel", + )); + } + AgentSessionPanel::Closed => unreachable!(), + } + } else { + lines.push(Line::from("The saved session is no longer available.")); + lines.push(Line::from("Press any key to close this panel.")); + } + if let Some(message) = message { + lines.push(Line::from("")); + lines.push(Line::from(bounded_display_text(message, 512))); + } + frame.render_widget( + Paragraph::new(lines) + .wrap(Wrap { trim: false }) + .block(Block::default().title(title).borders(Borders::ALL)), + popup, + ); +} + +#[cfg(unix)] +fn agent_resume_display(argv: &[String]) -> String { + argv.iter() + .map(|argument| format!("'{}'", argument.replace('\'', "'\\''"))) + .collect::>() + .join(" ") +} + +#[cfg(unix)] +fn agent_resume_shell_line(argv: &[String]) -> io::Result> { + if argv.is_empty() + || argv.iter().any(|argument| { + argument.is_empty() + || argument + .chars() + .any(|character| character.is_control() || character == '\0') + }) + { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "agent resume arguments are empty or contain control characters", + )); + } + let mut line = agent_resume_display(argv).into_bytes(); + line.push(b'\n'); + Ok(line) +} + +#[cfg(unix)] +fn handle_agent_session_key( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, + mut send: impl FnMut(&[u8]) -> io::Result<()>, +) -> io::Result<()> { + if key.kind != KeyEventKind::Press { + return Ok(()); + } + if provider.agent_session.is_none() { + state.agent_session_panel = AgentSessionPanel::Closed; + return Ok(()); + } + match state.agent_session_panel { + AgentSessionPanel::Details => match key.code { + KeyCode::Esc => state.agent_session_panel = AgentSessionPanel::Closed, + KeyCode::Char('r') => state.agent_session_panel = AgentSessionPanel::ConfirmResume, + KeyCode::Char('d') => state.agent_session_panel = AgentSessionPanel::ConfirmClear, + _ => {} + }, + AgentSessionPanel::ConfirmResume => match key.code { + KeyCode::Esc | KeyCode::Char('n') => { + state.agent_session_panel = AgentSessionPanel::Details; + } + KeyCode::Char('y') => { + let command = agent_resume_shell_line( + &provider.agent_session.as_ref().expect("checked above").argv, + )?; + send(&command)?; + state.agent_session_panel = AgentSessionPanel::Closed; + state.agent_session_message = None; + } + _ => {} + }, + AgentSessionPanel::ConfirmClear => match key.code { + KeyCode::Esc | KeyCode::Char('n') => { + state.agent_session_panel = AgentSessionPanel::Details; + } + KeyCode::Char('y') => { + let pane_id = provider + .agent_session + .as_ref() + .expect("checked above") + .pane_id + .clone(); + match (provider.clear_agent_session)(&pane_id) { + Ok(()) => { + provider.agent_session = None; + provider.capabilities.0 &= !TaskCapabilities::AGENT_SESSION; + state.agent_session_panel = AgentSessionPanel::Closed; + state.agent_session_message = None; + } + Err(error) => { + state.agent_session_message = + Some(format!("Could not clear binding: {error}")); + state.agent_session_panel = AgentSessionPanel::Details; + } + } + } + _ => {} + }, + AgentSessionPanel::Closed => {} + } + Ok(()) +} + +#[cfg(unix)] +fn handle_agent_session_input( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, + send: impl FnMut(&[u8]) -> io::Result<()>, +) -> io::Result { + if state.agent_session_panel == AgentSessionPanel::Closed { + return Ok(false); + } + handle_agent_session_key(state, provider, key, send)?; + Ok(true) +} + +#[cfg(unix)] +#[derive(Default)] +struct AttachedInputState { + prefix: bool, + help_open: bool, + task_panel: TaskPanelState, + selected_task: usize, + task_log: String, + task_log_scroll: u16, + task_log_verification: TaskLogVerification, + refresh_request: RefreshRequest, + task_refresh_message: Option, + pending_cancel: Option, + task_evidence: Option, + task_evidence_message: Option, + task_evidence_scroll: u16, + task_diff: Option, + task_diff_message: Option, + task_diff_scroll: u16, + task_search_draft: Option, + file_panel: FilePanelState, + file_directory: String, + file_listing: Option, + file_selected: usize, + file_include_hidden: bool, + quick_open_entries: Vec, + quick_open_query: Option, + quick_open_selected: usize, + quick_open_return: FilePanelState, + file_preview_return: FilePanelState, + file_preview: Option, + file_preview_scroll: u16, + file_edit_capability: FileEditCapability, + file_edit: Option, + file_diff_scroll: u16, + terminal_handoff: TerminalHandoffState, + command_palette_query: Option, + command_palette_selected: usize, + workspace_notes_draft: Option, + workspace_notes_cursor: usize, + workspace_notes_state: WorkspaceNotesState, + workspace_notes_panel: WorkspaceNotesPanel, + selected_workspace_note: usize, + workspace_notes_editing_index: Option, + workspace_notes_message: Option, + saved_commands_panel: SavedCommandsPanel, + selected_saved_command: usize, + saved_command_draft: Option, + saved_command_dialog: SavedCommandDialog, + saved_command_message: Option, + pending_saved_command: Option, + task_drafts_panel: SavedCommandsPanel, + selected_task_draft: usize, + task_draft_panel: TaskDraftPanel, + selected_task_plan: usize, + task_draft: Option, + task_draft_dialog: SavedCommandDialog, + task_draft_message: Option, + file_message: Option, + agent_session_panel: AgentSessionPanel, + agent_session_message: Option, + agent_detection: Option, +} + +fn agent_state_label(state: AgentState) -> &'static str { + match state { + AgentState::Unknown => "unknown", + AgentState::Idle => "idle", + AgentState::Working => "working", + AgentState::Blocked => "blocked", + } +} + +#[cfg(unix)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +enum FilePanelState { + #[default] + Closed, + Tree, + QuickOpen, + Viewer, + Editing, + DiffPreview, +} + +#[cfg(unix)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +enum FileEditCapability { + #[default] + Disabled, + Enabled, +} + +#[cfg(unix)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +enum TerminalHandoffState { + #[default] + Idle, + NeedsRedraw, +} + +#[cfg(unix)] +struct FilePreviewState { + path: String, + text: String, + truncated: bool, + binary: bool, +} + +#[cfg(unix)] +struct FileEditState { + path: String, + original: String, + buffer: String, + original_sha256: String, + cursor: usize, + discard_confirmation: bool, +} + +#[cfg(unix)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +enum TaskPanelState { + #[default] + Closed, + List, + Detail, + Stdout, + Stderr, + Evidence, + Diff, +} + +#[cfg(unix)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +enum WorkspaceNotesState { + #[default] + Editing, + ConfirmDiscard, + ConfirmDelete, +} + +#[cfg(unix)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +enum WorkspaceNotesPanel { + #[default] + Closed, + List, +} + +#[cfg(unix)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +enum TaskDraftPanel { + #[default] + Drafts, + Plans, +} + +#[cfg(unix)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +enum SavedCommandsPanel { + #[default] + Closed, + List, +} + +#[cfg(unix)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +enum SavedCommandDialog { + #[default] + Editing, + ConfirmDiscard, + ConfirmDelete, + ConfirmCreatePlan, +} + +#[cfg(unix)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +enum SavedCommandField { + #[default] + Name, + Command, +} + +#[cfg(unix)] +#[derive(Clone, Debug, Default, Eq, PartialEq)] +struct SavedCommandDraft { + editing_index: Option, + name: String, + command: String, + name_cursor: usize, + command_cursor: usize, + active_field: SavedCommandField, +} + +#[cfg(unix)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +enum TaskDraftField { + #[default] + Title, + Paths, + Dependencies, + QualityGate, + Prompt, +} + +#[cfg(unix)] +#[derive(Clone, Debug, Default, Eq, PartialEq)] +struct TaskDraftEdit { + editing_index: Option, + title: String, + paths: String, + dependencies: String, + quality_gate: String, + prompt: String, + title_cursor: usize, + paths_cursor: usize, + dependencies_cursor: usize, + quality_gate_cursor: usize, + prompt_cursor: usize, + active_field: TaskDraftField, +} + +#[cfg(unix)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum PaletteAction { + OpenTasks, + OpenFiles, + QuickOpen, + OpenNotes, + OpenSavedCommands, + OpenTaskDrafts, + OpenAgentSession, + RefreshTasks, + KeyboardHelp, + NextTab, + PreviousTab, +} + +#[cfg(unix)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +enum TaskLogVerification { + Verified, + #[default] + Unavailable, +} + +#[cfg(unix)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +enum RefreshRequest { + #[default] + Idle, + Requested, +} + +#[cfg(unix)] +fn handle_attached_key( + workspace: &mut Workspace, + writer: &mut SessionWriter, + task_provider: &mut TaskProvider<'_>, + state: &mut AttachedInputState, + key: KeyEvent, + on_change: &mut impl FnMut(&Workspace) -> io::Result<()>, +) -> io::Result { + if is_detach_key(key) { + return Ok(true); + } + if state.pending_saved_command.is_some() { + handle_saved_command_confirmation(state, task_provider, key, |bytes| { + writer.send_input(bytes) + }); + return Ok(false); + } + if handle_agent_session_input(state, task_provider, key, |bytes| writer.send_input(bytes))? { + return Ok(false); + } + if state.saved_commands_panel == SavedCommandsPanel::List || state.saved_command_draft.is_some() + { + handle_saved_commands_key(state, task_provider, key); + return Ok(false); + } + if state.task_drafts_panel == SavedCommandsPanel::List || state.task_draft.is_some() { + handle_task_drafts_key(state, task_provider, key); + return Ok(false); + } + if state.file_panel != FilePanelState::Closed { + handle_file_panel_key(state, task_provider, key); + return Ok(false); + } + if state.workspace_notes_panel == WorkspaceNotesPanel::List { + handle_workspace_notes_key(state, task_provider, key); + return Ok(false); + } + if state.command_palette_query.is_some() { + handle_command_palette_key(workspace, state, task_provider, key, on_change)?; + return Ok(false); + } + if state.task_search_draft.is_some() { + handle_task_search_edit(state, task_provider, key); + return Ok(false); + } + if state.pending_cancel.is_some() { + handle_task_cancel_confirmation(state, task_provider, key); + return Ok(false); + } + if state.task_panel == TaskPanelState::Evidence && attached_task_evidence_key(state, key) { + return Ok(false); + } + if state.task_panel == TaskPanelState::Diff && attached_task_diff_key(state, key) { + return Ok(false); + } + if handle_task_view_shortcut(state, task_provider, key) { + return Ok(false); + } + if handle_task_panel_action(state, task_provider, key) { + return Ok(false); + } + if attached_help_key(&mut state.help_open, &mut state.prefix, key) { + on_change(workspace)?; + return Ok(false); + } + if open_workspace_notes_shortcut(state, task_provider, key) { + return Ok(false); + } + if open_saved_commands_shortcut(state, task_provider, key) { + return Ok(false); + } + if open_task_drafts_shortcut(state, task_provider, key) { + return Ok(false); + } + if open_agent_session_shortcut(state, task_provider, key) { + return Ok(false); + } + if open_command_palette_shortcut(state, task_provider, key) { + return Ok(false); + } + if open_file_browser_shortcut(state, task_provider, key) { + return Ok(false); + } + if attached_task_board_key( + &mut state.task_panel, + &mut state.prefix, + &mut state.selected_task, + &task_provider.tasks, + &task_provider.view_preferences, + key, + ) { + return Ok(false); + } + if state.prefix { + state.prefix = false; + if key.code == KeyCode::Char('b') && key.modifiers.contains(KeyModifiers::CONTROL) { + writer.send_input(&[0x02])?; + } else { + let should_detach = attached_workspace_key(workspace, key); + on_change(workspace)?; + return Ok(should_detach); + } + } else if key.code == KeyCode::Char('b') && key.modifiers.contains(KeyModifiers::CONTROL) { + state.prefix = true; + } else if let Some(bytes) = key_input_bytes(key) { + writer.send_input(&bytes)?; + } + Ok(false) +} + +#[cfg(unix)] +fn handle_task_panel_action( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) -> bool { + if provider.capabilities.supports(TaskCapabilities::REFRESH) + && attached_task_refresh_key(state.task_panel, key) + { + state.refresh_request = RefreshRequest::Requested; + return true; + } + if provider.capabilities.supports(TaskCapabilities::CANCEL) + && attached_task_cancel_key(state.task_panel, key) + { + state.pending_cancel = provider + .tasks + .get(state.selected_task) + .map(|task| task.id.clone()); + if state.pending_cancel.is_none() { + state.task_refresh_message = Some("No task is selected to cancel.".to_owned()); + } + return true; + } + if matches!( + state.task_panel, + TaskPanelState::Stdout | TaskPanelState::Stderr + ) && attached_task_log_key(&mut state.task_panel, &mut state.task_log_scroll, key) + { + return true; + } + if provider.capabilities.supports(TaskCapabilities::LOGS) + && state.task_panel == TaskPanelState::Detail + && open_selected_task_log(state, provider, key) + { + return true; + } + if provider.capabilities.supports(TaskCapabilities::EVIDENCE) + && state.task_panel == TaskPanelState::Detail + && open_selected_task_evidence(state, provider, key) + { + return true; + } + provider.capabilities.supports(TaskCapabilities::DIFF) + && state.task_panel == TaskPanelState::Detail + && open_selected_task_diff(state, provider, key) +} + +#[cfg(unix)] +fn handle_task_view_shortcut( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) -> bool { + if state.task_panel != TaskPanelState::List || key.kind != KeyEventKind::Press { + return false; + } + match key.code { + KeyCode::Char('/') => { + state.task_search_draft = Some(provider.view_preferences.query.clone()); + true + } + KeyCode::Char('s') => { + provider.view_preferences.sort = provider.view_preferences.sort.next(); + save_task_view_preferences(state, provider); + true + } + _ => false, + } +} + +#[cfg(unix)] +fn open_command_palette_shortcut( + state: &mut AttachedInputState, + provider: &TaskProvider<'_>, + key: KeyEvent, +) -> bool { + if !state.prefix || key.kind != KeyEventKind::Press || key.code != KeyCode::Char('p') { + return false; + } + state.prefix = false; + state.command_palette_query = Some(String::new()); + state.command_palette_selected = 0; + let commands = palette_actions(provider.capabilities, ""); + if commands.is_empty() { + state.command_palette_query = None; + } + true +} + +#[cfg(unix)] +fn open_workspace_notes_shortcut( + state: &mut AttachedInputState, + provider: &TaskProvider<'_>, + key: KeyEvent, +) -> bool { + if !state.prefix + || key.kind != KeyEventKind::Press + || key.code != KeyCode::Char('n') + || !provider.capabilities.supports(TaskCapabilities::NOTES) + { + return false; + } + state.prefix = false; + state.workspace_notes_panel = WorkspaceNotesPanel::List; + state.selected_workspace_note = 0; + state.workspace_notes_draft = None; + state.workspace_notes_editing_index = None; + state.workspace_notes_state = WorkspaceNotesState::Editing; + state.workspace_notes_message = None; + true +} + +#[cfg(unix)] +fn handle_workspace_notes_key( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) { + if key.kind != KeyEventKind::Press { + return; + } + if state.workspace_notes_draft.is_none() { + handle_workspace_notes_list_key(state, provider, key); + return; + } + if state.workspace_notes_state == WorkspaceNotesState::ConfirmDiscard { + handle_workspace_notes_discard_confirmation(state, key); + return; + } + handle_workspace_note_edit_key(state, provider, key); +} + +#[cfg(unix)] +fn handle_workspace_notes_list_key( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) { + if state.workspace_notes_state == WorkspaceNotesState::ConfirmDelete { + match key.code { + KeyCode::Char('y') => { + if state.selected_workspace_note < provider.workspace_notes.len() { + let mut updated = provider.workspace_notes.clone(); + updated.remove(state.selected_workspace_note); + match (provider.save_workspace_notes)(&updated) { + Ok(()) => provider.workspace_notes = updated, + Err(error) => { + state.workspace_notes_message = + Some(format!("Could not delete note: {}", error.kind())); + } + } + state.selected_workspace_note = state + .selected_workspace_note + .min(provider.workspace_notes.len().saturating_sub(1)); + } + state.workspace_notes_state = WorkspaceNotesState::Editing; + } + KeyCode::Char('n') | KeyCode::Esc => { + state.workspace_notes_state = WorkspaceNotesState::Editing; + } + _ => {} + } + return; + } + state.workspace_notes_message = None; + match key.code { + KeyCode::Esc => { + state.workspace_notes_panel = WorkspaceNotesPanel::Closed; + state.workspace_notes_message = None; + } + KeyCode::Up if !provider.workspace_notes.is_empty() => { + state.selected_workspace_note = if state.selected_workspace_note == 0 { + provider.workspace_notes.len() - 1 + } else { + state.selected_workspace_note - 1 + }; + } + KeyCode::Down if !provider.workspace_notes.is_empty() => { + state.selected_workspace_note = + (state.selected_workspace_note + 1) % provider.workspace_notes.len(); + } + KeyCode::Char('n') if provider.workspace_notes.len() < MAX_WORKSPACE_NOTES_COUNT => { + state.workspace_notes_editing_index = None; + state.workspace_notes_draft = Some(String::new()); + state.workspace_notes_cursor = 0; + } + KeyCode::Enter if state.selected_workspace_note < provider.workspace_notes.len() => { + let note = provider.workspace_notes[state.selected_workspace_note].clone(); + state.workspace_notes_editing_index = Some(state.selected_workspace_note); + state.workspace_notes_cursor = note.len(); + state.workspace_notes_draft = Some(note); + } + KeyCode::Char('d') if state.selected_workspace_note < provider.workspace_notes.len() => { + state.workspace_notes_state = WorkspaceNotesState::ConfirmDelete; + } + KeyCode::Char('n') => { + state.workspace_notes_message = Some(format!( + "At most {MAX_WORKSPACE_NOTES_COUNT} notes can be stored." + )); + } + _ => {} + } +} + +#[cfg(unix)] +fn handle_workspace_note_edit_key( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) { + let Some(mut draft) = state.workspace_notes_draft.take() else { + return; + }; + state.workspace_notes_message = None; + match key.code { + KeyCode::Esc => { + let original = state + .workspace_notes_editing_index + .and_then(|index| provider.workspace_notes.get(index)); + if state.workspace_notes_editing_index.is_some() && original == Some(&draft) { + state.workspace_notes_editing_index = None; + state.workspace_notes_state = WorkspaceNotesState::Editing; + return; + } + state.workspace_notes_state = WorkspaceNotesState::ConfirmDiscard; + state.workspace_notes_draft = Some(draft); + return; + } + KeyCode::Char('s') if key.modifiers.contains(KeyModifiers::CONTROL) => { + save_workspace_note(state, provider, &draft); + return; + } + KeyCode::Left => { + while state.workspace_notes_cursor > 0 { + state.workspace_notes_cursor -= 1; + if draft.is_char_boundary(state.workspace_notes_cursor) { + break; + } + } + } + KeyCode::Right => { + if state.workspace_notes_cursor < draft.len() { + state.workspace_notes_cursor += 1; + while !draft.is_char_boundary(state.workspace_notes_cursor) { + state.workspace_notes_cursor += 1; + } + } + } + KeyCode::Home => { + state.workspace_notes_cursor = draft[..state.workspace_notes_cursor] + .rfind('\n') + .map_or(0, |index| index + 1); + } + KeyCode::End => { + state.workspace_notes_cursor += draft[state.workspace_notes_cursor..] + .find('\n') + .unwrap_or(draft.len() - state.workspace_notes_cursor); + } + KeyCode::Backspace if state.workspace_notes_cursor > 0 => { + let mut start = state.workspace_notes_cursor - 1; + while !draft.is_char_boundary(start) { + start -= 1; + } + draft.drain(start..state.workspace_notes_cursor); + state.workspace_notes_cursor = start; + } + KeyCode::Delete if state.workspace_notes_cursor < draft.len() => { + let mut end = state.workspace_notes_cursor + 1; + while !draft.is_char_boundary(end) { + end += 1; + } + draft.drain(state.workspace_notes_cursor..end); + } + KeyCode::Enter if draft.len() < MAX_WORKSPACE_NOTES_BYTES => { + draft.insert(state.workspace_notes_cursor, '\n'); + state.workspace_notes_cursor += 1; + } + KeyCode::Char(character) + if !key + .modifiers + .intersects(KeyModifiers::CONTROL | KeyModifiers::ALT) + && !character.is_control() + && draft.len().saturating_add(character.len_utf8()) + <= MAX_WORKSPACE_NOTES_BYTES => + { + draft.insert(state.workspace_notes_cursor, character); + state.workspace_notes_cursor += character.len_utf8(); + } + _ => {} + } + if state.workspace_notes_draft.is_none() { + state.workspace_notes_draft = Some(draft); + } +} + +#[cfg(unix)] +fn save_workspace_note( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + draft: &str, +) { + let mut updated = provider.workspace_notes.clone(); + let selected = if let Some(index) = state.workspace_notes_editing_index { + if index >= updated.len() { + state.workspace_notes_message = Some("Selected note no longer exists.".to_owned()); + state.workspace_notes_draft = Some(draft.to_owned()); + return; + } + draft.clone_into(&mut updated[index]); + index + } else { + if updated.len() >= MAX_WORKSPACE_NOTES_COUNT { + state.workspace_notes_message = Some(format!( + "At most {MAX_WORKSPACE_NOTES_COUNT} notes can be stored." + )); + state.workspace_notes_draft = Some(draft.to_owned()); + return; + } + updated.push(draft.to_owned()); + updated.len() - 1 + }; + let total_bytes = updated.iter().map(String::len).sum::(); + if total_bytes > MAX_WORKSPACE_NOTES_TOTAL_BYTES { + state.workspace_notes_message = Some(format!( + "Notes exceed the {MAX_WORKSPACE_NOTES_TOTAL_BYTES}-byte collection limit." + )); + state.workspace_notes_draft = Some(draft.to_owned()); + return; + } + match (provider.save_workspace_notes)(&updated) { + Ok(()) => { + provider.workspace_notes = updated; + state.selected_workspace_note = selected; + state.workspace_notes_editing_index = None; + state.workspace_notes_state = WorkspaceNotesState::Editing; + state.workspace_notes_draft = None; + } + Err(error) => { + state.workspace_notes_message = Some(format!("Could not save notes: {}", error.kind())); + state.workspace_notes_draft = Some(draft.to_owned()); + } + } +} + +#[cfg(unix)] +fn handle_workspace_notes_discard_confirmation(state: &mut AttachedInputState, key: KeyEvent) { + match key.code { + KeyCode::Char('y') => { + state.workspace_notes_draft = None; + state.workspace_notes_editing_index = None; + } + KeyCode::Char('n') | KeyCode::Esc => {} + _ => return, + } + state.workspace_notes_state = WorkspaceNotesState::Editing; + state.workspace_notes_message = None; +} + +#[cfg(unix)] +fn render_workspace_notes( + frame: &mut Frame<'_>, + draft: &str, + cursor: usize, + discard_confirmation: bool, + message: Option<&str>, +) { + let area = frame.area(); + if area.width < 24 || area.height < 8 { + return; + } + let popup = centered_rect(88, 82, area); + frame.render_widget(Clear, popup); + let cursor = cursor.min(draft.len()); + let prefix = &draft[..cursor]; + let cursor_line = prefix.bytes().filter(|byte| *byte == b'\n').count(); + let cursor_column = prefix + .rsplit_once('\n') + .map_or(prefix, |(_, line)| line) + .chars() + .count(); + let mut lines = draft + .split('\n') + .enumerate() + .map(|(index, line)| { + if index == cursor_line { + let split = line + .char_indices() + .nth(cursor_column) + .map_or(line.len(), |(offset, _)| offset); + Line::from(format!( + "{}▏{}", + bounded_display_text(&line[..split], 240), + bounded_display_text(&line[split..], 240) + )) + } else { + Line::from(bounded_display_text(line, 240)) + } + }) + .collect::>(); + if draft.is_empty() { + lines.push(Line::from( + "No notes yet. Type reusable prompt or context snippets here.", + )); + } + if let Some(message) = message { + lines.push(Line::from(bounded_display_text(message, 160))); + } + lines.push(Line::from(format!( + "{} / {MAX_WORKSPACE_NOTES_BYTES} bytes · Enter newline · Ctrl-S save · Esc return", + draft.len() + ))); + if discard_confirmation { + lines.push(Line::from( + "Discard unsaved notes? y discard · n/Esc keep editing", + )); + } + frame.render_widget( + Paragraph::new(lines).wrap(Wrap { trim: false }).block( + Block::default() + .title(" Rover repository notes · local state ") + .borders(Borders::ALL), + ), + popup, + ); +} + +#[cfg(unix)] +fn render_workspace_notes_list( + frame: &mut Frame<'_>, + notes: &[String], + selected: usize, + confirm_delete: bool, + message: Option<&str>, +) { + let area = frame.area(); + if area.width < 24 || area.height < 8 { + return; + } + let popup = centered_rect(78, 70, area); + frame.render_widget(Clear, popup); + let mut lines = Vec::new(); + if notes.is_empty() { + lines.push(Line::from( + "No saved notes. Press n to create a reusable note.", + )); + } else { + for (index, note) in notes.iter().enumerate().take(MAX_WORKSPACE_NOTES_COUNT) { + let title = note + .lines() + .next() + .filter(|line| !line.is_empty()) + .unwrap_or("(untitled)"); + let marker = if index == selected { ">" } else { " " }; + lines.push(Line::from(format!( + "{marker} {:02}. {}", + index + 1, + bounded_display_text(title, 96) + ))); + } + } + if let Some(message) = message { + lines.push(Line::from(bounded_display_text(message, 160))); + } + if confirm_delete { + lines.push(Line::from("Delete selected note? y delete · n/Esc keep it")); + } else { + lines.push(Line::from( + "↑/↓ select · n new · Enter edit · d delete · Esc close", + )); + } + frame.render_widget( + Paragraph::new(lines).block( + Block::default() + .title(" Rover repository notes · local state ") + .borders(Borders::ALL), + ), + popup, + ); +} + +#[cfg(unix)] +fn render_saved_commands_list( + frame: &mut Frame<'_>, + commands: &[SavedCommand], + selected: usize, + confirm_delete: bool, + message: Option<&str>, +) { + let area = frame.area(); + if area.width < 28 || area.height < 8 { + return; + } + let popup = centered_rect(82, 72, area); + frame.render_widget(Clear, popup); + let mut lines = Vec::new(); + if commands.is_empty() { + lines.push(Line::from("No saved commands. Press n to add one.")); + } else { + for (index, command) in commands.iter().enumerate().take(MAX_SAVED_COMMANDS) { + lines.push(Line::from(format!( + "{} {:02}. {} · {}", + if index == selected { ">" } else { " " }, + index + 1, + bounded_display_text(&command.name, MAX_SAVED_COMMAND_NAME_BYTES), + bounded_display_text(&command.command, MAX_SAVED_COMMAND_LINE_BYTES) + ))); + } + } + if let Some(message) = message { + lines.push(Line::from(bounded_display_text(message, 160))); + } + lines.push(Line::from(if confirm_delete { + "Delete selected command? y delete · n/Esc keep it" + } else { + "↑/↓ select · n new · Enter edit · d delete · r run with confirmation · Esc close" + })); + frame.render_widget( + Paragraph::new(lines).wrap(Wrap { trim: false }).block( + Block::default() + .title(" Rover saved shell commands · local state ") + .borders(Borders::ALL), + ), + popup, + ); +} + +#[cfg(unix)] +fn render_saved_commands_overlay( + frame: &mut Frame<'_>, + state: &AttachedInputState, + provider: &TaskProvider<'_>, +) { + if let Some(draft) = state.saved_command_draft.as_ref() { + render_saved_command_editor( + frame, + draft, + state.saved_command_dialog, + state.saved_command_message.as_deref(), + ); + } else if state.saved_commands_panel == SavedCommandsPanel::List { + render_saved_commands_list( + frame, + &provider.saved_commands, + state.selected_saved_command, + state.saved_command_dialog == SavedCommandDialog::ConfirmDelete, + state.saved_command_message.as_deref(), + ); + } + if let Some(index) = state.pending_saved_command { + render_saved_command_confirmation(frame, provider.saved_commands.get(index)); + } +} + +#[cfg(unix)] +fn render_saved_command_editor( + frame: &mut Frame<'_>, + draft: &SavedCommandDraft, + dialog: SavedCommandDialog, + message: Option<&str>, +) { + let area = frame.area(); + if area.width < 28 || area.height < 8 { + return; + } + let popup = centered_rect(82, 58, area); + frame.render_widget(Clear, popup); + let name_cursor = draft.name_cursor.min(draft.name.len()); + let command_cursor = draft.command_cursor.min(draft.command.len()); + let name_split = name_cursor; + let command_split = command_cursor; + let name = format!( + "{}{}{}", + bounded_display_text(&draft.name[..name_cursor], MAX_SAVED_COMMAND_NAME_BYTES), + if draft.active_field == SavedCommandField::Name { + "▏" + } else { + "" + }, + bounded_display_text(&draft.name[name_split..], MAX_SAVED_COMMAND_NAME_BYTES) + ); + let command = format!( + "{}{}{}", + bounded_display_text( + &draft.command[..command_cursor], + MAX_SAVED_COMMAND_LINE_BYTES + ), + if draft.active_field == SavedCommandField::Command { + "▏" + } else { + "" + }, + bounded_display_text( + &draft.command[command_split..], + MAX_SAVED_COMMAND_LINE_BYTES + ) + ); + let mut lines = vec![ + Line::from("Tab switches fields · Ctrl-S saves · Esc returns"), + Line::from(format!( + "Name (max {MAX_SAVED_COMMAND_NAME_BYTES} bytes): {name}" + )), + Line::from(format!( + "Command (max {MAX_SAVED_COMMAND_LINE_BYTES} bytes): {command}" + )), + ]; + if let Some(message) = message { + lines.push(Line::from(bounded_display_text(message, 160))); + } + if dialog == SavedCommandDialog::ConfirmDiscard { + lines.push(Line::from( + "Discard unsaved command? y discard · n/Esc keep editing", + )); + } + frame.render_widget( + Paragraph::new(lines).wrap(Wrap { trim: false }).block( + Block::default() + .title(" Edit saved shell command ") + .borders(Borders::ALL), + ), + popup, + ); +} + +#[cfg(unix)] +fn render_saved_command_confirmation(frame: &mut Frame<'_>, command: Option<&SavedCommand>) { + let area = frame.area(); + if area.width < 32 || area.height < 8 { + return; + } + let popup = centered_rect(90, 62, area); + frame.render_widget(Clear, popup); + let lines = match command { + Some(command) => vec![ + Line::from(format!( + "Run saved command: {}", + bounded_display_text(&command.name, MAX_SAVED_COMMAND_NAME_BYTES) + )), + Line::from(format!( + "Command: {}", + bounded_display_text(&command.command, MAX_SAVED_COMMAND_LINE_BYTES) + )), + Line::from( + "Runs in the attached shell with Rover's OS-user authority; it is not sandboxed.", + ), + Line::from("y send once · n/Esc cancel"), + ], + None => vec![ + Line::from("The selected saved command is unavailable."), + Line::from("Press y to dismiss."), + ], + }; + frame.render_widget( + Paragraph::new(lines).wrap(Wrap { trim: false }).block( + Block::default() + .title(" Confirm shell input ") + .borders(Borders::ALL), + ), + popup, + ); +} + +#[cfg(unix)] +fn render_task_drafts_overlay( + frame: &mut Frame<'_>, + state: &AttachedInputState, + provider: &TaskProvider<'_>, +) { + if let Some(draft) = state.task_draft.as_ref() { + render_task_draft_editor(frame, draft, state); + } else if state.task_drafts_panel == SavedCommandsPanel::List { + if state.task_draft_panel == TaskDraftPanel::Plans { + render_task_plan_list(frame, state, provider); + } else { + render_task_draft_list(frame, state, provider); + } + } +} + +#[cfg(unix)] +fn render_task_draft_editor( + frame: &mut Frame<'_>, + draft: &TaskDraftEdit, + state: &AttachedInputState, +) { + let area = frame.area(); + if area.width < 28 || area.height < 8 { + return; + } + let popup = centered_rect(88, 70, area); + frame.render_widget(Clear, popup); + let title = task_draft_line_window( + &draft.title, + draft.title_cursor, + draft.active_field == TaskDraftField::Title, + 160, + ); + let mut lines = vec![ + Line::from( + "Tab changes field · Enter adds line/advances title · Ctrl-S saves · Esc returns", + ), + Line::from(format!("Title: {title}")), + ]; + lines.extend(task_draft_field_view(draft)); + if let Some(message) = state.task_draft_message.as_deref() { + lines.push(Line::from(bounded_display_text(message, 160))); + } + if state.task_draft_dialog == SavedCommandDialog::ConfirmDiscard { + lines.push(Line::from( + "Discard unsaved draft? y discard · n/Esc keep editing", + )); + } + frame.render_widget( + Paragraph::new(lines).wrap(Wrap { trim: false }).block( + Block::default() + .title(" Edit local task briefing draft ") + .borders(Borders::ALL), + ), + popup, + ); +} + +#[cfg(unix)] +fn render_task_draft_list( + frame: &mut Frame<'_>, + state: &AttachedInputState, + provider: &TaskProvider<'_>, +) { + let area = frame.area(); + if area.width < 28 || area.height < 8 { + return; + } + let popup = centered_rect(84, 70, area); + frame.render_widget(Clear, popup); + let mut lines = Vec::new(); + if provider.task_drafts.is_empty() { + lines.push(Line::from("No task drafts. Press n to add one.")); + } else { + for (index, draft) in provider + .task_drafts + .iter() + .enumerate() + .take(MAX_TASK_DRAFTS) + { + let title = if draft.title.trim().is_empty() { + "(untitled draft)" + } else { + &draft.title + }; + let preview = draft.prompt.lines().next().unwrap_or_default(); + lines.push(Line::from(format!( + "{} {:02}. {} · {}", + if index == state.selected_task_draft { + ">" + } else { + " " + }, + index + 1, + bounded_display_text(title, MAX_TASK_DRAFT_TITLE_BYTES), + bounded_display_text(preview, 96) + ))); + } + } + if let Some(message) = state.task_draft_message.as_deref() { + lines.push(Line::from(bounded_display_text(message, 160))); + } + let help = match state.task_draft_dialog { + SavedCommandDialog::ConfirmDelete => "Delete selected draft? y delete · n/Esc keep it", + SavedCommandDialog::ConfirmCreatePlan => { + "Create a durable manual plan from this draft? y create · n/Esc cancel · no worker starts" + } + _ => "↑/↓ select · n new · Enter edit · p create plan · l plans · d delete · Esc close", + }; + lines.push(Line::from(help)); + frame.render_widget( + Paragraph::new(lines).wrap(Wrap { trim: false }).block( + Block::default() + .title(" Rover task briefing drafts · local state ") + .borders(Borders::ALL), + ), + popup, + ); +} + +#[cfg(unix)] +fn render_task_plan_list( + frame: &mut Frame<'_>, + state: &AttachedInputState, + provider: &TaskProvider<'_>, +) { + let area = frame.area(); + if area.width < 28 || area.height < 8 { + return; + } + let popup = centered_rect(84, 70, area); + frame.render_widget(Clear, popup); + let mut lines = Vec::new(); + if provider.task_plans.summaries.is_empty() { + lines.push(Line::from( + "No durable task plans. Press Esc to return to drafts.", + )); + } else { + for (index, plan) in provider.task_plans.summaries.iter().enumerate().take(1_000) { + lines.push(Line::from(format!( + "{} {} · {} · {} · {} deps · {} attempts", + if index == state.selected_task_plan { + ">" + } else { + " " + }, + bounded_display_text(&plan.title, MAX_TASK_DRAFT_TITLE_BYTES), + bounded_display_text(&plan.status, 24), + bounded_display_text(&plan.readiness, 24), + plan.dependency_count, + plan.attempt_count, + ))); + lines.push(Line::from(format!( + " {} · {}", + bounded_display_text(&plan.id, 128), + bounded_display_text(&plan.updated_at, 64), + ))); + } + } + if let Some(message) = state.task_draft_message.as_deref() { + lines.push(Line::from(bounded_display_text(message, 160))); + } + lines.push(Line::from( + "↑/↓ select · r refresh · Esc return · plans are not dispatched", + )); + frame.render_widget( + Paragraph::new(lines).wrap(Wrap { trim: false }).block( + Block::default() + .title(" Rover task plans · project state ") + .borders(Borders::ALL), + ), + popup, + ); +} + +#[cfg(unix)] +fn task_draft_line_window(value: &str, cursor: usize, active: bool, limit: usize) -> String { + let chars: Vec = value.chars().collect(); + let cursor = value[..cursor.min(value.len())] + .chars() + .count() + .min(chars.len()); + let start = cursor + .saturating_sub(limit / 2) + .min(chars.len().saturating_sub(limit)); + let end = (start + limit).min(chars.len()); + let visible = chars[start..end].iter().collect::(); + let cursor_in_view = cursor.saturating_sub(start).min(visible.chars().count()); + let mut result = String::new(); + if start > 0 { + result.push('…'); + } + for (index, character) in visible.chars().enumerate() { + if active && index == cursor_in_view { + result.push('▏'); + } + result.push(character); + } + if active && cursor_in_view == visible.chars().count() { + result.push('▏'); + } + if end < chars.len() { + result.push('…'); + } + result +} + +#[cfg(unix)] +fn task_draft_multiline_lines( + value: &str, + cursor: usize, + active: bool, + max_rows: usize, +) -> Vec> { + let cursor = cursor.min(value.len()); + let prefix = &value[..cursor]; + let cursor_line = prefix.bytes().filter(|byte| *byte == b'\n').count(); + let cursor_column = prefix + .rsplit('\n') + .next() + .unwrap_or_default() + .chars() + .count(); + let all_lines: Vec<&str> = value.split('\n').collect(); + let start = cursor_line + .saturating_sub(max_rows / 2) + .min(all_lines.len().saturating_sub(max_rows)); + all_lines + .iter() + .enumerate() + .skip(start) + .take(max_rows) + .map(|(index, line)| { + if index == cursor_line && active { + Line::from(task_draft_line_window(line, cursor_column, true, 180)) + } else { + Line::from(bounded_display_text(line, 180)) + } + }) + .collect() +} + +#[cfg(unix)] +fn task_draft_field_view(draft: &TaskDraftEdit) -> Vec> { + let mut lines = Vec::new(); + append_draft_multiline_field( + &mut lines, + "Paths", + &draft.paths, + draft.paths_cursor, + TaskDraftField::Paths, + draft, + MAX_TASK_DRAFT_PATHS_BYTES, + ); + append_draft_multiline_field( + &mut lines, + "Dependencies", + &draft.dependencies, + draft.dependencies_cursor, + TaskDraftField::Dependencies, + draft, + MAX_TASK_DRAFT_DEPENDENCIES_BYTES, + ); + append_draft_multiline_field( + &mut lines, + "Quality gate", + &draft.quality_gate, + draft.quality_gate_cursor, + TaskDraftField::QualityGate, + draft, + MAX_TASK_DRAFT_GATE_BYTES, + ); + append_draft_multiline_field( + &mut lines, + "Prompt (unsent)", + &draft.prompt, + draft.prompt_cursor, + TaskDraftField::Prompt, + draft, + MAX_TASK_DRAFT_PROMPT_BYTES, + ); + lines +} + +#[cfg(unix)] +fn append_draft_multiline_field( + lines: &mut Vec>, + label: &str, + value: &str, + cursor: usize, + field: TaskDraftField, + draft: &TaskDraftEdit, + max_bytes: usize, +) { + let active = draft.active_field == field; + let count = value.lines().count(); + lines.push(Line::from(format!( + "{label}{} ({} bytes max):{}", + if active { " ◀" } else { "" }, + max_bytes, + if active { "" } else { " (press Tab to edit)" }, + ))); + if active { + lines.extend(task_draft_multiline_lines(value, cursor, true, 8)); + } else { + lines.push(Line::from(format!( + "{}{}", + bounded_display_text(value.lines().next().unwrap_or_default(), 100), + if count > 1 { " …" } else { "" }, + ))); + } +} + +#[cfg(unix)] +fn open_saved_commands_shortcut( + state: &mut AttachedInputState, + provider: &TaskProvider<'_>, + key: KeyEvent, +) -> bool { + if !state.prefix + || key.kind != KeyEventKind::Press + || key.code != KeyCode::Char('k') + || !provider + .capabilities + .supports(TaskCapabilities::SAVED_COMMANDS) + { + return false; + } + state.prefix = false; + state.saved_commands_panel = SavedCommandsPanel::List; + state.selected_saved_command = 0; + state.saved_command_message = None; + true +} + +#[cfg(unix)] +fn open_task_drafts_shortcut( + state: &mut AttachedInputState, + provider: &TaskProvider<'_>, + key: KeyEvent, +) -> bool { + if !state.prefix + || key.kind != KeyEventKind::Press + || key.code != KeyCode::Char('d') + || !provider + .capabilities + .supports(TaskCapabilities::TASK_DRAFTS) + { + return false; + } + state.prefix = false; + state.task_drafts_panel = SavedCommandsPanel::List; + state.selected_task_draft = 0; + state.task_draft_panel = TaskDraftPanel::Drafts; + state.selected_task_plan = 0; + state.task_draft_message = None; + true +} + +#[cfg(unix)] +fn open_agent_session_shortcut( + state: &mut AttachedInputState, + provider: &TaskProvider<'_>, + key: KeyEvent, +) -> bool { + if !state.prefix + || key.kind != KeyEventKind::Press + || key.code != KeyCode::Char('a') + || !provider + .capabilities + .supports(TaskCapabilities::AGENT_SESSION) + { + return false; + } + state.prefix = false; + state.agent_session_panel = AgentSessionPanel::Details; + state.agent_session_message = None; + true +} + +#[cfg(unix)] +fn handle_task_drafts_key( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) { + if key.kind != KeyEventKind::Press { + return; + } + if let Some(mut draft) = state.task_draft.take() { + handle_task_draft_edit_key(state, provider, &mut draft, key); + return; + } + if state.task_draft_dialog == SavedCommandDialog::ConfirmDelete { + handle_task_draft_delete_confirmation(state, provider, key); + return; + } + if state.task_draft_dialog == SavedCommandDialog::ConfirmCreatePlan { + handle_task_plan_confirmation(state, provider, key); + return; + } + handle_task_draft_list_key(state, provider, key); +} + +#[cfg(unix)] +fn handle_task_plan_confirmation( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) { + match key.code { + KeyCode::Char('y') + if provider.capabilities.supports(TaskCapabilities::TASK_PLANS) + && state.selected_task_draft < provider.task_drafts.len() => + { + let draft = provider.task_drafts[state.selected_task_draft].clone(); + match (provider.task_plans.create)(&draft) { + Ok(plan) => { + provider.task_plans.summaries.push(plan.clone()); + provider.task_plans.summaries = + bounded_task_plan_summaries(&provider.task_plans.summaries); + state.selected_task_plan = + provider.task_plans.summaries.len().saturating_sub(1); + state.task_draft_message = Some(format!( + "Plan {} created; no worker was started.", + bounded_display_text(&plan.id, 128) + )); + } + Err(error) => { + state.task_draft_message = + Some(format!("Could not create plan: {}", error.kind())); + } + } + state.task_draft_dialog = SavedCommandDialog::Editing; + } + KeyCode::Char('n') | KeyCode::Esc => { + state.task_draft_dialog = SavedCommandDialog::Editing; + } + _ => {} + } +} + +#[cfg(unix)] +fn handle_task_draft_edit_key( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + draft: &mut TaskDraftEdit, + key: KeyEvent, +) { + if state.task_draft_dialog == SavedCommandDialog::ConfirmDiscard { + match key.code { + KeyCode::Char('y') => { + state.task_draft = None; + state.task_draft_dialog = SavedCommandDialog::Editing; + } + KeyCode::Char('n') | KeyCode::Esc => { + state.task_draft = Some(draft.clone()); + state.task_draft_dialog = SavedCommandDialog::Editing; + } + _ => state.task_draft = Some(draft.clone()), + } + return; + } + state.task_draft_message = None; + match key.code { + KeyCode::Esc => { + let original = draft + .editing_index + .and_then(|index| provider.task_drafts.get(index)); + if original + .is_some_and(|saved| saved.title == draft.title && saved.prompt == draft.prompt) + { + state.task_draft = None; + } else { + state.task_draft = Some(draft.clone()); + state.task_draft_dialog = SavedCommandDialog::ConfirmDiscard; + } + } + KeyCode::Char('s') if key.modifiers.contains(KeyModifiers::CONTROL) => { + save_task_draft(state, provider, draft); + if state.task_draft_message.is_some() && state.task_draft.is_none() { + state.task_draft = Some(draft.clone()); + } + } + KeyCode::Tab => { + draft.active_field = match draft.active_field { + TaskDraftField::Title => TaskDraftField::Paths, + TaskDraftField::Paths => TaskDraftField::Dependencies, + TaskDraftField::Dependencies => TaskDraftField::QualityGate, + TaskDraftField::QualityGate => TaskDraftField::Prompt, + TaskDraftField::Prompt => TaskDraftField::Title, + }; + state.task_draft = Some(draft.clone()); + } + KeyCode::Enter if draft.active_field == TaskDraftField::Title => { + draft.active_field = TaskDraftField::Paths; + state.task_draft = Some(draft.clone()); + } + _ => { + edit_task_draft(draft, key); + state.task_draft = Some(draft.clone()); + } + } +} + +#[cfg(unix)] +fn handle_task_draft_delete_confirmation( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) { + match key.code { + KeyCode::Char('y') => { + if state.selected_task_draft < provider.task_drafts.len() { + let mut updated = provider.task_drafts.clone(); + updated.remove(state.selected_task_draft); + match (provider.save_task_drafts)(&updated) { + Ok(()) => provider.task_drafts = updated, + Err(error) => { + state.task_draft_message = + Some(format!("Could not delete draft: {}", error.kind())); + } + } + state.selected_task_draft = state + .selected_task_draft + .min(provider.task_drafts.len().saturating_sub(1)); + } + state.task_draft_dialog = SavedCommandDialog::Editing; + } + KeyCode::Char('n') | KeyCode::Esc => state.task_draft_dialog = SavedCommandDialog::Editing, + _ => {} + } +} + +#[cfg(unix)] +fn handle_task_draft_list_key( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) { + state.task_draft_message = None; + if state.task_draft_panel == TaskDraftPanel::Plans { + match key.code { + KeyCode::Esc => state.task_draft_panel = TaskDraftPanel::Drafts, + KeyCode::Up if !provider.task_plans.summaries.is_empty() => { + state.selected_task_plan = if state.selected_task_plan == 0 { + provider.task_plans.summaries.len() - 1 + } else { + state.selected_task_plan - 1 + }; + } + KeyCode::Down if !provider.task_plans.summaries.is_empty() => { + state.selected_task_plan = + (state.selected_task_plan + 1) % provider.task_plans.summaries.len(); + } + KeyCode::Char('r') if provider.capabilities.supports(TaskCapabilities::TASK_PLANS) => { + match (provider.task_plans.load)() { + Ok(plans) => { + provider.task_plans.summaries = bounded_task_plan_summaries(&plans); + state.selected_task_plan = state + .selected_task_plan + .min(provider.task_plans.summaries.len().saturating_sub(1)); + } + Err(error) => { + state.task_draft_message = + Some(format!("Could not refresh plans: {}", error.kind())); + } + } + } + _ => {} + } + return; + } + match key.code { + KeyCode::Esc => state.task_drafts_panel = SavedCommandsPanel::Closed, + KeyCode::Up if !provider.task_drafts.is_empty() => { + state.selected_task_draft = if state.selected_task_draft == 0 { + provider.task_drafts.len() - 1 + } else { + state.selected_task_draft - 1 + }; + } + KeyCode::Down if !provider.task_drafts.is_empty() => { + state.selected_task_draft = + (state.selected_task_draft + 1) % provider.task_drafts.len(); + } + KeyCode::Char('n') if provider.task_drafts.len() < MAX_TASK_DRAFTS => { + state.task_draft = Some(TaskDraftEdit::default()); + } + KeyCode::Char('n') => { + state.task_draft_message = + Some(format!("At most {MAX_TASK_DRAFTS} drafts can be saved.")); + } + KeyCode::Enter if state.selected_task_draft < provider.task_drafts.len() => { + let saved = &provider.task_drafts[state.selected_task_draft]; + state.task_draft = Some(TaskDraftEdit { + editing_index: Some(state.selected_task_draft), + title_cursor: saved.title.len(), + paths_cursor: saved.paths.len(), + dependencies_cursor: saved.dependencies.len(), + quality_gate_cursor: saved.quality_gate.len(), + prompt_cursor: saved.prompt.len(), + title: saved.title.clone(), + paths: saved.paths.clone(), + dependencies: saved.dependencies.clone(), + quality_gate: saved.quality_gate.clone(), + prompt: saved.prompt.clone(), + active_field: TaskDraftField::Title, + }); + } + KeyCode::Char('d') if state.selected_task_draft < provider.task_drafts.len() => { + state.task_draft_dialog = SavedCommandDialog::ConfirmDelete; + } + KeyCode::Char('l') if provider.capabilities.supports(TaskCapabilities::TASK_PLANS) => { + state.task_draft_panel = TaskDraftPanel::Plans; + state.selected_task_plan = 0; + match (provider.task_plans.load)() { + Ok(plans) => provider.task_plans.summaries = bounded_task_plan_summaries(&plans), + Err(error) => { + state.task_draft_message = + Some(format!("Could not load plans: {}", error.kind())); + } + } + } + KeyCode::Char('p') + if provider.capabilities.supports(TaskCapabilities::TASK_PLANS) + && state.selected_task_draft < provider.task_drafts.len() => + { + state.task_draft_dialog = SavedCommandDialog::ConfirmCreatePlan; + } + _ => {} + } +} + +#[cfg(unix)] +fn edit_task_draft(draft: &mut TaskDraftEdit, key: KeyEvent) { + let (value, cursor, limit) = match draft.active_field { + TaskDraftField::Title => ( + &mut draft.title, + &mut draft.title_cursor, + MAX_TASK_DRAFT_TITLE_BYTES, + ), + TaskDraftField::Paths => ( + &mut draft.paths, + &mut draft.paths_cursor, + MAX_TASK_DRAFT_PATHS_BYTES, + ), + TaskDraftField::Dependencies => ( + &mut draft.dependencies, + &mut draft.dependencies_cursor, + MAX_TASK_DRAFT_DEPENDENCIES_BYTES, + ), + TaskDraftField::QualityGate => ( + &mut draft.quality_gate, + &mut draft.quality_gate_cursor, + MAX_TASK_DRAFT_GATE_BYTES, + ), + TaskDraftField::Prompt => ( + &mut draft.prompt, + &mut draft.prompt_cursor, + MAX_TASK_DRAFT_PROMPT_BYTES, + ), + }; + if key.code == KeyCode::Enter && draft.active_field != TaskDraftField::Title { + if value.len() < limit { + value.insert(*cursor, '\n'); + *cursor += 1; + } + return; + } + match key.code { + KeyCode::Left if *cursor > 0 => { + *cursor -= 1; + while !value.is_char_boundary(*cursor) { + *cursor -= 1; + } + } + KeyCode::Right if *cursor < value.len() => { + *cursor += 1; + while !value.is_char_boundary(*cursor) { + *cursor += 1; + } + } + KeyCode::Home => *cursor = 0, + KeyCode::End => *cursor = value.len(), + KeyCode::Backspace if *cursor > 0 => { + let mut start = *cursor - 1; + while !value.is_char_boundary(start) { + start -= 1; + } + value.drain(start..*cursor); + *cursor = start; + } + KeyCode::Delete if *cursor < value.len() => { + let mut end = *cursor + 1; + while !value.is_char_boundary(end) { + end += 1; + } + value.drain(*cursor..end); + } + KeyCode::Char(character) + if !key + .modifiers + .intersects(KeyModifiers::CONTROL | KeyModifiers::ALT) + && !character.is_control() + && !is_bidi_format(character) + && value.len().saturating_add(character.len_utf8()) <= limit => + { + value.insert(*cursor, character); + *cursor += character.len_utf8(); + } + _ => {} + } +} + +#[cfg(unix)] +fn save_task_draft( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + draft: &TaskDraftEdit, +) { + let value = TaskDraft { + title: draft.title.clone(), + paths: draft.paths.clone(), + dependencies: draft.dependencies.clone(), + quality_gate: draft.quality_gate.clone(), + prompt: draft.prompt.clone(), + }; + if !task_draft_is_valid(&value) { + state.task_draft_message = Some( + "Draft exceeds its title or prompt byte limit or contains unsupported control text." + .to_owned(), + ); + return; + } + let mut updated = provider.task_drafts.clone(); + let selected = if let Some(index) = draft.editing_index { + let Some(existing) = updated.get_mut(index) else { + state.task_draft_message = Some("Selected draft no longer exists.".to_owned()); + return; + }; + *existing = value; + index + } else { + if updated.len() >= MAX_TASK_DRAFTS { + state.task_draft_message = + Some(format!("At most {MAX_TASK_DRAFTS} drafts can be saved.")); + return; + } + updated.push(value); + updated.len() - 1 + }; + match (provider.save_task_drafts)(&updated) { + Ok(()) => { + provider.task_drafts = updated; + state.selected_task_draft = selected; + state.task_draft = None; + state.task_draft_dialog = SavedCommandDialog::Editing; + } + Err(error) => { + state.task_draft_message = Some(format!("Could not save draft: {}", error.kind())); + } + } +} + +#[cfg(unix)] +fn handle_saved_commands_key( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) { + if key.kind != KeyEventKind::Press { + return; + } + if let Some(draft) = state.saved_command_draft.take() { + handle_saved_command_editor_key(state, provider, draft, key); + return; + } + handle_saved_command_list_key(state, provider, key); +} + +#[cfg(unix)] +fn handle_saved_command_list_key( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) { + if state.saved_command_dialog == SavedCommandDialog::ConfirmDelete { + match key.code { + KeyCode::Char('y') if state.selected_saved_command < provider.saved_commands.len() => { + let mut updated = provider.saved_commands.clone(); + updated.remove(state.selected_saved_command); + match (provider.save_saved_commands)(&updated) { + Ok(()) => provider.saved_commands = updated, + Err(error) => { + state.saved_command_message = + Some(format!("Could not delete command: {}", error.kind())); + } + } + state.selected_saved_command = state + .selected_saved_command + .min(provider.saved_commands.len().saturating_sub(1)); + state.saved_command_dialog = SavedCommandDialog::Editing; + } + KeyCode::Char('n') | KeyCode::Esc => { + state.saved_command_dialog = SavedCommandDialog::Editing; + } + _ => {} + } + return; + } + state.saved_command_message = None; + match key.code { + KeyCode::Esc => state.saved_commands_panel = SavedCommandsPanel::Closed, + KeyCode::Up if !provider.saved_commands.is_empty() => { + state.selected_saved_command = if state.selected_saved_command == 0 { + provider.saved_commands.len() - 1 + } else { + state.selected_saved_command - 1 + }; + } + KeyCode::Down if !provider.saved_commands.is_empty() => { + state.selected_saved_command = + (state.selected_saved_command + 1) % provider.saved_commands.len(); + } + KeyCode::Char('n') if provider.saved_commands.len() < MAX_SAVED_COMMANDS => { + state.saved_command_draft = Some(SavedCommandDraft::default()); + } + KeyCode::Enter if state.selected_saved_command < provider.saved_commands.len() => { + let saved = &provider.saved_commands[state.selected_saved_command]; + state.saved_command_draft = Some(SavedCommandDraft { + editing_index: Some(state.selected_saved_command), + name: saved.name.clone(), + command: saved.command.clone(), + name_cursor: saved.name.len(), + command_cursor: saved.command.len(), + active_field: SavedCommandField::Name, + }); + } + KeyCode::Char('d') if state.selected_saved_command < provider.saved_commands.len() => { + state.saved_command_dialog = SavedCommandDialog::ConfirmDelete; + } + KeyCode::Char('r') if state.selected_saved_command < provider.saved_commands.len() => { + state.pending_saved_command = Some(state.selected_saved_command); + } + KeyCode::Char('n') => { + state.saved_command_message = Some(format!( + "At most {MAX_SAVED_COMMANDS} commands can be saved." + )); + } + _ => {} + } +} + +#[cfg(unix)] +fn handle_saved_command_editor_key( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + mut draft: SavedCommandDraft, + key: KeyEvent, +) { + if state.saved_command_dialog == SavedCommandDialog::ConfirmDiscard { + state.saved_command_draft = Some(draft); + handle_saved_command_discard_confirmation(state, key); + return; + } + state.saved_command_message = None; + match key.code { + KeyCode::Esc => { + let original = draft + .editing_index + .and_then(|index| provider.saved_commands.get(index)); + if original + .is_some_and(|saved| saved.name == draft.name && saved.command == draft.command) + { + state.saved_command_draft = None; + return; + } + state.saved_command_dialog = SavedCommandDialog::ConfirmDiscard; + state.saved_command_draft = Some(draft); + } + KeyCode::Char('s') if key.modifiers.contains(KeyModifiers::CONTROL) => { + save_saved_command(state, provider, &draft); + if state.saved_command_message.is_some() && state.saved_command_draft.is_none() { + state.saved_command_draft = Some(draft); + } + } + KeyCode::Tab => { + draft.active_field = match draft.active_field { + SavedCommandField::Name => SavedCommandField::Command, + SavedCommandField::Command => SavedCommandField::Name, + }; + state.saved_command_draft = Some(draft); + } + KeyCode::Enter if draft.active_field == SavedCommandField::Name => { + draft.active_field = SavedCommandField::Command; + state.saved_command_draft = Some(draft); + } + _ => { + edit_saved_command_field(&mut draft, key); + state.saved_command_draft = Some(draft); + } + } +} + +#[cfg(unix)] +fn edit_saved_command_field(draft: &mut SavedCommandDraft, key: KeyEvent) { + let (value, cursor, limit) = match draft.active_field { + SavedCommandField::Name => ( + &mut draft.name, + &mut draft.name_cursor, + MAX_SAVED_COMMAND_NAME_BYTES, + ), + SavedCommandField::Command => ( + &mut draft.command, + &mut draft.command_cursor, + MAX_SAVED_COMMAND_LINE_BYTES, + ), + }; + match key.code { + KeyCode::Left => { + while *cursor > 0 { + *cursor -= 1; + if value.is_char_boundary(*cursor) { + break; + } + } + } + KeyCode::Right => { + if *cursor < value.len() { + *cursor += 1; + while !value.is_char_boundary(*cursor) { + *cursor += 1; + } + } + } + KeyCode::Home => *cursor = 0, + KeyCode::End => *cursor = value.len(), + KeyCode::Backspace if *cursor > 0 => { + let mut start = *cursor - 1; + while !value.is_char_boundary(start) { + start -= 1; + } + value.drain(start..*cursor); + *cursor = start; + } + KeyCode::Delete if *cursor < value.len() => { + let mut end = *cursor + 1; + while !value.is_char_boundary(end) { + end += 1; + } + value.drain(*cursor..end); + } + KeyCode::Char(character) + if !key + .modifiers + .intersects(KeyModifiers::CONTROL | KeyModifiers::ALT) + && !character.is_control() + && !is_bidi_format(character) + && value.len().saturating_add(character.len_utf8()) <= limit => + { + value.insert(*cursor, character); + *cursor += character.len_utf8(); + } + _ => {} + } +} + +#[cfg(unix)] +fn save_saved_command( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + draft: &SavedCommandDraft, +) { + let command = SavedCommand { + name: draft.name.clone(), + command: draft.command.clone(), + }; + if !saved_command_is_valid(&command) { + state.saved_command_message = Some( + "Name and one-line command must be nonempty, printable, and within their byte limits." + .to_owned(), + ); + return; + } + if provider + .saved_commands + .iter() + .enumerate() + .any(|(index, saved)| Some(index) != draft.editing_index && saved.name == command.name) + { + state.saved_command_message = Some("Saved command names must be unique.".to_owned()); + return; + } + let mut updated = provider.saved_commands.clone(); + let selected = if let Some(index) = draft.editing_index { + let Some(existing) = updated.get_mut(index) else { + state.saved_command_message = Some("Selected command no longer exists.".to_owned()); + return; + }; + *existing = command; + index + } else { + if updated.len() >= MAX_SAVED_COMMANDS { + state.saved_command_message = Some(format!( + "At most {MAX_SAVED_COMMANDS} commands can be saved." + )); + return; + } + updated.push(command); + updated.len() - 1 + }; + match (provider.save_saved_commands)(&updated) { + Ok(()) => { + provider.saved_commands = updated; + state.selected_saved_command = selected; + state.saved_command_draft = None; + state.saved_command_dialog = SavedCommandDialog::Editing; + } + Err(error) => { + state.saved_command_message = Some(format!("Could not save command: {}", error.kind())); + } + } +} + +#[cfg(unix)] +fn handle_saved_command_discard_confirmation(state: &mut AttachedInputState, key: KeyEvent) { + match key.code { + KeyCode::Char('y') => { + state.saved_command_draft = None; + state.saved_command_dialog = SavedCommandDialog::Editing; + } + KeyCode::Char('n') | KeyCode::Esc => { + state.saved_command_dialog = SavedCommandDialog::Editing; + } + _ => {} + } +} + +#[cfg(unix)] +fn handle_saved_command_confirmation( + state: &mut AttachedInputState, + provider: &TaskProvider<'_>, + key: KeyEvent, + mut send_input: impl FnMut(&[u8]) -> io::Result<()>, +) { + if key.kind != KeyEventKind::Press { + return; + } + match key.code { + KeyCode::Char('y') => { + let Some(command) = state + .pending_saved_command + .and_then(|index| provider.saved_commands.get(index)) + else { + state.pending_saved_command = None; + state.saved_command_message = + Some("Selected saved command is unavailable.".to_owned()); + return; + }; + if !saved_command_is_valid(command) { + state.pending_saved_command = None; + state.saved_command_message = Some("Saved command failed validation.".to_owned()); + return; + } + let mut input = command.command.as_bytes().to_vec(); + input.push(b'\r'); + state.pending_saved_command = None; + if let Err(error) = send_input(&input) { + state.saved_command_message = Some(format!( + "Could not send command input; check the session before retrying: {}", + error.kind() + )); + } + } + KeyCode::Char('n') | KeyCode::Esc => state.pending_saved_command = None, + _ => {} + } +} + +#[cfg(unix)] +fn handle_command_palette_key( + workspace: &mut Workspace, + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, + on_change: &mut impl FnMut(&Workspace) -> io::Result<()>, +) -> io::Result<()> { + if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat { + return Ok(()); + } + match key.code { + KeyCode::Esc => state.command_palette_query = None, + KeyCode::Enter => { + let query = state.command_palette_query.as_deref().unwrap_or_default(); + let actions = palette_actions(provider.capabilities, query); + let Some(action) = actions.get(state.command_palette_selected).copied() else { + state.command_palette_selected = 0; + return Ok(()); + }; + state.command_palette_query = None; + dispatch_palette_action(action, workspace, state, provider, on_change)?; + } + KeyCode::Up | KeyCode::Down => { + let query = state.command_palette_query.as_deref().unwrap_or_default(); + let action_count = palette_actions(provider.capabilities, query).len(); + if action_count > 0 { + state.command_palette_selected = if key.code == KeyCode::Up { + if state.command_palette_selected == 0 { + action_count - 1 + } else { + state.command_palette_selected - 1 + } + } else { + (state.command_palette_selected + 1) % action_count + }; + } else { + state.command_palette_selected = 0; + } + } + KeyCode::Backspace => { + if let Some(query) = state.command_palette_query.as_mut() { + query.pop(); + } + state.command_palette_selected = 0; + } + KeyCode::Char(character) + if !key + .modifiers + .intersects(KeyModifiers::CONTROL | KeyModifiers::ALT) + && !character.is_control() + && state.command_palette_query.as_ref().is_some_and(|query| { + query.len().saturating_add(character.len_utf8()) <= MAX_PALETTE_QUERY_BYTES + }) => + { + if let Some(query) = state.command_palette_query.as_mut() { + query.push(character); + } + state.command_palette_selected = 0; + } + _ => {} + } + Ok(()) +} + +#[cfg(unix)] +fn dispatch_palette_action( + action: PaletteAction, + workspace: &mut Workspace, + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + on_change: &mut impl FnMut(&Workspace) -> io::Result<()>, +) -> io::Result<()> { + match action { + PaletteAction::OpenTasks => { + state.task_panel = TaskPanelState::List; + if !provider.tasks.is_empty() { + state.selected_task = state.selected_task.min(provider.tasks.len() - 1); + } + } + PaletteAction::OpenFiles => { + state.file_directory.clear(); + state.file_selected = 0; + state.file_include_hidden = false; + state.file_message = None; + state.file_panel = FilePanelState::Tree; + refresh_file_directory(state, provider, None); + } + PaletteAction::QuickOpen => start_quick_open(state, provider, FilePanelState::Closed), + PaletteAction::OpenNotes => { + state.workspace_notes_panel = WorkspaceNotesPanel::List; + state.selected_workspace_note = 0; + state.workspace_notes_draft = None; + state.workspace_notes_editing_index = None; + state.workspace_notes_state = WorkspaceNotesState::Editing; + state.workspace_notes_message = None; + } + PaletteAction::OpenSavedCommands => { + state.saved_commands_panel = SavedCommandsPanel::List; + state.selected_saved_command = 0; + state.saved_command_message = None; + } + PaletteAction::OpenTaskDrafts => { + state.task_drafts_panel = SavedCommandsPanel::List; + state.selected_task_draft = 0; + state.task_draft_message = None; + } + PaletteAction::OpenAgentSession => { + state.agent_session_panel = AgentSessionPanel::Details; + state.agent_session_message = None; + } + PaletteAction::RefreshTasks => { + state.refresh_request = RefreshRequest::Requested; + state.task_refresh_message = None; + } + PaletteAction::KeyboardHelp => state.help_open = true, + PaletteAction::NextTab | PaletteAction::PreviousTab => { + focus_neighbor_tab(workspace, action == PaletteAction::NextTab); + on_change(workspace)?; + } + } + Ok(()) +} + +#[cfg(unix)] +fn palette_actions(capabilities: TaskCapabilities, query: &str) -> Vec { + let all = [ + (PaletteAction::OpenTasks, "Open task board"), + (PaletteAction::KeyboardHelp, "Keyboard help"), + (PaletteAction::NextTab, "Next workspace tab"), + (PaletteAction::PreviousTab, "Previous workspace tab"), + (PaletteAction::RefreshTasks, "Refresh task board"), + (PaletteAction::OpenFiles, "Open repository file tree"), + (PaletteAction::QuickOpen, "Quick open repository file"), + (PaletteAction::OpenNotes, "Open repository notes"), + ( + PaletteAction::OpenSavedCommands, + "Manage saved shell commands", + ), + (PaletteAction::OpenTaskDrafts, "Manage task briefing drafts"), + (PaletteAction::OpenAgentSession, "Open saved agent session"), + ]; + let query = query.to_lowercase(); + all.into_iter() + .filter(|(action, _label)| match action { + PaletteAction::RefreshTasks => capabilities.supports(TaskCapabilities::REFRESH), + PaletteAction::OpenFiles | PaletteAction::QuickOpen => { + capabilities.supports(TaskCapabilities::FILES) + } + PaletteAction::OpenNotes => capabilities.supports(TaskCapabilities::NOTES), + PaletteAction::OpenSavedCommands => { + capabilities.supports(TaskCapabilities::SAVED_COMMANDS) + } + PaletteAction::OpenTaskDrafts => capabilities.supports(TaskCapabilities::TASK_DRAFTS), + PaletteAction::OpenAgentSession => { + capabilities.supports(TaskCapabilities::AGENT_SESSION) + } + _ => true, + }) + .filter(|(_, label)| label.to_lowercase().contains(&query)) + .map(|(action, _)| action) + .collect() +} + +#[cfg(unix)] +fn render_command_palette( + frame: &mut Frame<'_>, + query: &str, + selected: usize, + capabilities: TaskCapabilities, +) { + let area = frame.area(); + if area.width < 28 || area.height < 8 { + return; + } + let popup = centered_rect(64, 62, area); + frame.render_widget(Clear, popup); + let actions = palette_actions(capabilities, query); + let mut lines = vec![Line::from(format!( + "Command: {}_", + bounded_display_text(query, MAX_PALETTE_QUERY_BYTES) + ))]; + if actions.is_empty() { + lines.push(Line::from("No matching available commands.")); + } else { + for (index, action) in actions.iter().take(12).enumerate() { + let marker = if index == selected { "> " } else { " " }; + let style = if index == selected { + Style::default() + .fg(Color::Black) + .bg(Color::Cyan) + .add_modifier(Modifier::BOLD) + } else { + Style::default() + }; + lines.push(Line::from(Span::styled( + format!("{marker}{}", palette_action_label(*action)), + style, + ))); + } + } + lines.push(Line::from("Type to filter · Enter run · Esc close")); + frame.render_widget( + Paragraph::new(lines).block( + Block::default() + .title(" Rover command palette ") + .borders(Borders::ALL), + ), + popup, + ); +} + +#[cfg(unix)] +fn palette_action_label(action: PaletteAction) -> &'static str { + match action { + PaletteAction::OpenTasks => "Open task board", + PaletteAction::OpenFiles => "Open repository file tree", + PaletteAction::QuickOpen => "Quick open repository file", + PaletteAction::OpenNotes => "Open repository notes", + PaletteAction::OpenSavedCommands => "Manage saved shell commands", + PaletteAction::OpenTaskDrafts => "Manage task briefing drafts", + PaletteAction::OpenAgentSession => "Open saved agent session", + PaletteAction::RefreshTasks => "Refresh task board", + PaletteAction::KeyboardHelp => "Keyboard help", + PaletteAction::NextTab => "Next workspace tab", + PaletteAction::PreviousTab => "Previous workspace tab", + } +} + +#[cfg(unix)] +fn open_file_browser_shortcut( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) -> bool { + if !state.prefix + || state.task_panel != TaskPanelState::Closed + || key.kind != KeyEventKind::Press + || !provider.capabilities.supports(TaskCapabilities::FILES) + { + return false; + } + match key.code { + KeyCode::Char('f') => { + state.prefix = false; + state.file_directory.clear(); + state.file_selected = 0; + state.file_include_hidden = false; + state.file_message = None; + state.file_panel = FilePanelState::Tree; + refresh_file_directory(state, provider, None); + true + } + KeyCode::Char('.') => { + state.prefix = false; + start_quick_open(state, provider, FilePanelState::Closed); + true + } + _ => false, + } +} + +#[cfg(unix)] +fn handle_file_panel_key( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) { + if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat { + return; + } + match state.file_panel { + FilePanelState::Tree => handle_file_tree_key(state, provider, key), + FilePanelState::QuickOpen if state.quick_open_query.is_some() => { + handle_quick_open_edit(state, provider, key); + } + FilePanelState::QuickOpen => handle_quick_open_key(state, provider, key), + FilePanelState::Viewer => handle_file_preview_key(state, provider, key), + FilePanelState::Editing => handle_file_editor_key(state, provider, key), + FilePanelState::DiffPreview => handle_file_diff_key(state, key), + FilePanelState::Closed => {} + } +} + +#[cfg(unix)] +fn handle_file_tree_key( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) { + let entries = state + .file_listing + .as_ref() + .map_or(&[][..], |listing| listing.entries.as_slice()); + match key.code { + KeyCode::Esc => state.file_panel = FilePanelState::Closed, + KeyCode::Up if !entries.is_empty() => { + state.file_selected = state.file_selected.saturating_sub(1); + } + KeyCode::Down if !entries.is_empty() => { + state.file_selected = (state.file_selected + 1).min(entries.len() - 1); + } + KeyCode::Enter => { + if let Some(entry) = entries.get(state.file_selected).cloned() { + match entry.kind { + BrowserEntryKind::Directory => { + state.file_directory = entry.path; + state.file_selected = 0; + state.file_message = None; + refresh_file_directory(state, provider, None); + } + BrowserEntryKind::File => { + open_file_preview(state, provider, &entry.path, FilePanelState::Tree); + } + BrowserEntryKind::Symlink | BrowserEntryKind::Other => { + state.file_message = Some( + "This entry is not opened; symlinks and special files are refused." + .to_owned(), + ); + } + } + } + } + KeyCode::Backspace => { + if let Some((parent, _)) = state.file_directory.rsplit_once('/') { + state.file_directory = parent.to_owned(); + } else { + state.file_directory.clear(); + } + state.file_selected = 0; + state.file_message = None; + refresh_file_directory(state, provider, None); + } + KeyCode::Char('h') => { + let selected = entries + .get(state.file_selected) + .map(|entry| entry.path.clone()); + state.file_include_hidden = !state.file_include_hidden; + refresh_file_directory(state, provider, selected.as_deref()); + } + KeyCode::Char('.') => start_quick_open(state, provider, FilePanelState::Tree), + KeyCode::Char('r') => { + let selected = entries + .get(state.file_selected) + .map(|entry| entry.path.clone()); + refresh_file_directory(state, provider, selected.as_deref()); + } + _ => {} + } +} + +#[cfg(unix)] +fn refresh_file_directory( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + preserve_path: Option<&str>, +) { + state.file_message = None; + match (provider.load_directory)(&state.file_directory, state.file_include_hidden) { + Ok(listing) => { + state.file_selected = preserve_path + .and_then(|path| listing.entries.iter().position(|entry| entry.path == path)) + .unwrap_or(0); + state.file_listing = Some(listing); + } + Err(error) => { + state.file_listing = None; + state.file_selected = 0; + state.file_message = Some(bounded_display_text( + &format!("Unable to list repository files: {error}"), + 120, + )); + } + } +} + +#[cfg(unix)] +fn start_quick_open( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + return_to: FilePanelState, +) { + state.quick_open_return = return_to; + state.quick_open_query = Some(String::new()); + state.quick_open_selected = 0; + state.file_message = None; + state.file_panel = FilePanelState::QuickOpen; + match (provider.load_quick_open)() { + Ok(listing) => { + state.quick_open_entries = listing.entries; + if listing.truncated { + state.file_message = + Some("Quick-open scan reached its 10,000-entry limit.".to_owned()); + } + if let Some(error) = listing.git_error { + state.file_message = Some(bounded_display_text( + &format!("Git status unavailable: {error}"), + 120, + )); + } + } + Err(error) => { + state.quick_open_entries.clear(); + state.file_message = Some(bounded_display_text( + &format!("Quick-open scan failed: {error}"), + 120, + )); + } + } +} + +#[cfg(unix)] +fn handle_quick_open_edit( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) { + let Some(query) = state.quick_open_query.as_mut() else { + return; + }; + match key.code { + KeyCode::Esc => { + state.quick_open_query = None; + state.file_panel = state.quick_open_return; + } + KeyCode::Enter => { + let selected = quick_open_matches(state) + .into_iter() + .find(|index| *index == state.quick_open_selected) + .and_then(|index| state.quick_open_entries.get(index)) + .map(|entry| entry.path.clone()); + if let Some(path) = selected { + open_file_preview(state, provider, &path, FilePanelState::QuickOpen); + state.quick_open_query = None; + } + } + KeyCode::Backspace => { + query.pop(); + state.quick_open_selected = quick_open_matches(state).first().copied().unwrap_or(0); + } + KeyCode::Up => move_quick_open_selection(state, false), + KeyCode::Down => move_quick_open_selection(state, true), + KeyCode::Char(character) + if !key + .modifiers + .intersects(KeyModifiers::CONTROL | KeyModifiers::ALT) + && !character.is_control() + && query.len().saturating_add(character.len_utf8()) <= MAX_FILE_QUERY_BYTES => + { + query.push(character); + state.quick_open_selected = quick_open_matches(state).first().copied().unwrap_or(0); + } + _ => {} + } +} + +#[cfg(unix)] +fn handle_quick_open_key( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) { + let indices = quick_open_matches(state); + match key.code { + KeyCode::Esc => { + state.file_panel = state.quick_open_return; + } + KeyCode::Up if !indices.is_empty() => { + let position = indices + .iter() + .position(|index| *index == state.quick_open_selected) + .unwrap_or(0); + let previous = if position == 0 { + indices.len() - 1 + } else { + position - 1 + }; + state.quick_open_selected = indices[previous]; + } + KeyCode::Down if !indices.is_empty() => { + let position = indices + .iter() + .position(|index| *index == state.quick_open_selected) + .unwrap_or(indices.len() - 1); + state.quick_open_selected = indices[(position + 1) % indices.len()]; + } + KeyCode::Enter => { + if let Some(entry) = state.quick_open_entries.get(state.quick_open_selected) { + open_file_preview( + state, + provider, + &entry.path.clone(), + FilePanelState::QuickOpen, + ); + } + } + _ => {} + } +} + +#[cfg(unix)] +fn move_quick_open_selection(state: &mut AttachedInputState, forward: bool) { + let indices = quick_open_matches(state); + if indices.is_empty() { + state.quick_open_selected = 0; + return; + } + let position = indices + .iter() + .position(|index| *index == state.quick_open_selected) + .unwrap_or(0); + let next = if forward { + (position + 1) % indices.len() + } else if position == 0 { + indices.len() - 1 + } else { + position - 1 + }; + state.quick_open_selected = indices[next]; +} + +#[cfg(unix)] +fn open_file_preview( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + path: &str, + return_to: FilePanelState, +) { + state.file_preview_scroll = 0; + state.file_edit_capability = if provider.capabilities.supports(TaskCapabilities::FILE_EDIT) { + FileEditCapability::Enabled + } else { + FileEditCapability::Disabled + }; + state.file_message = None; + match (provider.load_file_preview)(path) { + Ok((bytes, truncated)) => { + let binary = bytes.contains(&0); + let text = if binary { + String::new() + } else { + sanitize_screen_text(&String::from_utf8_lossy(&bytes)) + }; + state.file_preview = Some(FilePreviewState { + path: path.to_owned(), + text, + truncated, + binary, + }); + state.file_preview_return = return_to; + state.file_panel = FilePanelState::Viewer; + } + Err(error) => { + state.file_message = Some(bounded_display_text( + &format!("Unable to open repository file: {error}"), + 160, + )); + } + } +} + +#[cfg(unix)] +fn handle_file_preview_key( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) { + match key.code { + KeyCode::Esc | KeyCode::Enter => { + state.file_panel = state.file_preview_return; + state.file_preview = None; + } + KeyCode::Up => state.file_preview_scroll = state.file_preview_scroll.saturating_sub(1), + KeyCode::Down => state.file_preview_scroll = state.file_preview_scroll.saturating_add(1), + KeyCode::PageUp => state.file_preview_scroll = state.file_preview_scroll.saturating_sub(12), + KeyCode::PageDown => { + state.file_preview_scroll = state.file_preview_scroll.saturating_add(12); + } + KeyCode::Char('e') if provider.capabilities.supports(TaskCapabilities::FILE_EDIT) => { + start_file_edit(state, provider); + } + _ => {} + } +} + +#[cfg(unix)] +fn start_file_edit(state: &mut AttachedInputState, provider: &mut TaskProvider<'_>) { + let Some(preview) = state.file_preview.as_ref() else { + return; + }; + let path = preview.path.clone(); + state.file_message = None; + match (provider.load_file_for_edit)(&path) { + Ok(bytes) if bytes.len() <= MAX_FILE_EDIT_BYTES && !bytes.contains(&0) => { + match String::from_utf8(bytes) { + Ok(text) + if !text.chars().any(|character| { + character.is_control() && !matches!(character, '\n' | '\r' | '\t') + }) => + { + let digest = rover_core::Sha256Digest::of(text.as_bytes()).to_hex(); + state.file_edit = Some(FileEditState { + path, + original: text.clone(), + buffer: text, + original_sha256: digest, + cursor: 0, + discard_confirmation: false, + }); + state.file_panel = FilePanelState::Editing; + } + Ok(_) => { + state.file_message = Some( + "File contains terminal control characters; editing refused.".to_owned(), + ); + } + Err(_) => { + state.file_message = + Some("File is not valid UTF-8; editing refused.".to_owned()); + } + } + } + Ok(_) => { + state.file_message = Some("File is binary or exceeds the 8 MiB edit limit.".to_owned()); + } + Err(error) => { + state.file_message = Some(bounded_display_text( + &format!("Unable to load file for editing: {error}"), + 160, + )); + } + } +} + +#[cfg(unix)] +fn handle_file_editor_key( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) { + if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat { + return; + } + let Some(edit) = state.file_edit.as_mut() else { + state.file_panel = FilePanelState::Viewer; + return; + }; + if edit.discard_confirmation { + match key.code { + KeyCode::Char('y' | 'Y') => { + state.file_edit = None; + state.file_panel = FilePanelState::Viewer; + } + KeyCode::Char('n' | 'N') | KeyCode::Esc => { + edit.discard_confirmation = false; + } + _ => {} + } + return; + } + match key.code { + KeyCode::Esc => { + if edit.buffer == edit.original { + state.file_edit = None; + state.file_panel = FilePanelState::Viewer; + } else { + edit.discard_confirmation = true; + } + } + KeyCode::Char('s') if key.modifiers.contains(KeyModifiers::CONTROL) => { + save_file_edit(state, provider); + } + KeyCode::Char('d') if key.modifiers.contains(KeyModifiers::CONTROL) => { + state.file_diff_scroll = 0; + state.file_panel = FilePanelState::DiffPreview; + } + KeyCode::Char('e') if key.modifiers.contains(KeyModifiers::CONTROL) => { + handoff_file_edit_to_external_editor(state, provider); + } + KeyCode::Left => edit.cursor = previous_char_boundary(&edit.buffer, edit.cursor), + KeyCode::Right => edit.cursor = next_char_boundary(&edit.buffer, edit.cursor), + KeyCode::Home => edit.cursor = current_line_start(&edit.buffer, edit.cursor), + KeyCode::End => edit.cursor = current_line_end(&edit.buffer, edit.cursor), + KeyCode::Up => edit.cursor = vertical_cursor_move(&edit.buffer, edit.cursor, false), + KeyCode::Down => edit.cursor = vertical_cursor_move(&edit.buffer, edit.cursor, true), + KeyCode::Backspace if edit.cursor > 0 => { + let previous = previous_char_boundary(&edit.buffer, edit.cursor); + edit.buffer.replace_range(previous..edit.cursor, ""); + edit.cursor = previous; + } + KeyCode::Delete if edit.cursor < edit.buffer.len() => { + let next = next_char_boundary(&edit.buffer, edit.cursor); + edit.buffer.replace_range(edit.cursor..next, ""); + } + KeyCode::Enter if edit.buffer.len() < MAX_FILE_EDIT_BYTES => { + edit.buffer.insert(edit.cursor, '\n'); + edit.cursor += 1; + } + KeyCode::Char(character) + if !key + .modifiers + .intersects(KeyModifiers::CONTROL | KeyModifiers::ALT) + && !character.is_control() + && edit.buffer.len().saturating_add(character.len_utf8()) + <= MAX_FILE_EDIT_BYTES => + { + edit.buffer.insert(edit.cursor, character); + edit.cursor += character.len_utf8(); + } + _ => {} + } +} + +#[cfg(unix)] +fn save_file_edit(state: &mut AttachedInputState, provider: &mut TaskProvider<'_>) { + let Some(edit) = state.file_edit.as_ref() else { + return; + }; + if edit.buffer == edit.original { + state.file_message = Some("No edits to save.".to_owned()); + return; + } + match (provider.save_file)(&edit.path, &edit.original_sha256, edit.buffer.as_bytes()) { + Ok(()) => { + let path = edit.path.clone(); + let return_to = state.file_preview_return; + state.file_edit = None; + state.file_panel = FilePanelState::Viewer; + open_file_preview(state, provider, &path, return_to); + if state.file_preview.is_some() { + state.file_message = Some("File saved atomically.".to_owned()); + } + } + Err(error) => { + state.file_message = Some(bounded_display_text( + &format!("Save failed; edits remain in the buffer: {error}"), + 180, + )); + } + } +} + +#[cfg(unix)] +fn handoff_file_edit_to_external_editor( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, +) { + let Some(edit) = state.file_edit.as_ref() else { + return; + }; + let path = edit.path.clone(); + let input = edit.buffer.as_bytes().to_vec(); + state.file_message = None; + let result = with_terminal_suspended(|| (provider.external_file_editor)(&path, &input)); + state.terminal_handoff = TerminalHandoffState::NeedsRedraw; + match result { + Ok(bytes) if bytes.len() <= MAX_FILE_EDIT_BYTES && !bytes.contains(&0) => { + match String::from_utf8(bytes) { + Ok(text) + if !text.chars().any(|character| { + character.is_control() && !matches!(character, '\n' | '\r' | '\t') + }) => + { + if let Some(edit) = state.file_edit.as_mut() { + edit.cursor = text.len(); + edit.buffer = text; + edit.discard_confirmation = false; + state.file_message = Some( + "External editor returned; Ctrl-S saves through Rover.".to_owned(), + ); + } + } + Ok(_) => { + state.file_message = Some( + "External editor output contains terminal controls; buffer unchanged." + .to_owned(), + ); + } + Err(_) => { + state.file_message = Some( + "External editor output is not valid UTF-8; buffer unchanged.".to_owned(), + ); + } + } + } + Ok(_) => { + state.file_message = Some( + "External editor output is binary or exceeds the 8 MiB limit; buffer unchanged." + .to_owned(), + ); + } + Err(error) => { + state.file_message = Some(bounded_display_text( + &format!("External editor handoff failed; buffer retained: {error}"), + 180, + )); + } + } +} + +#[cfg(unix)] +fn with_terminal_suspended(action: impl FnOnce() -> io::Result) -> io::Result { + let mut guard = SuspendedTerminal::enter()?; + let result = action(); + guard.restore()?; + result +} + +#[cfg(unix)] +fn handle_file_diff_key(state: &mut AttachedInputState, key: KeyEvent) { + match key.code { + KeyCode::Esc | KeyCode::Enter => state.file_panel = FilePanelState::Editing, + KeyCode::Up => state.file_diff_scroll = state.file_diff_scroll.saturating_sub(1), + KeyCode::Down => state.file_diff_scroll = state.file_diff_scroll.saturating_add(1), + KeyCode::PageUp => state.file_diff_scroll = state.file_diff_scroll.saturating_sub(12), + KeyCode::PageDown => state.file_diff_scroll = state.file_diff_scroll.saturating_add(12), + _ => {} + } +} + +#[cfg(unix)] +fn previous_char_boundary(text: &str, cursor: usize) -> usize { + text[..cursor] + .char_indices() + .next_back() + .map_or(0, |(index, _)| index) +} + +#[cfg(unix)] +fn next_char_boundary(text: &str, cursor: usize) -> usize { + text[cursor..] + .char_indices() + .nth(1) + .map_or(text.len(), |(offset, _)| cursor + offset) +} + +#[cfg(unix)] +fn current_line_start(text: &str, cursor: usize) -> usize { + text[..cursor].rfind('\n').map_or(0, |index| index + 1) +} + +#[cfg(unix)] +fn current_line_end(text: &str, cursor: usize) -> usize { + text[cursor..] + .find('\n') + .map_or(text.len(), |offset| cursor + offset) +} + +#[cfg(unix)] +fn vertical_cursor_move(text: &str, cursor: usize, down: bool) -> usize { + let line_start = current_line_start(text, cursor); + let column = text[line_start..cursor].chars().count(); + let target_start = if down { + let line_end = current_line_end(text, cursor); + if line_end == text.len() { + return cursor; + } + line_end + 1 + } else { + if line_start == 0 { + return cursor; + } + let previous_end = line_start - 1; + text[..previous_end] + .rfind('\n') + .map_or(0, |index| index + 1) + }; + let target_end = text[target_start..] + .find('\n') + .map_or(text.len(), |offset| target_start + offset); + text[target_start..target_end] + .char_indices() + .nth(column) + .map_or(target_end, |(offset, _)| target_start + offset) +} + +#[cfg(unix)] +fn quick_open_matches(state: &AttachedInputState) -> Vec { + let query = state + .quick_open_query + .as_deref() + .unwrap_or_default() + .to_lowercase(); + state + .quick_open_entries + .iter() + .enumerate() + .filter_map(|(index, entry)| entry.path.to_lowercase().contains(&query).then_some(index)) + .collect() +} + +#[cfg(unix)] +fn attached_task_refresh_key(panel: TaskPanelState, key: KeyEvent) -> bool { + matches!(panel, TaskPanelState::List | TaskPanelState::Detail) + && key.code == KeyCode::Char('r') + && (key.kind == KeyEventKind::Press || key.kind == KeyEventKind::Repeat) +} + +#[cfg(unix)] +fn attached_task_cancel_key(panel: TaskPanelState, key: KeyEvent) -> bool { + matches!(panel, TaskPanelState::List | TaskPanelState::Detail) + && key.code == KeyCode::Char('c') + && (key.kind == KeyEventKind::Press || key.kind == KeyEventKind::Repeat) +} + +fn task_visible_indices(tasks: &[TaskSummary], preferences: &TaskViewPreferences) -> Vec { + let query = preferences.query.to_lowercase(); + let mut indices = tasks + .iter() + .enumerate() + .filter_map(|(index, task)| { + let matches = query.is_empty() + || [ + task.id.as_str(), + task.status.as_str(), + task.objective.as_str(), + task.candidate.as_deref().unwrap_or_default(), + task.error.as_deref().unwrap_or_default(), + ] + .into_iter() + .any(|field| field.to_lowercase().contains(&query)); + matches.then_some(index) + }) + .collect::>(); + indices.sort_by(|left, right| { + let left = &tasks[*left]; + let right = &tasks[*right]; + let order = match preferences.sort { + TaskSortOrder::Updated => right.updated_at.cmp(&left.updated_at), + TaskSortOrder::Status => left + .status + .to_lowercase() + .cmp(&right.status.to_lowercase()) + .then_with(|| right.updated_at.cmp(&left.updated_at)), + TaskSortOrder::Objective => left + .objective + .to_lowercase() + .cmp(&right.objective.to_lowercase()), + }; + order.then_with(|| left.id.cmp(&right.id)) + }); + indices +} + +#[cfg(unix)] +fn save_task_view_preferences(state: &mut AttachedInputState, provider: &mut TaskProvider<'_>) { + if let Err(error) = (provider.save_view_preferences)(&provider.view_preferences) { + state.task_refresh_message = Some(bounded_display_text( + &format!("Task view preference save failed: {error}"), + 100, + )); + } +} + +#[cfg(unix)] +fn handle_task_search_edit( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) { + if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat { + return; + } + let Some(draft) = state.task_search_draft.as_mut() else { + return; + }; + match key.code { + KeyCode::Esc => state.task_search_draft = None, + KeyCode::Enter => { + let committed = draft.trim().to_owned(); + state.task_search_draft = None; + provider.view_preferences.query = truncate_utf8(&committed, MAX_TASK_FILTER_BYTES); + state.selected_task = task_visible_indices(&provider.tasks, &provider.view_preferences) + .first() + .copied() + .unwrap_or(0); + save_task_view_preferences(state, provider); + } + KeyCode::Backspace => { + draft.pop(); + } + KeyCode::Char(character) + if !key + .modifiers + .intersects(KeyModifiers::CONTROL | KeyModifiers::ALT) + && !character.is_control() + && draft.len().saturating_add(character.len_utf8()) <= MAX_TASK_FILTER_BYTES => + { + draft.push(character); + } + _ => {} + } +} + +#[cfg(unix)] +fn attached_task_evidence_key(state: &mut AttachedInputState, key: KeyEvent) -> bool { + if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat { + return false; + } + match key.code { + KeyCode::Esc | KeyCode::Enter => state.task_panel = TaskPanelState::Detail, + KeyCode::Up => state.task_evidence_scroll = state.task_evidence_scroll.saturating_sub(1), + KeyCode::Down => state.task_evidence_scroll = state.task_evidence_scroll.saturating_add(1), + KeyCode::PageUp => { + state.task_evidence_scroll = state.task_evidence_scroll.saturating_sub(12); + } + KeyCode::PageDown => { + state.task_evidence_scroll = state.task_evidence_scroll.saturating_add(12); + } + _ => {} + } + true +} + +#[cfg(unix)] +fn open_selected_task_evidence( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) -> bool { + if (key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat) + || key.code != KeyCode::Char('i') + { + return false; + } + state.task_evidence = None; + state.task_evidence_message = None; + state.task_evidence_scroll = 0; + if let Some(task) = provider.tasks.get(state.selected_task).cloned() { + match (provider.load_evidence)(&task) { + Ok(Some(evidence)) + if task.investigation_id.as_deref() == Some(evidence.id.as_str()) + && task + .candidate + .as_deref() + .is_none_or(|candidate| candidate == evidence.candidate) => + { + state.task_evidence = Some(evidence); + } + Ok(Some(_)) => { + state.task_evidence_message = + Some("Evidence record does not match the selected task.".to_owned()); + } + Ok(None) => { + state.task_evidence_message = Some( + "No investigation yet; execution completion is not acceptance.".to_owned(), + ); + } + Err(error) => { + state.task_evidence_message = Some(bounded_display_text( + &format!("Unable to load task evidence: {error}"), + 180, + )); + } + } + } else { + state.task_evidence_message = Some("No task is selected.".to_owned()); + } + state.task_panel = TaskPanelState::Evidence; + true +} + +#[cfg(unix)] +fn attached_task_diff_key(state: &mut AttachedInputState, key: KeyEvent) -> bool { + if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat { + return false; + } + match key.code { + KeyCode::Esc | KeyCode::Enter => state.task_panel = TaskPanelState::Detail, + KeyCode::Up => state.task_diff_scroll = state.task_diff_scroll.saturating_sub(1), + KeyCode::Down => state.task_diff_scroll = state.task_diff_scroll.saturating_add(1), + KeyCode::PageUp => state.task_diff_scroll = state.task_diff_scroll.saturating_sub(12), + KeyCode::PageDown => state.task_diff_scroll = state.task_diff_scroll.saturating_add(12), + _ => {} + } + true +} + +#[cfg(unix)] +fn open_selected_task_diff( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) -> bool { + if (key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat) + || key.code != KeyCode::Char('d') + { + return false; + } + state.task_diff = None; + state.task_diff_message = None; + state.task_diff_scroll = 0; + if let Some(task) = provider.tasks.get(state.selected_task).cloned() { + match (provider.load_diff)(&task) { + Ok(Some(diff)) + if task.base_snapshot.as_deref() == Some(diff.base_snapshot.as_str()) + && task.candidate.as_deref() == Some(diff.candidate.as_str()) + && diff.changes.len() <= MAX_TASK_DIFF_RECORDS + && diff.changes.iter().all(|change| { + matches!(change.status.as_str(), "added" | "deleted" | "modified") + }) => + { + state.task_diff = Some(diff); + } + Ok(Some(_)) => { + state.task_diff_message = + Some("Snapshot comparison does not match the selected task.".to_owned()); + } + Ok(None) => { + state.task_diff_message = Some( + "No retained base and candidate snapshots are linked to this task.".to_owned(), + ); + } + Err(error) => { + state.task_diff_message = Some(bounded_display_text( + &format!("Unable to load candidate changes: {error}"), + 180, + )); + } + } + } else { + state.task_diff_message = Some("No task is selected.".to_owned()); + } + state.task_panel = TaskPanelState::Diff; + true +} + +#[cfg(unix)] +fn handle_task_cancel_confirmation( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) { + if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat { + return; + } + match key.code { + KeyCode::Char('y') => { + if let Some(task_id) = state.pending_cancel.as_deref() { + state.task_refresh_message = Some(match (provider.cancel_task)(task_id) { + Ok(()) => { + "Cancellation requested; prior external actions are not undone.".to_owned() + } + Err(error) => { + bounded_display_text(&format!("Cancellation failed: {error}"), 100) + } + }); + } + state.pending_cancel = None; + } + KeyCode::Char('n') | KeyCode::Esc => state.pending_cancel = None, + _ => {} + } +} + +fn render_task_cancel_confirmation(frame: &mut Frame<'_>, task_id: &str) { + let area = frame.area(); + if area.width < 32 || area.height < 8 { + return; + } + let popup = centered_rect(74, 30, area); + frame.render_widget(Clear, popup); + frame.render_widget( + Paragraph::new(vec![ + Line::from(format!( + "Cancel task {}?", + bounded_display_text(task_id, 48) + )), + Line::from("Cancellation does not undo external actions."), + Line::from("y confirm · n or Esc keep task"), + ]) + .block( + Block::default() + .title(" Confirm task cancellation ") + .borders(Borders::ALL), + ), + popup, + ); +} + +fn render_task_evidence( + frame: &mut Frame<'_>, + evidence: Option<&TaskInvestigationEvidence>, + message: Option<&str>, + scroll: u16, +) { + let area = frame.area(); + if area.width < 24 || area.height < 8 { + return; + } + let popup = centered_rect(86, 82, area); + frame.render_widget(Clear, popup); + let mut lines = Vec::new(); + if let Some(evidence) = evidence { + lines.extend([ + Line::from(format!( + "Investigation: {}", + bounded_display_text(&evidence.id, 72) + )), + Line::from(format!( + "Candidate: {}", + bounded_display_text(&evidence.candidate, 100) + )), + Line::from(format!( + "Stored decision: {}", + bounded_display_text(&evidence.decision, 72) + )), + Line::from(format!( + "Decision reason: {}", + bounded_display_text(&evidence.decision_reason, 220) + )), + Line::from( + "Recorded checks (stored outcomes are evidence, not independent verification):", + ), + ]); + for check in evidence.checks.iter().take(MAX_TASK_EVIDENCE_ITEMS) { + lines.push(Line::from(format!( + "CHECK {} · {} · required: {} · tests: {} · skipped: {} · {}", + bounded_display_text(&check.id, 36), + bounded_display_text(&check.outcome, 20), + check.required, + check.tests, + check.skipped, + bounded_display_text(&check.meaning, 180) + ))); + } + if evidence.checks.len() > MAX_TASK_EVIDENCE_ITEMS { + lines.push(Line::from( + "Additional check records omitted at the TUI limit.", + )); + } + if !evidence.unknowns.is_empty() { + lines.push(Line::from("Recorded unknowns:")); + for unknown in evidence.unknowns.iter().take(MAX_TASK_EVIDENCE_ITEMS) { + lines.push(Line::from(format!( + "UNKNOWN: {}", + bounded_display_text(unknown, 200) + ))); + } + if evidence.unknowns.len() > MAX_TASK_EVIDENCE_ITEMS { + lines.push(Line::from( + "Additional unknown records omitted at the TUI limit.", + )); + } + } + if !evidence.findings.is_empty() { + lines.push(Line::from("Recorded findings:")); + for finding in evidence.findings.iter().take(MAX_TASK_EVIDENCE_ITEMS) { + let severity = finding + .severity + .as_deref() + .map(|value| format!("{} · ", bounded_display_text(value, 20))) + .unwrap_or_default(); + lines.push(Line::from(format!( + "FINDING: {severity}{} {}", + bounded_display_text(&finding.path, 120), + bounded_display_text(&finding.message, 200) + ))); + } + if evidence.findings.len() > MAX_TASK_EVIDENCE_ITEMS { + lines.push(Line::from( + "Additional finding records omitted at the TUI limit.", + )); + } + } + } else { + lines.push(Line::from( + message.unwrap_or("No investigation record is available."), + )); + } + lines.push(Line::from( + "↑/↓, PgUp/PgDn scroll · Esc returns to task detail", + )); + frame.render_widget( + Paragraph::new(lines) + .wrap(Wrap { trim: false }) + .scroll((scroll, 0)) + .block( + Block::default() + .title(" Rover task evidence · recorded assessment ") + .borders(Borders::ALL), + ), + popup, + ); +} + +fn render_task_diff( + frame: &mut Frame<'_>, + diff: Option<&TaskDiffEvidence>, + message: Option<&str>, + scroll: u16, +) { + let area = frame.area(); + if area.width < 24 || area.height < 8 { + return; + } + let popup = centered_rect(86, 82, area); + frame.render_widget(Clear, popup); + let mut lines = Vec::new(); + if let Some(diff) = diff { + lines.extend([ + Line::from(format!( + "Base snapshot: {}", + bounded_display_text(&diff.base_snapshot, 88) + )), + Line::from(format!( + "Candidate snapshot: {}", + bounded_display_text(&diff.candidate, 88) + )), + Line::from(format!("{} verified path changes", diff.changes.len())), + ]); + for change in diff.changes.iter().take(MAX_TASK_DIFF_ITEMS) { + lines.push(Line::from(format!( + "{:<10} {:<20} {}", + bounded_display_text(&change.status, 10), + bounded_display_text(&change.category, 20), + bounded_display_text(&change.path, 180) + ))); + } + if diff.changes.len() > MAX_TASK_DIFF_ITEMS { + lines.push(Line::from(format!( + "{} additional paths omitted from this bounded view; use `rover diff` for the full patch.", + diff.changes.len() - MAX_TASK_DIFF_ITEMS + ))); + } else { + lines.push(Line::from( + "Use `rover diff --base --candidate ` for the complete applicable patch.", + )); + } + } else { + lines.push(Line::from( + message.unwrap_or("No retained snapshot comparison is available."), + )); + } + lines.push(Line::from( + "↑/↓, PgUp/PgDn scroll · Esc returns to task detail", + )); + frame.render_widget( + Paragraph::new(lines) + .wrap(Wrap { trim: false }) + .scroll((scroll, 0)) + .block( + Block::default() + .title(" Rover candidate diff · verified snapshot inventory ") + .borders(Borders::ALL), + ), + popup, + ); +} + +#[cfg(unix)] +fn render_file_tree(frame: &mut Frame<'_>, state: &AttachedInputState) { + let area = frame.area(); + if area.width < 24 || area.height < 8 { + return; + } + let popup = centered_rect(88, 86, area); + frame.render_widget(Clear, popup); + let listing = state.file_listing.as_ref(); + let entries = listing.map_or(&[][..], |listing| listing.entries.as_slice()); + let rows_available = usize::from(popup.height.saturating_sub(7)).max(1); + let visible = entries.len().min(MAX_FILE_BROWSER_ROWS).min(rows_available); + let start = if entries.len() > visible { + state + .file_selected + .saturating_sub(visible / 2) + .min(entries.len().saturating_sub(visible)) + } else { + 0 + }; + let directory = if state.file_directory.is_empty() { + "/".to_owned() + } else { + state.file_directory.clone() + }; + let mut lines = vec![Line::from(format!( + "{} · {} entries · hidden files {}", + bounded_display_text(&directory, 128), + entries.len(), + if state.file_include_hidden { + "shown" + } else { + "hidden" + } + ))]; + if let Some(error) = listing.and_then(|listing| listing.git_error.as_deref()) { + lines.push(Line::from(Span::styled( + bounded_display_text(&format!("Git status unavailable: {error}"), 120), + Style::default().fg(Color::Yellow), + ))); + } + if entries.is_empty() { + lines.push(Line::from("This directory has no visible entries.")); + } else { + append_file_tree_rows(&mut lines, entries, state.file_selected, start, visible); + if entries.len() > visible { + lines.push(Line::from(format!( + "Showing {}–{} of {} entries.", + start + 1, + start + visible, + entries.len() + ))); + } + } + if listing.is_some_and(|listing| listing.truncated) { + lines.push(Line::from( + "Directory listing reached the 10,000-entry limit.", + )); + } + if let Some(message) = state.file_message.as_deref() { + lines.push(Line::from(bounded_display_text(message, 120))); + } + lines.push(Line::from( + "↑/↓ select · Enter open · Backspace parent · h hidden · . quick open · Esc close", + )); + frame.render_widget( + Paragraph::new(lines).block( + Block::default() + .title(" Rover files · read only · Git status ") + .borders(Borders::ALL), + ), + popup, + ); +} + +#[cfg(unix)] +fn append_file_tree_rows( + lines: &mut Vec>, + entries: &[BrowserEntry], + selected: usize, + start: usize, + visible: usize, +) { + for (position, entry) in entries.iter().enumerate().skip(start).take(visible) { + let marker = if position == selected { "> " } else { " " }; + let icon = match entry.kind { + BrowserEntryKind::Directory => "▸ ", + BrowserEntryKind::File => " ", + BrowserEntryKind::Symlink => "↗ ", + BrowserEntryKind::Other => "? ", + }; + let (status, color) = git_status_label(entry.git_status); + let selected_style = if position == selected { + Style::default() + .fg(Color::Black) + .bg(Color::Cyan) + .add_modifier(Modifier::BOLD) + } else { + Style::default() + }; + let path_style = if position == selected { + selected_style + } else { + Style::default().fg(color) + }; + lines.push(Line::from(vec![ + Span::styled(marker, selected_style), + Span::styled(status, path_style), + Span::styled(icon, path_style), + Span::styled(bounded_display_text(&entry.path, 140), path_style), + ])); + } +} + +#[cfg(unix)] +fn render_quick_open(frame: &mut Frame<'_>, state: &AttachedInputState) { + let area = frame.area(); + if area.width < 24 || area.height < 8 { + return; + } + let popup = centered_rect(88, 86, area); + frame.render_widget(Clear, popup); + let indices = quick_open_matches(state); + let rows_available = usize::from(popup.height.saturating_sub(7)).max(1); + let visible = indices.len().min(MAX_FILE_BROWSER_ROWS).min(rows_available); + let selected_position = indices + .iter() + .position(|index| *index == state.quick_open_selected) + .unwrap_or(0); + let start = if indices.len() > visible { + selected_position + .saturating_sub(visible / 2) + .min(indices.len().saturating_sub(visible)) + } else { + 0 + }; + let query = state.quick_open_query.as_deref().unwrap_or_default(); + let mut lines = vec![Line::from(format!( + "Quick open: {}_", + bounded_display_text(query, MAX_FILE_QUERY_BYTES) + ))]; + if indices.is_empty() { + lines.push(Line::from("No files match. Hidden files are included.")); + } else { + for index in indices.iter().copied().skip(start).take(visible) { + let entry = &state.quick_open_entries[index]; + let marker = if index == state.quick_open_selected { + "> " + } else { + " " + }; + let (_, color) = git_status_label(entry.git_status); + let style = if index == state.quick_open_selected { + Style::default() + .fg(Color::Black) + .bg(Color::Cyan) + .add_modifier(Modifier::BOLD) + } else { + Style::default().fg(color) + }; + let (status, _) = git_status_label(entry.git_status); + lines.push(Line::from(vec![ + Span::styled(marker, style), + Span::styled(status, style), + Span::styled(bounded_display_text(&entry.path, 160), style), + ])); + } + if indices.len() > visible { + lines.push(Line::from(format!( + "Showing {}–{} of {} matches.", + start + 1, + start + visible, + indices.len() + ))); + } + } + if state.quick_open_entries.len() >= MAX_BROWSER_ENTRIES { + lines.push(Line::from("Quick-open scan is bounded at 10,000 paths.")); + } + if let Some(message) = state.file_message.as_deref() { + lines.push(Line::from(bounded_display_text(message, 120))); + } + lines.push(Line::from("Type to filter · Enter preview · Esc return")); + frame.render_widget( + Paragraph::new(lines).block( + Block::default() + .title(" Rover quick open · hidden files included ") + .borders(Borders::ALL), + ), + popup, + ); +} + +#[cfg(unix)] +fn render_file_preview(frame: &mut Frame<'_>, state: &AttachedInputState) { + let area = frame.area(); + if area.width < 24 || area.height < 8 { + return; + } + let popup = centered_rect(92, 90, area); + frame.render_widget(Clear, popup); + let mut lines = Vec::new(); + if let Some(preview) = state.file_preview.as_ref() { + lines.push(Line::from(format!( + "{} · preview limit: 256 KiB{}", + bounded_display_text(&preview.path, 120), + if preview.truncated { + " · truncated" + } else { + "" + } + ))); + if preview.binary { + lines.push(Line::from("Binary content is not rendered as text.")); + } else { + lines.extend(preview.text.lines().map(Line::from)); + } + } else if let Some(message) = state.file_message.as_deref() { + lines.push(Line::from(bounded_display_text(message, 160))); + } + lines.push(Line::from( + if state.file_edit_capability == FileEditCapability::Enabled { + "↑/↓, PgUp/PgDn scroll · e edit · Esc return" + } else { + "↑/↓, PgUp/PgDn scroll · Esc return" + }, + )); + frame.render_widget( + Paragraph::new(lines) + .wrap(Wrap { trim: false }) + .scroll((state.file_preview_scroll, 0)) + .block( + Block::default() + .title(" Rover file preview · bounded ") + .borders(Borders::ALL), + ), + popup, + ); +} + +#[cfg(unix)] +fn render_file_editor(frame: &mut Frame<'_>, state: &AttachedInputState) { + let area = frame.area(); + if area.width < 24 || area.height < 8 { + return; + } + let popup = centered_rect(94, 92, area); + frame.render_widget(Clear, popup); + let mut lines = Vec::new(); + if let Some(edit) = state.file_edit.as_ref() { + lines.push(Line::from(format!( + "{} · {} bytes · {}", + bounded_display_text(&edit.path, 100), + edit.buffer.len(), + if edit.buffer == edit.original { + "clean" + } else { + "modified" + } + ))); + lines.extend(file_editor_visible_lines(edit)); + if edit.discard_confirmation { + lines.push(Line::from( + "Discard unsaved changes? y discard · n/Esc keep editing", + )); + } else if let Some(message) = state.file_message.as_deref() { + lines.push(Line::from(bounded_display_text(message, 160))); + } + } + lines.push(Line::from( + "Ctrl-S save · Ctrl-D diff · Ctrl-E external editor · Esc return", + )); + frame.render_widget( + Paragraph::new(lines).wrap(Wrap { trim: false }).block( + Block::default() + .title(" Rover inline editor · 8 MiB limit ") + .borders(Borders::ALL), + ), + popup, + ); +} + +#[cfg(unix)] +fn file_editor_visible_lines(edit: &FileEditState) -> Vec> { + const CONTEXT_LINES: usize = 40; + const MAX_RENDERED_LINES: usize = 120; + const MAX_RENDERED_LINE_CHARS: usize = 2048; + let source_lines = edit.buffer.split('\n').collect::>(); + let cursor_line = edit.buffer[..edit.cursor] + .bytes() + .filter(|byte| *byte == b'\n') + .count(); + let start = cursor_line.saturating_sub(CONTEXT_LINES); + let end = source_lines + .len() + .min(start.saturating_add(MAX_RENDERED_LINES)); + let mut visible = Vec::with_capacity(end.saturating_sub(start).saturating_add(2)); + if start > 0 { + visible.push(Line::from( + "… earlier lines hidden · move cursor up to view …", + )); + } + for (index, source_line) in source_lines.iter().enumerate().take(end).skip(start) { + let source_line = source_line.trim_end_matches('\r'); + let mut display = source_line.to_owned(); + if index == cursor_line { + let column = edit.buffer[current_line_start(&edit.buffer, edit.cursor)..edit.cursor] + .chars() + .count() + .min(display.chars().count()); + let byte_position = display + .char_indices() + .nth(column) + .map_or(display.len(), |(byte_position, _)| byte_position); + display.insert(byte_position, '▏'); + } + visible.push(Line::from(bounded_display_text( + &sanitize_screen_text(&display), + MAX_RENDERED_LINE_CHARS, + ))); + } + if end < source_lines.len() { + visible.push(Line::from( + "… later lines hidden · move cursor down to view …", + )); + } + visible +} + +#[cfg(unix)] +fn render_file_diff(frame: &mut Frame<'_>, state: &AttachedInputState) { + let area = frame.area(); + if area.width < 24 || area.height < 8 { + return; + } + let popup = centered_rect(92, 90, area); + frame.render_widget(Clear, popup); + let mut lines = Vec::new(); + if let Some(edit) = state.file_edit.as_ref() { + lines.extend(file_diff_lines(&edit.path, &edit.original, &edit.buffer)); + } + lines.push(Line::from("↑/↓, PgUp/PgDn scroll · Esc return to editor")); + frame.render_widget( + Paragraph::new(lines) + .wrap(Wrap { trim: false }) + .scroll((state.file_diff_scroll, 0)) + .block( + Block::default() + .title(" Rover unsaved file diff ") + .borders(Borders::ALL), + ), + popup, + ); +} + +#[cfg(unix)] +fn file_diff_lines(path: &str, original: &str, edited: &str) -> Vec> { + const MAX_DIFF_LINES: usize = 512; + const CONTEXT_LINES: usize = 3; + let old = original.split_inclusive('\n').collect::>(); + let new = edited.split_inclusive('\n').collect::>(); + let safe_path = bounded_display_text(&sanitize_screen_text(path).replace('\n', "�"), 120); + let mut output = vec![ + Line::from(format!("--- a/{safe_path}")), + Line::from(format!("+++ b/{safe_path}")), + ]; + if original == edited { + output.push(Line::from("No content differences.")); + return output; + } + + let Some(operations) = bounded_line_diff(&old, &new) else { + output.push(Line::from( + "Diff preview unavailable: comparison exceeds its bounded line-work budget.", + )); + return output; + }; + let mut old_before = vec![0_usize; operations.len() + 1]; + let mut new_before = vec![0_usize; operations.len() + 1]; + for (index, operation) in operations.iter().enumerate() { + old_before[index + 1] = old_before[index] + usize::from(operation.consumes_old()); + new_before[index + 1] = new_before[index] + usize::from(operation.consumes_new()); + } + + let mut hunks: Vec<(usize, usize)> = Vec::new(); + for (index, operation) in operations.iter().enumerate() { + if operation.is_equal() { + continue; + } + let start = index.saturating_sub(CONTEXT_LINES); + let end = index + .saturating_add(CONTEXT_LINES + 1) + .min(operations.len()); + if let Some((_, previous_end)) = hunks.last_mut() { + if start <= *previous_end { + *previous_end = (*previous_end).max(end); + continue; + } + } + hunks.push((start, end)); + } + + let mut shown_old = 0_usize; + let mut shown_new = 0_usize; + for (hunk_index, (start, end)) in hunks.iter().copied().enumerate() { + if hunk_index >= 64 { + output.push(Line::from("… diff preview truncated after 64 hunks …")); + break; + } + let hunk_old = old_before[end] - old_before[start]; + let hunk_new = new_before[end] - new_before[start]; + let changed_old = operations[start..end] + .iter() + .filter(|operation| matches!(operation, LineDiffOperation::Removed(_))) + .count(); + let changed_new = operations[start..end] + .iter() + .filter(|operation| matches!(operation, LineDiffOperation::Added(_))) + .count(); + if shown_old.saturating_add(changed_old) > MAX_DIFF_LINES + || shown_new.saturating_add(changed_new) > MAX_DIFF_LINES + { + output.push(Line::from( + "… diff preview truncated at 512 changed lines per side …", + )); + break; + } + shown_old += changed_old; + shown_new += changed_new; + let old_start = if hunk_old == 0 { + old_before[start] + } else { + old_before[start] + 1 + }; + let new_start = if hunk_new == 0 { + new_before[start] + } else { + new_before[start] + 1 + }; + output.push(Line::from(format!( + "@@ -{old_start},{hunk_old} +{new_start},{hunk_new} @@" + ))); + for operation in &operations[start..end] { + match operation { + LineDiffOperation::Equal(line) => push_file_diff_line(&mut output, ' ', line), + LineDiffOperation::Removed(line) => push_file_diff_line(&mut output, '-', line), + LineDiffOperation::Added(line) => push_file_diff_line(&mut output, '+', line), + } + } + } + output +} + +#[cfg(unix)] +#[derive(Clone, Copy)] +enum LineDiffOperation<'a> { + Equal(&'a str), + Removed(&'a str), + Added(&'a str), +} + +#[cfg(unix)] +impl LineDiffOperation<'_> { + fn consumes_old(self) -> bool { + !matches!(self, Self::Added(_)) + } + + fn consumes_new(self) -> bool { + !matches!(self, Self::Removed(_)) + } + + fn is_equal(self) -> bool { + matches!(self, Self::Equal(_)) + } +} + +#[cfg(unix)] +fn bounded_line_diff<'a>(old: &[&'a str], new: &[&'a str]) -> Option>> { + const MAX_LINES_PER_SIDE: usize = 10_000; + const MAX_LCS_CELLS: usize = 1_000_000; + let rows = old.len().checked_add(1)?; + let columns = new.len().checked_add(1)?; + if old.len() > MAX_LINES_PER_SIDE + || new.len() > MAX_LINES_PER_SIDE + || rows.checked_mul(columns)? > MAX_LCS_CELLS + { + return None; + } + let mut lcs = vec![0_u32; rows * columns]; + for old_index in 1..rows { + for new_index in 1..columns { + let index = old_index * columns + new_index; + lcs[index] = if old[old_index - 1] == new[new_index - 1] { + lcs[(old_index - 1) * columns + new_index - 1] + 1 + } else { + lcs[(old_index - 1) * columns + new_index] + .max(lcs[old_index * columns + new_index - 1]) + }; + } + } + + let mut operations = Vec::with_capacity(old.len().saturating_add(new.len())); + let (mut old_index, mut new_index) = (old.len(), new.len()); + while old_index > 0 || new_index > 0 { + if old_index > 0 && new_index > 0 && old[old_index - 1] == new[new_index - 1] { + operations.push(LineDiffOperation::Equal(old[old_index - 1])); + old_index -= 1; + new_index -= 1; + } else if old_index > 0 + && (new_index == 0 + || lcs[(old_index - 1) * columns + new_index] + >= lcs[old_index * columns + new_index - 1]) + { + operations.push(LineDiffOperation::Removed(old[old_index - 1])); + old_index -= 1; + } else { + operations.push(LineDiffOperation::Added(new[new_index - 1])); + new_index -= 1; + } + } + operations.reverse(); + Some(operations) +} + +#[cfg(unix)] +fn push_file_diff_line(output: &mut Vec>, prefix: char, line: &str) { + const MAX_DIFF_LINE_CHARS: usize = 2048; + let content = line.trim_end_matches('\n').trim_end_matches('\r'); + output.push(Line::from(format!( + "{prefix}{}", + bounded_display_text(&sanitize_screen_text(content), MAX_DIFF_LINE_CHARS) + ))); + if !line.ends_with('\n') { + output.push(Line::from("\\ No newline at end of file")); + } +} + +#[cfg(unix)] +fn git_status_label(status: Option) -> (&'static str, Color) { + match status { + Some(BrowserGitStatus::Added) => ("[A] ", Color::Green), + Some(BrowserGitStatus::Modified) => ("[M] ", Color::Yellow), + Some(BrowserGitStatus::Deleted) => ("[D] ", Color::Red), + Some(BrowserGitStatus::Renamed) => ("[R] ", Color::Cyan), + Some(BrowserGitStatus::Untracked) => ("[?] ", Color::Green), + Some(BrowserGitStatus::Conflict) => ("[!] ", Color::Magenta), + None => (" ", Color::Reset), + } +} + +#[cfg(unix)] +fn open_selected_task_log( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, + key: KeyEvent, +) -> bool { + if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat { + return false; + } + let stdout = match key.code { + KeyCode::Char('o') => true, + KeyCode::Char('e') => false, + _ => return false, + }; + let digest = provider + .tasks + .get(state.selected_task) + .and_then(|task| task.process.as_ref()) + .map(|process| { + if stdout { + &process.stdout_sha256 + } else { + &process.stderr_sha256 + } + }); + state.task_log = if let Some(digest) = digest { + match (provider.load_blob)(digest).and_then(|bytes| { + prepare_task_log(&bytes, digest) + .map_err(|message| io::Error::new(io::ErrorKind::InvalidData, message)) + }) { + Ok((text, true)) => { + state.task_log_verification = TaskLogVerification::Verified; + text + } + Ok((text, false)) => { + state.task_log_verification = TaskLogVerification::Unavailable; + text + } + Err(error) => { + state.task_log_verification = TaskLogVerification::Unavailable; + bounded_display_text(&format!("Unable to load task output: {error}"), 256) + } + } + } else { + state.task_log_verification = TaskLogVerification::Unavailable; + "No process output is recorded for this task.".to_owned() + }; + state.task_log_scroll = 0; + state.task_panel = if stdout { + TaskPanelState::Stdout + } else { + TaskPanelState::Stderr + }; + true +} + +#[cfg(unix)] +fn refresh_task_snapshot( + tasks: &mut Vec, + selected: &mut usize, + message: &mut Option, + refresh: &mut impl FnMut() -> io::Result>, +) { + let selected_id = tasks.get(*selected).map(|task| task.id.as_str()); + match refresh() { + Ok(refreshed) if refreshed.len() <= 1_000 => { + *selected = selected_id + .and_then(|id| refreshed.iter().position(|task| task.id == id)) + .unwrap_or_else(|| (*selected).min(refreshed.len().saturating_sub(1))); + let count = refreshed.len(); + *tasks = refreshed; + *message = Some(format!("Refreshed {count} project tasks.")); + } + Ok(_) => { + *message = Some("Refresh refused: more than 1,000 project tasks.".to_owned()); + } + Err(error) => { + *message = Some(bounded_display_text( + &format!("Refresh failed: {error}"), + 96, + )); + } + } +} + +#[cfg(unix)] +fn refresh_task_snapshot_if_requested( + state: &mut AttachedInputState, + provider: &mut TaskProvider<'_>, +) { + if state.refresh_request != RefreshRequest::Requested { + return; + } + state.refresh_request = RefreshRequest::Idle; + refresh_task_snapshot( + &mut provider.tasks, + &mut state.selected_task, + &mut state.task_refresh_message, + &mut provider.refresh, + ); + let visible = task_visible_indices(&provider.tasks, &provider.view_preferences); + if !visible.contains(&state.selected_task) { + state.selected_task = visible.first().copied().unwrap_or(0); + } +} + +#[cfg(unix)] +fn pane_terminal_size( + workspace: &Workspace, + area: Rect, + pane_id: &str, +) -> io::Result { + let body = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Length(1), + Constraint::Min(1), + Constraint::Length(1), + ]) + .split(area)[1]; + let pane_rect = + find_pane_rect(workspace.active_tab().render_root(), body, pane_id).unwrap_or(body); + let columns = usize::from(pane_rect.width.saturating_sub(2).clamp(2, 1000)); + let rows = usize::from(pane_rect.height.saturating_sub(2).clamp(1, 1000)); + let rows = rows.min(250_000 / columns); + TerminalSize::new(rows.max(1), columns) +} + +#[cfg(unix)] +fn send_resize(writer: &mut SessionWriter, size: TerminalSize) -> io::Result<()> { + let rows = u16::try_from(size.rows).map_err(|_| { + io::Error::new(io::ErrorKind::InvalidInput, "terminal rows exceed protocol") + })?; + let columns = u16::try_from(size.columns).map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidInput, + "terminal columns exceed protocol", + ) + })?; + writer.resize(rows, columns) +} + +#[cfg(unix)] +fn find_pane_rect(node: &PaneNode, area: Rect, pane_id: &str) -> Option { + match node { + PaneNode::Leaf(pane) => (pane.id() == pane_id).then_some(area), + PaneNode::Split(split) => { + let direction = match split.axis() { + SplitAxis::Horizontal => Direction::Horizontal, + SplitAxis::Vertical => Direction::Vertical, + }; + let ratio = u32::from(split.first_basis_points()); + let children = Layout::default() + .direction(direction) + .constraints([ + Constraint::Ratio(ratio, 10_000), + Constraint::Ratio(10_000 - ratio, 10_000), + ]) + .split(area); + find_pane_rect(split.first(), children[0], pane_id) + .or_else(|| find_pane_rect(split.second(), children[1], pane_id)) + } + } +} + +#[cfg(unix)] +fn is_detach_key(key: KeyEvent) -> bool { + key.code == KeyCode::Char(']') && key.modifiers.contains(KeyModifiers::CONTROL) +} + +#[cfg(unix)] +fn attached_workspace_key(workspace: &mut Workspace, key: KeyEvent) -> bool { + match key.code { + KeyCode::Char('q') => true, + KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => true, + _ => handle_key(workspace, key), + } +} + +#[cfg(unix)] +fn attached_task_board_key( + panel: &mut TaskPanelState, + prefix: &mut bool, + selected: &mut usize, + tasks: &[TaskSummary], + preferences: &TaskViewPreferences, + key: KeyEvent, +) -> bool { + if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat { + return false; + } + let visible = task_visible_indices(tasks, preferences); + let selected_position = visible.iter().position(|index| index == selected); + match *panel { + TaskPanelState::Detail => { + match key.code { + KeyCode::Esc | KeyCode::Enter => *panel = TaskPanelState::List, + KeyCode::Up if !visible.is_empty() => { + let position = selected_position.unwrap_or(0); + *selected = if position == 0 { + *visible.last().unwrap_or(&0) + } else { + visible[position - 1] + }; + } + KeyCode::Down if !visible.is_empty() => { + let position = selected_position.unwrap_or(visible.len() - 1); + *selected = visible[(position + 1) % visible.len()]; + } + _ => {} + } + true + } + TaskPanelState::List => { + match key.code { + KeyCode::Esc | KeyCode::Char('t') => *panel = TaskPanelState::Closed, + KeyCode::Enter if !visible.is_empty() => { + if selected_position.is_none() { + *selected = visible[0]; + } + *panel = TaskPanelState::Detail; + } + KeyCode::Up if !visible.is_empty() => { + let position = selected_position.unwrap_or(0); + *selected = if position == 0 { + *visible.last().unwrap_or(&0) + } else { + visible[position - 1] + }; + } + KeyCode::Down if !visible.is_empty() => { + let position = selected_position.unwrap_or(visible.len() - 1); + *selected = visible[(position + 1) % visible.len()]; + } + _ => {} + } + true + } + TaskPanelState::Closed => { + if *prefix && key.code == KeyCode::Char('t') { + *prefix = false; + *panel = TaskPanelState::List; + *selected = visible.first().copied().unwrap_or(0); + return true; + } + false + } + TaskPanelState::Stdout + | TaskPanelState::Stderr + | TaskPanelState::Evidence + | TaskPanelState::Diff => false, + } +} + +#[cfg(unix)] +fn attached_task_log_key(panel: &mut TaskPanelState, scroll: &mut u16, key: KeyEvent) -> bool { + if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat { + return false; + } + match key.code { + KeyCode::Esc | KeyCode::Enter => *panel = TaskPanelState::Detail, + KeyCode::Up => *scroll = scroll.saturating_sub(1), + KeyCode::Down => *scroll = scroll.saturating_add(1), + KeyCode::PageUp => *scroll = scroll.saturating_sub(12), + KeyCode::PageDown => *scroll = scroll.saturating_add(12), + _ => {} + } + true +} + +#[cfg(unix)] +fn key_input_bytes(key: KeyEvent) -> Option> { + let mut bytes = match key.code { + KeyCode::Char(character) => { + if key.modifiers.contains(KeyModifiers::CONTROL) { + let value = match character.to_ascii_lowercase() { + ' ' | '@' => 0, + 'a'..='z' => character.to_ascii_lowercase() as u8 - b'a' + 1, + '[' => 0x1b, + '\\' => 0x1c, + ']' => 0x1d, + '^' => 0x1e, + '_' => 0x1f, + _ => return None, + }; + vec![value] + } else { + let mut encoded = [0; 4]; + character.encode_utf8(&mut encoded).as_bytes().to_vec() + } + } + KeyCode::Enter => vec![b'\r'], + KeyCode::Tab => vec![b'\t'], + KeyCode::BackTab => b"\x1b[Z".to_vec(), + KeyCode::Backspace => vec![0x7f], + KeyCode::Esc => vec![0x1b], + KeyCode::Up => b"\x1b[A".to_vec(), + KeyCode::Down => b"\x1b[B".to_vec(), + KeyCode::Right => b"\x1b[C".to_vec(), + KeyCode::Left => b"\x1b[D".to_vec(), + KeyCode::Home => b"\x1b[H".to_vec(), + KeyCode::End => b"\x1b[F".to_vec(), + KeyCode::Delete => b"\x1b[3~".to_vec(), + KeyCode::Insert => b"\x1b[2~".to_vec(), + KeyCode::PageUp => b"\x1b[5~".to_vec(), + KeyCode::PageDown => b"\x1b[6~".to_vec(), + KeyCode::F(1) => b"\x1bOP".to_vec(), + KeyCode::F(2) => b"\x1bOQ".to_vec(), + KeyCode::F(3) => b"\x1bOR".to_vec(), + KeyCode::F(4) => b"\x1bOS".to_vec(), + KeyCode::F(number @ 5..=12) => format!("\x1b[{}~", 15 + (number - 5) * 3).into_bytes(), + _ => return None, + }; + if key.modifiers.contains(KeyModifiers::ALT) { + bytes.insert(0, 0x1b); + } + Some(bytes) +} + +#[cfg(unix)] +fn find_pane_ref<'a>(node: &'a PaneNode, pane_id: &str) -> Option<&'a Pane> { + match node { + PaneNode::Leaf(pane) if pane.id() == pane_id => Some(pane), + PaneNode::Leaf(_) => None, + PaneNode::Split(split) => { + find_pane_ref(split.first(), pane_id).or_else(|| find_pane_ref(split.second(), pane_id)) + } + } +} + +#[cfg(unix)] +struct SuspendedTerminal { + active: bool, +} + +#[cfg(unix)] +impl SuspendedTerminal { + fn enter() -> io::Result { + disable_raw_mode()?; + let guard = Self { active: true }; + execute!( + io::stdout(), + DisableBracketedPaste, + LeaveAlternateScreen, + Show + )?; + Ok(guard) + } + + fn restore(&mut self) -> io::Result<()> { + if !self.active { + return Ok(()); + } + execute!( + io::stdout(), + EnterAlternateScreen, + Hide, + EnableBracketedPaste + )?; + enable_raw_mode()?; + self.active = false; + Ok(()) + } +} + +#[cfg(unix)] +impl Drop for SuspendedTerminal { + fn drop(&mut self) { + let _ = self.restore(); + } +} + +struct TerminalGuard { + control: C, +} + +trait TerminalControl { + fn enable_raw(&mut self) -> io::Result<()>; + fn enter_screen(&mut self) -> io::Result<()>; + fn restore(&mut self); +} + +impl TerminalGuard { + fn enter() -> io::Result { + Self::enter_with(CrosstermControl::new()) + } +} + +impl TerminalGuard { + fn enter_with(mut control: C) -> io::Result { + control.enable_raw()?; + let mut guard = Self { control }; + guard.control.enter_screen()?; + Ok(guard) + } +} + +impl TerminalControl for CrosstermControl { + fn enable_raw(&mut self) -> io::Result<()> { + enable_raw_mode() + } + + fn enter_screen(&mut self) -> io::Result<()> { + execute!( + &mut self.stdout, + EnterAlternateScreen, + Hide, + EnableBracketedPaste + )?; + Ok(()) + } + + fn restore(&mut self) { + let _ = execute!( + &mut self.stdout, + DisableBracketedPaste, + LeaveAlternateScreen, + Show + ); + let _ = disable_raw_mode(); + } +} + +struct CrosstermControl { + stdout: io::Stdout, +} + +impl CrosstermControl { + fn new() -> Self { + Self { + stdout: io::stdout(), + } + } +} + +impl Drop for TerminalGuard { + fn drop(&mut self) { + self.control.restore(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::path::PathBuf; + use std::sync::{Arc, Mutex}; + #[cfg(unix)] + use std::{fs, os::unix::fs::PermissionsExt}; + + fn workspace() -> Workspace { + Workspace::new("project", "Rover 项目", "main", PathBuf::from("/repo")) + .expect("valid fixture workspace") + } + + #[cfg(unix)] + #[test] + fn terminal_bell_is_suppressed_for_the_focused_pane_only() { + let mut workspace = workspace(); + let first = workspace.active_tab().active_pane().to_owned(); + assert!(!pane_is_inactive(&workspace, &first)); + + let second = workspace + .split_active( + SplitAxis::Horizontal, + 5_000, + rover_execution::layout::PaneSpec::scratch(), + ) + .unwrap(); + assert!(pane_is_inactive(&workspace, &first)); + assert!(!pane_is_inactive(&workspace, &second)); + + workspace.focus_pane(&first).unwrap(); + assert!(!pane_is_inactive(&workspace, &first)); + assert!(pane_is_inactive(&workspace, &second)); + } + + #[cfg(unix)] + fn private_state_dir() -> (PathBuf, SafeDir) { + let path = std::env::temp_dir().join(format!( + "rover-tui-layout-test-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + )); + fs::create_dir(&path).unwrap(); + fs::set_permissions(&path, fs::Permissions::from_mode(0o700)).unwrap(); + let directory = SafeDir::open(&path).unwrap(); + (path, directory) + } + + fn press(code: KeyCode) -> KeyEvent { + KeyEvent::new(code, KeyModifiers::NONE) + } + + #[cfg(unix)] + fn test_agent_binding() -> AgentSessionBinding { + let path = std::env::current_dir().unwrap(); + let repository = path.to_str().unwrap(); + let identity = rover_core::RepositoryIdentity::resolve(repository).unwrap(); + AgentSessionBinding::new( + &identity, + "pane-1", + "codex-exec", + "session-quoted", + rover_agents::AgentSessionSource::ExplicitUser, + ) + .unwrap() + } + + #[cfg(unix)] + #[test] + fn agent_resume_shell_line_quotes_exact_arguments_and_rejects_controls() { + let line = agent_resume_shell_line(&[ + "codex".to_owned(), + "resume".to_owned(), + "id with ' quote".to_owned(), + ]) + .unwrap(); + assert_eq!( + String::from_utf8(line).unwrap(), + "'codex' 'resume' 'id with '\\'' quote'\n" + ); + assert!(agent_resume_shell_line(&["codex".to_owned(), "id\nnext".to_owned()]).is_err()); + } + + #[cfg(unix)] + #[test] + fn agent_session_requires_confirmation_to_resume_or_clear_binding() { + let sent = Arc::new(Mutex::new(Vec::>::new())); + let sent_by_provider = Arc::clone(&sent); + let cleared = Arc::new(Mutex::new(Vec::::new())); + let cleared_by_provider = Arc::clone(&cleared); + let mut provider = TaskProvider::new( + Vec::new(), + || Ok(Vec::new()), + |_| Ok(Vec::new()), + |_| Ok(()), + |_| Ok(None), + |_| Ok(None), + ); + provider + .configure_agent_session(&test_agent_binding(), move |pane| { + cleared_by_provider.lock().unwrap().push(pane.to_owned()); + Ok(()) + }) + .unwrap(); + let mut state = AttachedInputState { + agent_session_panel: AgentSessionPanel::ConfirmResume, + ..AttachedInputState::default() + }; + handle_agent_session_key( + &mut state, + &mut provider, + press(KeyCode::Char('n')), + |bytes| { + sent_by_provider.lock().unwrap().push(bytes.to_vec()); + Ok(()) + }, + ) + .unwrap(); + assert!(sent.lock().unwrap().is_empty()); + assert_eq!(state.agent_session_panel, AgentSessionPanel::Details); + + state.agent_session_panel = AgentSessionPanel::ConfirmResume; + handle_agent_session_key( + &mut state, + &mut provider, + press(KeyCode::Char('y')), + |bytes| { + sent.lock().unwrap().push(bytes.to_vec()); + Ok(()) + }, + ) + .unwrap(); + assert_eq!(state.agent_session_panel, AgentSessionPanel::Closed); + assert_eq!(sent.lock().unwrap().len(), 1); + + state.agent_session_panel = AgentSessionPanel::ConfirmClear; + handle_agent_session_key(&mut state, &mut provider, press(KeyCode::Char('y')), |_| { + Ok(()) + }) + .unwrap(); + assert_eq!(cleared.lock().unwrap().as_slice(), ["pane-1"]); + assert!(provider.agent_session.is_none()); + assert!(!provider + .capabilities + .supports(TaskCapabilities::AGENT_SESSION)); + } + + fn task_summary(id: &str) -> TaskSummary { + TaskSummary { + id: id.to_owned(), + objective: format!("objective for {id}"), + status: "RUNNING".to_owned(), + updated_at: "2026-09-26T00:00:00Z".to_owned(), + error: None, + candidate: None, + base_snapshot: None, + investigation_id: None, + attempt_count: 0, + process: None, + } + } + + fn investigation_evidence() -> TaskInvestigationEvidence { + TaskInvestigationEvidence { + id: "inv_task_a".to_owned(), + candidate: "snapshot_a".to_owned(), + decision: "BLOCKED".to_owned(), + decision_reason: "review is pending".to_owned(), + checks: vec![TaskCheckEvidence { + id: "unit".to_owned(), + outcome: "PASS".to_owned(), + meaning: "recorded tests passed".to_owned(), + required: true, + tests: 4, + skipped: 0, + }], + unknowns: vec!["Unknown security properties remain.".to_owned()], + findings: vec![TaskEvidenceFinding { + path: "src/main.rs".to_owned(), + message: "review this recorded finding".to_owned(), + severity: Some("warning".to_owned()), + }], + } + } + + fn task_diff() -> TaskDiffEvidence { + TaskDiffEvidence { + base_snapshot: "snap_base".to_owned(), + candidate: "snap_candidate".to_owned(), + changes: vec![TaskDiffChange { + path: "src/main.rs".to_owned(), + status: "modified".to_owned(), + category: "source".to_owned(), + }], + } + } + + #[cfg(unix)] + #[test] + fn task_refresh_preserves_selection_by_id_across_reordering() { + let mut tasks = vec![task_summary("task_a"), task_summary("task_b")]; + let mut selected = 1; + let mut message = None; + refresh_task_snapshot(&mut tasks, &mut selected, &mut message, &mut || { + Ok(vec![ + task_summary("task_b"), + task_summary("task_c"), + task_summary("task_a"), + ]) + }); + assert_eq!(tasks[0].id, "task_b"); + assert_eq!(selected, 0); + assert_eq!(message.as_deref(), Some("Refreshed 3 project tasks.")); + } + + #[cfg(unix)] + #[test] + fn task_board_search_sort_and_persisted_selection_are_deterministic() { + let mut alpha = task_summary("task_alpha"); + alpha.status = "WAITING".to_owned(); + alpha.updated_at = "2026-09-24T00:00:00Z".to_owned(); + alpha.candidate = Some("candidate-17".to_owned()); + let mut beta = task_summary("task_beta"); + beta.status = "RUNNING".to_owned(); + beta.objective = "Build the release".to_owned(); + beta.updated_at = "2026-09-26T00:00:00Z".to_owned(); + let tasks = vec![alpha, beta]; + + let updated = task_visible_indices(&tasks, &TaskViewPreferences::default()); + assert_eq!(updated, [1, 0]); + let by_status = task_visible_indices( + &tasks, + &TaskViewPreferences { + query: String::new(), + sort: TaskSortOrder::Status, + }, + ); + assert_eq!(by_status, [1, 0]); + let filtered = task_visible_indices( + &tasks, + &TaskViewPreferences { + query: "CANDIDATE-17".to_owned(), + sort: TaskSortOrder::Objective, + }, + ); + assert_eq!(filtered, [0]); + + let saved = Arc::new(Mutex::new(Vec::new())); + let saved_preferences = Arc::clone(&saved); + let mut provider = TaskProvider::new( + tasks, + || Ok(Vec::new()), + |_| Err(io::Error::new(io::ErrorKind::NotFound, "no blob")), + |_| Ok(()), + |_| Ok(None), + |_| Ok(None), + ); + provider.configure_view_preferences(&TaskViewPreferences::default(), move |preferences| { + saved_preferences.lock().unwrap().push(preferences.clone()); + Ok(()) + }); + let mut state = AttachedInputState { + task_search_draft: Some("candidate-17".to_owned()), + ..AttachedInputState::default() + }; + handle_task_search_edit(&mut state, &mut provider, press(KeyCode::Enter)); + assert_eq!(provider.view_preferences.query, "candidate-17"); + assert_eq!(state.selected_task, 0); + assert!(state.task_search_draft.is_none()); + assert_eq!( + saved.lock().unwrap().as_slice(), + [provider.view_preferences.clone()] + ); + } + + #[cfg(unix)] + #[test] + fn task_search_enforces_utf8_byte_limit_and_escape_discards_draft() { + let saved = Arc::new(Mutex::new(0)); + let saved_preferences = Arc::clone(&saved); + let mut provider = TaskProvider::new( + Vec::new(), + || Ok(Vec::new()), + |_| Err(io::Error::new(io::ErrorKind::NotFound, "no blob")), + |_| Ok(()), + |_| Ok(None), + |_| Ok(None), + ); + provider.configure_view_preferences(&TaskViewPreferences::default(), move |_| { + *saved_preferences.lock().unwrap() += 1; + Ok(()) + }); + let mut state = AttachedInputState { + task_search_draft: Some("é".repeat(MAX_TASK_FILTER_BYTES / 2)), + ..AttachedInputState::default() + }; + handle_task_search_edit(&mut state, &mut provider, press(KeyCode::Char('a'))); + assert_eq!( + state.task_search_draft.as_deref().unwrap().len(), + MAX_TASK_FILTER_BYTES + ); + handle_task_search_edit(&mut state, &mut provider, press(KeyCode::Esc)); + assert!(state.task_search_draft.is_none()); + assert!(provider.view_preferences.query.is_empty()); + assert_eq!(*saved.lock().unwrap(), 0); + } + + #[cfg(unix)] + fn test_file_browser_provider() -> TaskProvider<'static> { + let mut provider = TaskProvider::new( + Vec::new(), + || Ok(Vec::new()), + |_| Err(io::Error::new(io::ErrorKind::NotFound, "no blob")), + |_| Ok(()), + |_| Ok(None), + |_| Ok(None), + ); + provider.configure_file_browser( + |directory, include_hidden| { + let entries = if directory == "src" { + vec![BrowserEntry { + path: "src/main.rs".to_owned(), + kind: BrowserEntryKind::File, + git_status: Some(BrowserGitStatus::Modified), + }] + } else { + vec![ + BrowserEntry { + path: "src".to_owned(), + kind: BrowserEntryKind::Directory, + git_status: None, + }, + BrowserEntry { + path: "visible.txt".to_owned(), + kind: BrowserEntryKind::File, + git_status: Some(BrowserGitStatus::Added), + }, + BrowserEntry { + path: "link".to_owned(), + kind: BrowserEntryKind::Symlink, + git_status: None, + }, + ] + }; + Ok(BrowserListing { + entries, + truncated: false, + git_error: if include_hidden { + None + } else { + Some("fixture warning".to_owned()) + }, + }) + }, + || { + Ok(BrowserListing { + entries: vec![BrowserEntry { + path: ".hidden/config.toml".to_owned(), + kind: BrowserEntryKind::File, + git_status: Some(BrowserGitStatus::Untracked), + }], + truncated: false, + git_error: None, + }) + }, + |path| { + Ok(( + format!("preview for {path}\u{001b}[31m\nline two").into_bytes(), + false, + )) + }, + ); + provider.configure_file_editor( + |_| Ok(b"fn main() {}\n".to_vec()), + |_, _, _| Ok(()), + |_, bytes| Ok(bytes.to_vec()), + ); + provider + } + + #[cfg(unix)] + #[test] + fn file_tree_navigates_and_previews_sanitized_text() { + let mut provider = test_file_browser_provider(); + let mut state = AttachedInputState { + prefix: true, + ..AttachedInputState::default() + }; + assert!(open_file_browser_shortcut( + &mut state, + &mut provider, + press(KeyCode::Char('f')) + )); + assert_eq!(state.file_panel, FilePanelState::Tree); + let mut terminal = Terminal::new(TestBackend::new(100, 30)).unwrap(); + terminal + .draw(|frame| render_file_tree(frame, &state)) + .unwrap(); + let rendered = buffer_text(terminal.backend().buffer()); + assert!(rendered.contains("[A]")); + assert!(rendered.contains("Git status unavailable")); + + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Enter)); + assert_eq!(state.file_directory, "src"); + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Enter)); + assert_eq!(state.file_panel, FilePanelState::Viewer); + let preview = state.file_preview.as_ref().unwrap(); + assert_eq!(preview.path, "src/main.rs"); + assert!(!preview.text.contains('\u{001b}')); + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Esc)); + assert_eq!(state.file_panel, FilePanelState::Tree); + } + + #[cfg(unix)] + #[test] + fn quick_open_searches_hidden_files_and_returns_from_preview() { + let mut provider = test_file_browser_provider(); + let mut state = AttachedInputState { + file_panel: FilePanelState::Tree, + ..AttachedInputState::default() + }; + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Char('.'))); + assert_eq!(state.file_panel, FilePanelState::QuickOpen); + for character in "hidden".chars() { + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Char(character))); + } + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Enter)); + assert_eq!(state.file_panel, FilePanelState::Viewer); + assert_eq!( + state.file_preview.as_ref().unwrap().path, + ".hidden/config.toml" + ); + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Esc)); + assert_eq!(state.file_panel, FilePanelState::QuickOpen); + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Esc)); + assert_eq!(state.file_panel, FilePanelState::Tree); + } + + #[cfg(unix)] + #[test] + fn command_palette_filters_available_actions_and_runs_file_commands() { + let mut provider = test_file_browser_provider(); + provider.configure_workspace_notes(&[], |_| Ok(())); + let mut state = AttachedInputState { + prefix: true, + ..AttachedInputState::default() + }; + assert!(open_command_palette_shortcut( + &mut state, + &provider, + press(KeyCode::Char('p')) + )); + assert_eq!(state.command_palette_query.as_deref(), Some("")); + let mut workspace = workspace(); + for character in "quick".chars() { + handle_command_palette_key( + &mut workspace, + &mut state, + &mut provider, + press(KeyCode::Char(character)), + &mut |_| Ok(()), + ) + .unwrap(); + } + assert_eq!( + palette_actions(provider.capabilities, "quick"), + [PaletteAction::QuickOpen] + ); + handle_command_palette_key( + &mut workspace, + &mut state, + &mut provider, + press(KeyCode::Enter), + &mut |_| Ok(()), + ) + .unwrap(); + assert_eq!(state.file_panel, FilePanelState::QuickOpen); + assert!(state.command_palette_query.is_none()); + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Esc)); + + state.prefix = true; + open_command_palette_shortcut(&mut state, &provider, press(KeyCode::Char('p'))); + for character in "tree".chars() { + handle_command_palette_key( + &mut workspace, + &mut state, + &mut provider, + press(KeyCode::Char(character)), + &mut |_| Ok(()), + ) + .unwrap(); + } + handle_command_palette_key( + &mut workspace, + &mut state, + &mut provider, + press(KeyCode::Enter), + &mut |_| Ok(()), + ) + .unwrap(); + assert_eq!(state.file_panel, FilePanelState::Tree); + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Esc)); + + state.prefix = true; + open_command_palette_shortcut(&mut state, &provider, press(KeyCode::Char('p'))); + for character in "notes".chars() { + handle_command_palette_key( + &mut workspace, + &mut state, + &mut provider, + press(KeyCode::Char(character)), + &mut |_| Ok(()), + ) + .unwrap(); + } + assert_eq!( + palette_actions(provider.capabilities, "notes"), + [PaletteAction::OpenNotes] + ); + handle_command_palette_key( + &mut workspace, + &mut state, + &mut provider, + press(KeyCode::Enter), + &mut |_| Ok(()), + ) + .unwrap(); + assert_eq!(state.workspace_notes_panel, WorkspaceNotesPanel::List); + assert!(state.workspace_notes_draft.is_none()); + + assert!(palette_actions(TaskCapabilities::read_only(false), "file").is_empty()); + assert!(palette_actions(TaskCapabilities::read_only(false), "notes").is_empty()); + assert!(palette_actions(TaskCapabilities::read_only(false), "shell").is_empty()); + } + + #[cfg(unix)] + #[test] + fn command_palette_opens_saved_commands_when_supported() { + let mut provider = test_file_browser_provider(); + provider.configure_saved_commands(&[], |_| Ok(())); + provider.configure_task_drafts(&[], |_| Ok(())); + let mut state = AttachedInputState { + prefix: true, + ..AttachedInputState::default() + }; + let mut workspace = workspace(); + assert!(open_command_palette_shortcut( + &mut state, + &provider, + press(KeyCode::Char('p')) + )); + for character in "shell".chars() { + handle_command_palette_key( + &mut workspace, + &mut state, + &mut provider, + press(KeyCode::Char(character)), + &mut |_| Ok(()), + ) + .unwrap(); + } + assert_eq!( + palette_actions(provider.capabilities, "shell"), + [PaletteAction::OpenSavedCommands] + ); + handle_command_palette_key( + &mut workspace, + &mut state, + &mut provider, + press(KeyCode::Enter), + &mut |_| Ok(()), + ) + .unwrap(); + assert_eq!(state.saved_commands_panel, SavedCommandsPanel::List); + assert!(palette_actions(TaskCapabilities::read_only(false), "shell").is_empty()); + assert_eq!( + palette_actions(provider.capabilities, "draft"), + [PaletteAction::OpenTaskDrafts] + ); + assert!(palette_actions(TaskCapabilities::read_only(false), "draft").is_empty()); + } + + #[cfg(unix)] + #[test] + fn saved_command_runs_only_after_confirmation_and_sends_one_line() { + let saved = Arc::new(Mutex::new(Vec::>::new())); + let saved_copy = Arc::clone(&saved); + let mut provider = TaskProvider::new( + Vec::new(), + || Ok(Vec::new()), + |_| Err(io::Error::new(io::ErrorKind::NotFound, "no blob")), + |_| Ok(()), + |_| Ok(None), + |_| Ok(None), + ); + provider.configure_saved_commands(&[], move |commands| { + saved_copy.lock().unwrap().push(commands.to_vec()); + Ok(()) + }); + let mut state = AttachedInputState { + prefix: true, + ..AttachedInputState::default() + }; + assert!(open_saved_commands_shortcut( + &mut state, + &provider, + press(KeyCode::Char('k')) + )); + handle_saved_commands_key(&mut state, &mut provider, press(KeyCode::Char('n'))); + for character in "Check".chars() { + handle_saved_commands_key(&mut state, &mut provider, press(KeyCode::Char(character))); + } + handle_saved_commands_key(&mut state, &mut provider, press(KeyCode::Tab)); + for character in "cargo test".chars() { + handle_saved_commands_key(&mut state, &mut provider, press(KeyCode::Char(character))); + } + handle_saved_commands_key( + &mut state, + &mut provider, + KeyEvent::new(KeyCode::Char('s'), KeyModifiers::CONTROL), + ); + assert_eq!( + provider.saved_commands, + [SavedCommand { + name: "Check".to_owned(), + command: "cargo test".to_owned(), + }] + ); + assert_eq!(saved.lock().unwrap().len(), 1); + + handle_saved_commands_key(&mut state, &mut provider, press(KeyCode::Char('r'))); + assert_eq!(state.pending_saved_command, Some(0)); + let sent = Arc::new(Mutex::new(Vec::>::new())); + let sent_copy = Arc::clone(&sent); + handle_saved_command_confirmation( + &mut state, + &provider, + press(KeyCode::Char('n')), + move |bytes| { + sent_copy.lock().unwrap().push(bytes.to_vec()); + Ok(()) + }, + ); + assert!(sent.lock().unwrap().is_empty()); + + state.pending_saved_command = Some(0); + let sent_copy = Arc::clone(&sent); + handle_saved_command_confirmation( + &mut state, + &provider, + press(KeyCode::Char('y')), + move |bytes| { + sent_copy.lock().unwrap().push(bytes.to_vec()); + Ok(()) + }, + ); + assert_eq!(sent.lock().unwrap().as_slice(), [b"cargo test\r".to_vec()]); + assert!(state.pending_saved_command.is_none()); + } + + #[cfg(unix)] + #[test] + fn task_briefing_drafts_are_multiline_persisted_and_never_dispatched() { + let saved = Arc::new(Mutex::new(Vec::>::new())); + let saved_copy = Arc::clone(&saved); + let mut provider = TaskProvider::new( + Vec::new(), + || Ok(Vec::new()), + |_| Err(io::Error::new(io::ErrorKind::NotFound, "no blob")), + |_| Ok(()), + |_| Ok(None), + |_| Ok(None), + ); + provider.configure_task_drafts(&[], move |drafts| { + saved_copy.lock().unwrap().push(drafts.to_vec()); + Ok(()) + }); + let mut state = AttachedInputState { + prefix: true, + ..AttachedInputState::default() + }; + assert!(open_task_drafts_shortcut( + &mut state, + &provider, + press(KeyCode::Char('d')) + )); + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char('n'))); + for character in "Audit plan".chars() { + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char(character))); + } + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Tab)); + for character in "src/**".chars() { + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char(character))); + } + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Tab)); + for character in "task_base".chars() { + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char(character))); + } + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Tab)); + for character in "cargo test".chars() { + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char(character))); + } + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Tab)); + for character in "Compare feature paths".chars() { + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char(character))); + } + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Enter)); + for character in "Keep findings reviewable".chars() { + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char(character))); + } + handle_task_drafts_key( + &mut state, + &mut provider, + KeyEvent::new(KeyCode::Char('s'), KeyModifiers::CONTROL), + ); + assert_eq!( + provider.task_drafts, + [TaskDraft { + title: "Audit plan".to_owned(), + paths: "src/**".to_owned(), + dependencies: "task_base".to_owned(), + quality_gate: "cargo test".to_owned(), + prompt: "Compare feature paths\nKeep findings reviewable".to_owned() + }] + ); + assert_eq!(saved.lock().unwrap().len(), 1); + assert!(provider.tasks.is_empty()); + + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Enter)); + for _ in 0..4 { + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Tab)); + } + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char('!'))); + handle_task_drafts_key( + &mut state, + &mut provider, + KeyEvent::new(KeyCode::Char('s'), KeyModifiers::CONTROL), + ); + assert!(provider.task_drafts[0].prompt.ends_with("reviewable!")); + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Enter)); + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char('x'))); + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Esc)); + assert_eq!(state.task_draft_dialog, SavedCommandDialog::ConfirmDiscard); + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char('y'))); + assert_eq!(provider.task_drafts[0].title, "Audit plan"); + + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char('d'))); + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char('y'))); + assert!(provider.task_drafts.is_empty()); + assert_eq!(saved.lock().unwrap().len(), 3); + } + + #[cfg(unix)] + #[test] + fn task_draft_creation_is_confirmed_and_plan_list_is_project_loaded() { + let created = Arc::new(Mutex::new(Vec::::new())); + let created_copy = Arc::clone(&created); + let plan = TaskPlanSummary { + id: "plan_a".into(), + title: "Drafted work".into(), + status: "READY".into(), + readiness: "READY".into(), + dependency_count: 0, + attempt_count: 0, + updated_at: "2026-09-26T00:00:00Z".into(), + }; + let mut provider = TaskProvider::new( + Vec::new(), + || Ok(Vec::new()), + |_| Err(io::Error::new(io::ErrorKind::NotFound, "no blob")), + |_| Ok(()), + |_| Ok(None), + |_| Ok(None), + ); + provider.configure_task_drafts( + &[TaskDraft { + title: "Drafted work".into(), + paths: "src/**".into(), + dependencies: String::new(), + quality_gate: "human review".into(), + prompt: "Implement a feature".into(), + }], + |_| Ok(()), + ); + let plan_copy = plan.clone(); + let created_plan = plan.clone(); + provider.configure_task_plans( + &[], + move || Ok(vec![plan_copy.clone()]), + move |draft| { + created_copy.lock().unwrap().push(draft.clone()); + Ok(created_plan.clone()) + }, + ); + let mut state = AttachedInputState { + task_drafts_panel: SavedCommandsPanel::List, + ..AttachedInputState::default() + }; + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char('p'))); + assert_eq!( + state.task_draft_dialog, + SavedCommandDialog::ConfirmCreatePlan + ); + assert!(created.lock().unwrap().is_empty()); + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char('n'))); + assert!(created.lock().unwrap().is_empty()); + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char('p'))); + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char('y'))); + assert_eq!(created.lock().unwrap().len(), 1); + assert_eq!(provider.task_plans.summaries, [plan.clone()]); + assert!(state + .task_draft_message + .as_deref() + .is_some_and(|message| message.contains("no worker was started"))); + + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char('l'))); + assert_eq!(state.task_draft_panel, TaskDraftPanel::Plans); + let mut terminal = Terminal::new(TestBackend::new(100, 24)).unwrap(); + terminal + .draw(|frame| render_task_drafts_overlay(frame, &state, &provider)) + .unwrap(); + assert!(buffer_text(terminal.backend().buffer()).contains("READY")); + handle_task_drafts_key(&mut state, &mut provider, press(KeyCode::Char('r'))); + assert_eq!(provider.task_plans.summaries, [plan]); + } + + #[cfg(unix)] + #[test] + fn repository_notes_are_bounded_saved_and_dirty_close_requires_confirmation() { + let saved = Arc::new(Mutex::new(Vec::>::new())); + let saved_copy = Arc::clone(&saved); + let mut provider = TaskProvider::new( + Vec::new(), + || Ok(Vec::new()), + |_| Err(io::Error::new(io::ErrorKind::NotFound, "no blob")), + |_| Ok(()), + |_| Ok(None), + |_| Ok(None), + ); + provider.configure_workspace_notes(&[], move |notes| { + saved_copy.lock().unwrap().push(notes.to_vec()); + Ok(()) + }); + let mut state = AttachedInputState { + prefix: true, + ..AttachedInputState::default() + }; + assert!(open_workspace_notes_shortcut( + &mut state, + &provider, + press(KeyCode::Char('n')) + )); + handle_workspace_notes_key(&mut state, &mut provider, press(KeyCode::Char('n'))); + handle_workspace_notes_key(&mut state, &mut provider, press(KeyCode::Char('p'))); + handle_workspace_notes_key(&mut state, &mut provider, press(KeyCode::Enter)); + handle_workspace_notes_key(&mut state, &mut provider, press(KeyCode::Char('x'))); + handle_workspace_notes_key(&mut state, &mut provider, press(KeyCode::Left)); + handle_workspace_notes_key(&mut state, &mut provider, press(KeyCode::Char('r'))); + assert_eq!(state.workspace_notes_draft.as_deref(), Some("p\nrx")); + + handle_workspace_notes_key(&mut state, &mut provider, press(KeyCode::Esc)); + assert_eq!( + state.workspace_notes_state, + WorkspaceNotesState::ConfirmDiscard + ); + handle_workspace_notes_key(&mut state, &mut provider, press(KeyCode::Char('n'))); + assert!(state.workspace_notes_draft.is_some()); + + handle_workspace_notes_key( + &mut state, + &mut provider, + KeyEvent::new(KeyCode::Char('s'), KeyModifiers::CONTROL), + ); + assert!(state.workspace_notes_draft.is_none()); + assert_eq!(provider.workspace_notes, ["p\nrx"]); + assert_eq!(saved.lock().unwrap().as_slice(), [vec!["p\nrx"]]); + + handle_workspace_notes_key(&mut state, &mut provider, press(KeyCode::Enter)); + handle_workspace_notes_key(&mut state, &mut provider, press(KeyCode::End)); + handle_workspace_notes_key(&mut state, &mut provider, press(KeyCode::Char('!'))); + handle_workspace_notes_key( + &mut state, + &mut provider, + KeyEvent::new(KeyCode::Char('s'), KeyModifiers::CONTROL), + ); + assert_eq!(provider.workspace_notes, ["p\nrx!"]); + + handle_workspace_notes_key(&mut state, &mut provider, press(KeyCode::Char('d'))); + assert_eq!( + state.workspace_notes_state, + WorkspaceNotesState::ConfirmDelete + ); + handle_workspace_notes_key(&mut state, &mut provider, press(KeyCode::Char('y'))); + assert!(provider.workspace_notes.is_empty()); + assert_eq!( + saved.lock().unwrap().as_slice(), + [ + vec!["p\nrx".to_owned()], + vec!["p\nrx!".to_owned()], + Vec::::new() + ] + ); + } + + #[cfg(unix)] + #[test] + fn repository_notes_enforce_utf8_byte_limit_and_hide_unsupported_palette_action() { + let mut provider = TaskProvider::new( + Vec::new(), + || Ok(Vec::new()), + |_| Err(io::Error::new(io::ErrorKind::NotFound, "no blob")), + |_| Ok(()), + |_| Ok(None), + |_| Ok(None), + ); + let mut state = AttachedInputState { + workspace_notes_draft: Some("é".repeat(MAX_WORKSPACE_NOTES_BYTES / 2)), + ..AttachedInputState::default() + }; + handle_workspace_notes_key(&mut state, &mut provider, press(KeyCode::Char('a'))); + assert_eq!( + state.workspace_notes_draft.as_deref().unwrap().len(), + MAX_WORKSPACE_NOTES_BYTES + ); + assert!( + !palette_actions(TaskCapabilities::read_only(false), "notes") + .contains(&PaletteAction::OpenNotes) + ); + } + + #[cfg(unix)] + #[test] + fn workspace_notes_replace_terminal_and_bidi_controls_on_load() { + let mut provider = TaskProvider::new( + Vec::new(), + || Ok(Vec::new()), + |_| Err(io::Error::new(io::ErrorKind::NotFound, "no blob")), + |_| Ok(()), + |_| Ok(None), + |_| Ok(None), + ); + provider.configure_workspace_notes(&["safe\u{1b}[2J \u{202e}text".to_owned()], |_| Ok(())); + assert_eq!(provider.workspace_notes, ["safe�[2J �text"]); + provider.configure_workspace_notes( + &[format!( + "{}\u{1b}", + "x".repeat(MAX_WORKSPACE_NOTES_BYTES - 1) + )], + |_| Ok(()), + ); + assert!(provider.workspace_notes[0].len() <= MAX_WORKSPACE_NOTES_BYTES); + } + + #[cfg(unix)] + #[test] + fn inline_editor_previews_diff_saves_with_digest_and_confirms_discard() { + let mut provider = test_file_browser_provider(); + let saved = Arc::new(Mutex::new(None)); + let saved_file = Arc::clone(&saved); + provider.save_file = Box::new(move |path, digest, bytes| { + *saved_file.lock().unwrap() = + Some((path.to_owned(), digest.to_owned(), bytes.to_vec())); + Ok(()) + }); + let mut state = AttachedInputState { + file_panel: FilePanelState::Viewer, + file_preview: Some(FilePreviewState { + path: "src/main.rs".to_owned(), + text: "fn main() {}\n".to_owned(), + truncated: false, + binary: false, + }), + file_preview_return: FilePanelState::Tree, + ..AttachedInputState::default() + }; + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Char('e'))); + assert_eq!(state.file_panel, FilePanelState::Editing); + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::End)); + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Char('!'))); + handle_file_panel_key( + &mut state, + &mut provider, + KeyEvent::new(KeyCode::Char('d'), KeyModifiers::CONTROL), + ); + assert_eq!(state.file_panel, FilePanelState::DiffPreview); + let mut terminal = Terminal::new(TestBackend::new(100, 30)).unwrap(); + terminal + .draw(|frame| render_file_diff(frame, &state)) + .unwrap(); + let rendered = buffer_text(terminal.backend().buffer()); + assert!(rendered.contains("-fn main() {}")); + assert!(rendered.contains("+fn main() {}!")); + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Esc)); + assert_eq!(state.file_panel, FilePanelState::Editing); + handle_file_panel_key( + &mut state, + &mut provider, + KeyEvent::new(KeyCode::Char('s'), KeyModifiers::CONTROL), + ); + assert_eq!(state.file_panel, FilePanelState::Viewer); + let (path, digest, bytes) = saved.lock().unwrap().clone().unwrap(); + assert_eq!(path, "src/main.rs"); + assert_eq!( + digest, + rover_core::Sha256Digest::of(b"fn main() {}\n").to_hex() + ); + assert_eq!(bytes, b"fn main() {}!\n"); + + let mut state = AttachedInputState { + file_panel: FilePanelState::Viewer, + file_preview: Some(FilePreviewState { + path: "src/main.rs".to_owned(), + text: "fn main() {}\n".to_owned(), + truncated: false, + binary: false, + }), + ..AttachedInputState::default() + }; + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Char('e'))); + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Char('x'))); + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Esc)); + assert!(state.file_edit.as_ref().unwrap().discard_confirmation); + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Char('n'))); + assert_eq!(state.file_panel, FilePanelState::Editing); + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Esc)); + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Char('y'))); + assert_eq!(state.file_panel, FilePanelState::Viewer); + assert!(state.file_edit.is_none()); + } + + #[cfg(unix)] + #[test] + fn stale_inline_save_keeps_buffer_and_diff_preview_is_bounded() { + let mut provider = test_file_browser_provider(); + provider.save_file = Box::new(|_, _, _| { + Err(io::Error::new( + io::ErrorKind::WouldBlock, + "file changed since editing began; reload before saving", + )) + }); + let mut state = AttachedInputState { + file_panel: FilePanelState::Viewer, + file_preview: Some(FilePreviewState { + path: "src/main.rs".to_owned(), + text: "fn main() {}\n".to_owned(), + truncated: false, + binary: false, + }), + ..AttachedInputState::default() + }; + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Char('e'))); + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::End)); + handle_file_panel_key(&mut state, &mut provider, press(KeyCode::Char('!'))); + handle_file_panel_key( + &mut state, + &mut provider, + KeyEvent::new(KeyCode::Char('s'), KeyModifiers::CONTROL), + ); + assert_eq!(state.file_panel, FilePanelState::Editing); + assert!(state.file_edit.as_ref().unwrap().buffer.ends_with("!\n")); + assert!(state + .file_message + .as_deref() + .unwrap() + .contains("reload before saving")); + + let mut changed = String::new(); + for line in 0..1000 { + use std::fmt::Write as _; + writeln!(changed, "line {line}").unwrap(); + } + let diff = file_diff_lines("large.txt", "old\n", &changed); + assert!(diff.len() <= 1028); + assert!(diff + .iter() + .any(|line| line.to_string().contains("diff preview truncated"))); + let old_many_lines = "old\n".repeat(1_001); + let new_many_lines = "new\n".repeat(1_001); + let unavailable = file_diff_lines("large.txt", &old_many_lines, &new_many_lines); + assert!(unavailable + .iter() + .any(|line| line.to_string().contains("comparison exceeds"))); + let newline_only = file_diff_lines("eof.txt", "line\n", "line"); + assert!(newline_only.iter().any(|line| line.to_string() == "-line")); + assert!(newline_only.iter().any(|line| line.to_string() == "+line")); + assert!(newline_only + .iter() + .any(|line| line.to_string() == "\\ No newline at end of file")); + } + + #[cfg(unix)] + #[test] + fn file_diff_includes_unified_line_ranges_context_and_sanitized_paths() { + let diff = file_diff_lines( + "src/evil\n\u{202e}name.rs", + "before\nkeep\nold\nafter\n", + "before\nkeep\nnew\nafter\n", + ); + let rendered = diff.iter().map(ToString::to_string).collect::>(); + assert!(rendered[0].contains("src/evil��name.rs")); + assert!(!rendered[0].contains('\n')); + assert!(rendered.contains(&"@@ -1,4 +1,4 @@".to_owned())); + assert!(rendered.contains(&" before".to_owned())); + assert!(rendered.contains(&" keep".to_owned())); + assert!(rendered.contains(&"-old".to_owned())); + assert!(rendered.contains(&"+new".to_owned())); + assert!(rendered.contains(&" after".to_owned())); + + let mut original = String::new(); + for line in 0..24 { + use std::fmt::Write as _; + writeln!(original, "line {line}").unwrap(); + } + let edited = original + .replace("line 2\n", "changed two\n") + .replace("line 17\n", "changed seventeen\n"); + let hunks = file_diff_lines("many.rs", &original, &edited); + assert_eq!( + hunks + .iter() + .filter(|line| line.to_string().starts_with("@@ ")) + .count(), + 2 + ); + assert!(hunks.iter().any(|line| line.to_string() == "-line 2")); + assert!(hunks + .iter() + .any(|line| line.to_string() == "+changed seventeen")); + assert!(!hunks.iter().any(|line| line.to_string() == "-line 9")); + + let inserted = file_diff_lines("x", "keep\n", "new\nkeep\n"); + assert!(inserted + .iter() + .any(|line| line.to_string() == "@@ -1,1 +1,2 @@")); + let deleted = file_diff_lines("x", "keep\nremoved\n", "keep\n"); + assert!(deleted + .iter() + .any(|line| line.to_string() == "@@ -1,2 +1,1 @@")); + let unchanged = file_diff_lines("x", "same\n", "same\n"); + assert!(unchanged + .iter() + .any(|line| line.to_string() == "No content differences.")); + } + + #[cfg(unix)] + #[test] + fn refresh_shortcut_is_scoped_to_task_list_and_detail() { + let refresh = press(KeyCode::Char('r')); + assert!(attached_task_refresh_key(TaskPanelState::List, refresh)); + assert!(attached_task_refresh_key(TaskPanelState::Detail, refresh)); + assert!(!attached_task_refresh_key(TaskPanelState::Closed, refresh)); + assert!(!attached_task_refresh_key(TaskPanelState::Stdout, refresh)); + assert!(!attached_task_refresh_key( + TaskPanelState::List, + KeyEvent::new_with_kind( + KeyCode::Char('r'), + KeyModifiers::NONE, + KeyEventKind::Release + ) + )); + } + + #[cfg(unix)] + #[test] + fn cancel_shortcut_is_scoped_to_task_list_and_detail() { + let cancel = press(KeyCode::Char('c')); + assert!(attached_task_cancel_key(TaskPanelState::List, cancel)); + assert!(attached_task_cancel_key(TaskPanelState::Detail, cancel)); + assert!(!attached_task_cancel_key(TaskPanelState::Closed, cancel)); + assert!(!attached_task_cancel_key(TaskPanelState::Stdout, cancel)); + } + + #[cfg(unix)] + #[test] + fn task_cancel_requires_confirmation_and_calls_selected_task_id() { + let cancelled = Arc::new(Mutex::new(Vec::new())); + let cancellation_log = Arc::clone(&cancelled); + let mut provider = TaskProvider::new( + Vec::new(), + || Ok(Vec::new()), + |_| Err(io::Error::new(io::ErrorKind::NotFound, "no blob")), + |task_id| { + cancellation_log + .lock() + .expect("cancellation log mutex") + .push(task_id.to_owned()); + Ok(()) + }, + |_| Ok(None), + |_| Ok(None), + ); + let mut state = AttachedInputState { + pending_cancel: Some("task_selected".to_owned()), + ..AttachedInputState::default() + }; + handle_task_cancel_confirmation(&mut state, &mut provider, press(KeyCode::Char('n'))); + assert!(state.pending_cancel.is_none()); + assert!(cancelled.lock().unwrap().is_empty()); + + state.pending_cancel = Some("task_selected".to_owned()); + handle_task_cancel_confirmation(&mut state, &mut provider, press(KeyCode::Char('y'))); + assert!(state.pending_cancel.is_none()); + assert_eq!(*cancelled.lock().unwrap(), ["task_selected".to_owned()]); + assert_eq!( + state.task_refresh_message.as_deref(), + Some("Cancellation requested; prior external actions are not undone.") + ); + } + + #[cfg(unix)] + #[test] + fn linked_task_evidence_opens_scrolls_and_returns_to_detail() { + let mut task = task_summary("task_a"); + task.candidate = Some("snapshot_a".to_owned()); + task.investigation_id = Some("inv_task_a".to_owned()); + let mut provider = TaskProvider::new( + vec![task], + || Ok(Vec::new()), + |_| Err(io::Error::new(io::ErrorKind::NotFound, "no blob")), + |_| Err(io::Error::new(io::ErrorKind::Unsupported, "cancel unused")), + |_| Ok(Some(investigation_evidence())), + |_| Ok(None), + ); + let mut state = AttachedInputState { + task_panel: TaskPanelState::Detail, + ..AttachedInputState::default() + }; + assert!(open_selected_task_evidence( + &mut state, + &mut provider, + press(KeyCode::Char('i')) + )); + assert_eq!(state.task_panel, TaskPanelState::Evidence); + assert_eq!(state.task_evidence.as_ref().unwrap().decision, "BLOCKED"); + assert!(attached_task_evidence_key( + &mut state, + press(KeyCode::PageDown) + )); + assert_eq!(state.task_evidence_scroll, 12); + assert!(attached_task_evidence_key(&mut state, press(KeyCode::Esc))); + assert_eq!(state.task_panel, TaskPanelState::Detail); + } + + #[cfg(unix)] + #[test] + fn evidence_renderer_labels_recorded_decisions_and_sanitizes_text() { + let mut evidence = investigation_evidence(); + evidence.decision_reason = "pending\u{001b}[31m review\u{202e}".to_owned(); + let mut terminal = Terminal::new(TestBackend::new(80, 20)).unwrap(); + terminal + .draw(|frame| render_task_evidence(frame, Some(&evidence), None, 0)) + .unwrap(); + let text = buffer_text(terminal.backend().buffer()); + assert!(text.contains("recorded assessment")); + assert!(text.contains("Stored decision: BLOCKED")); + assert!(text.contains("Recorded checks")); + assert!(text.contains("UNKNOWN:")); + assert!(text.contains("FINDING:")); + assert!(!text.contains('\u{001b}')); + assert!(!text.contains('\u{202e}')); + } + + #[cfg(unix)] + #[test] + fn candidate_diff_view_is_bound_scrollable_and_sanitized() { + let mut task = task_summary("task_diff"); + task.base_snapshot = Some("snap_base".to_owned()); + task.candidate = Some("snap_candidate".to_owned()); + let mut diff = task_diff(); + diff.changes[0].path = "src/main.rs\u{001b}[31m\u{202e}".to_owned(); + let mut provider = TaskProvider::new( + vec![task], + || Ok(Vec::new()), + |_| Err(io::Error::new(io::ErrorKind::NotFound, "no blob")), + |_| Err(io::Error::new(io::ErrorKind::Unsupported, "cancel unused")), + |_| Ok(None), + |_| Ok(Some(diff.clone())), + ); + let mut state = AttachedInputState { + task_panel: TaskPanelState::Detail, + ..AttachedInputState::default() + }; + assert!(open_selected_task_diff( + &mut state, + &mut provider, + press(KeyCode::Char('d')) + )); + assert_eq!(state.task_panel, TaskPanelState::Diff); + assert_eq!(state.task_diff.as_ref().unwrap().changes.len(), 1); + assert!(attached_task_diff_key(&mut state, press(KeyCode::PageDown))); + assert_eq!(state.task_diff_scroll, 12); + assert!(attached_task_diff_key(&mut state, press(KeyCode::Esc))); + assert_eq!(state.task_panel, TaskPanelState::Detail); + provider.tasks[0].candidate = Some("snap_stale".to_owned()); + assert!(open_selected_task_diff( + &mut state, + &mut provider, + press(KeyCode::Char('d')) + )); + assert!(state.task_diff.is_none()); + assert_eq!( + state.task_diff_message.as_deref(), + Some("Snapshot comparison does not match the selected task.") + ); + + let mut terminal = Terminal::new(TestBackend::new(96, 20)).unwrap(); + let display_diff = task_diff(); + terminal + .draw(|frame| render_task_diff(frame, Some(&display_diff), None, 0)) + .unwrap(); + let text = buffer_text(terminal.backend().buffer()); + assert!(text.contains("verified snapshot inventory")); + assert!(text.contains("Base snapshot: snap_base")); + assert!(text.contains("modified")); + assert!(!text.contains('\u{001b}')); + assert!(!text.contains('\u{202e}')); + } + + #[cfg(unix)] + #[test] + fn cancellation_confirmation_names_task_and_warns_about_external_actions() { + let mut terminal = Terminal::new(TestBackend::new(80, 20)).unwrap(); + terminal + .draw(|frame| render_task_cancel_confirmation(frame, "task_selected")) + .unwrap(); + let text = buffer_text(terminal.backend().buffer()); + assert!(text.contains("Confirm task cancellation")); + assert!(text.contains("task_selected")); + assert!(text.contains("does not undo external actions")); + assert!(text.contains("y confirm")); + } + + #[cfg(unix)] + #[test] + fn failed_or_oversized_task_refresh_keeps_the_previous_snapshot() { + let mut tasks = vec![task_summary("task_a")]; + let original = tasks.clone(); + let mut selected = 0; + let mut message = None; + refresh_task_snapshot(&mut tasks, &mut selected, &mut message, &mut || { + Err(io::Error::other("index is offline\u{001b}")) + }); + assert_eq!(tasks, original); + assert_eq!(message.as_deref(), Some("Refresh failed: index is offline")); + + refresh_task_snapshot(&mut tasks, &mut selected, &mut message, &mut || { + Ok((0..=1_000) + .map(|index| task_summary(&format!("task_{index}"))) + .collect()) + }); + assert_eq!(tasks, original); + assert_eq!( + message.as_deref(), + Some("Refresh refused: more than 1,000 project tasks.") + ); + } + + #[test] + fn task_log_requires_matching_digest_and_sanitizes_controls() { + let bytes = "safe\x1b[31m red\x1b[0m\u{202e}hidden".as_bytes(); + let digest = rover_core::Sha256Digest::of(bytes).to_hex(); + let (text, verified) = prepare_task_log(bytes, &digest).unwrap(); + assert!(verified); + assert!(text.contains("safe�[31m red�[0m�hidden")); + assert!(prepare_task_log(bytes, &"0".repeat(64)).is_err()); + assert!(prepare_task_log(bytes, "BAD").is_err()); + } + + #[test] + fn task_log_view_clips_large_content_after_verifying_full_blob() { + let mut bytes = vec![b'x'; MAX_TASK_LOG_BYTES + 20]; + bytes.extend_from_slice(b"verified-tail"); + let digest = rover_core::Sha256Digest::of(&bytes).to_hex(); + let (text, verified) = prepare_task_log(&bytes, &digest).unwrap(); + assert!(verified); + assert!(text.starts_with(&"x".repeat(MAX_TASK_LOG_BYTES))); + assert!(text.ends_with("[Output clipped at 64 KiB for TUI display.]\n")); + assert!(!text.contains("verified-tail")); + } + + #[test] + fn task_log_renderer_identifies_verified_content_and_scrolls() { + let mut terminal = Terminal::new(TestBackend::new(64, 14)).unwrap(); + terminal + .draw(|frame| render_task_log(frame, "stdout", "first\nsecond\nthird", 1, true)) + .unwrap(); + let text = buffer_text(terminal.backend().buffer()); + assert!(text.contains("stdout · SHA-256 verified")); + assert!(!text.contains("first")); + assert!(text.contains("second")); + assert!(text.contains("third")); + } + + #[cfg(unix)] + #[test] + fn attached_task_log_keys_scroll_and_return_to_details() { + let mut panel = TaskPanelState::Stdout; + let mut scroll = 0; + assert!(attached_task_log_key( + &mut panel, + &mut scroll, + press(KeyCode::Down) + )); + assert_eq!(scroll, 1); + assert!(attached_task_log_key( + &mut panel, + &mut scroll, + press(KeyCode::PageDown) + )); + assert_eq!(scroll, 13); + assert!(attached_task_log_key( + &mut panel, + &mut scroll, + press(KeyCode::PageUp) + )); + assert_eq!(scroll, 1); + assert!(attached_task_log_key( + &mut panel, + &mut scroll, + press(KeyCode::Esc) + )); + assert_eq!(panel, TaskPanelState::Detail); + } + + fn buffer_text(buffer: &ratatui::buffer::Buffer) -> String { + (0..buffer.area.height) + .map(|y| { + (0..buffer.area.width) + .filter_map(|x| buffer.cell((x, y))) + .map(ratatui::buffer::Cell::symbol) + .collect::() + }) + .collect::() + } + + #[test] + fn test_backend_renders_tabs_session_pane_and_unicode_title() { + let mut terminal = Terminal::new(TestBackend::new(48, 12)).unwrap(); + terminal + .draw(|frame| render_workspace(frame, &workspace())) + .unwrap(); + let text = buffer_text(terminal.backend().buffer()); + assert!(text.contains("Rover")); + assert!(text.contains('项') && text.contains('目')); + assert!(text.contains("Session: main")); + assert!(text.contains("Waiting for a terminal screen")); + } + + #[test] + fn attached_rendering_documents_reserved_keys_without_claiming_q_is_global() { + let mut terminal = Terminal::new(TestBackend::new(100, 5)).unwrap(); + terminal + .draw(|frame| render_workspace_mode(frame, &workspace(), &[], true, false)) + .unwrap(); + let text = buffer_text(terminal.backend().buffer()); + assert!(text.contains("Ctrl-]")); + assert!(text.contains("Ctrl-B")); + assert!(text.contains("then q quit")); + } + + #[test] + fn keyboard_help_overlay_captures_keys_until_closed() { + let mut help_open = false; + assert!(handle_help_key(&mut help_open, press(KeyCode::Char('?')))); + assert!(help_open); + assert!(handle_help_key(&mut help_open, press(KeyCode::Char('q')))); + assert!(help_open); + assert!(handle_help_key(&mut help_open, press(KeyCode::Esc))); + assert!(!help_open); + assert!(!handle_help_key(&mut help_open, press(KeyCode::Char('q')))); + + let mut terminal = Terminal::new(TestBackend::new(80, 20)).unwrap(); + terminal + .draw(|frame| render_workspace_mode(frame, &workspace(), &[], true, true)) + .unwrap(); + let text = buffer_text(terminal.backend().buffer()); + assert!(text.contains("Rover keyboard help")); + assert!(text.contains("Ctrl-]")); + assert!(text.contains("command palette")); + assert!(text.contains("close this help")); + } + + #[test] + fn task_board_renderer_bounds_rows_and_sanitizes_record_text() { + let tasks = (0..15) + .map(|index| TaskSummary { + id: format!("task-{index}"), + objective: if index == 13 { + "inspect\u{1b}[31m candidate".to_owned() + } else { + format!("objective {index}") + }, + status: "RUNNING".to_owned(), + updated_at: format!("2026-09-26T00:00:{:02}Z", 59 - index), + error: (index == 13).then(|| "task error\u{1b}[31m".to_owned()), + candidate: (index == 13).then(|| "snapshot_13".to_owned()), + base_snapshot: (index == 13).then(|| "snapshot_base".to_owned()), + investigation_id: None, + attempt_count: usize::from(index == 13) * 2, + process: (index == 13).then(|| TaskProcessEvidence { + exit_code: 3, + error: Some("process error".to_owned()), + timed_out: false, + cancelled: false, + truncated: true, + stdout_bytes: 44, + stderr_bytes: 12, + stdout_sha256: "a".repeat(64), + stderr_sha256: "b".repeat(64), + }), + }) + .collect::>(); + let mut terminal = Terminal::new(TestBackend::new(96, 24)).unwrap(); + terminal + .draw(|frame| render_task_board(frame, &tasks, 13, false)) + .unwrap(); + let text = buffer_text(terminal.backend().buffer()); + assert!(text.contains("Rover task board")); + assert!(text.contains("Showing 4–15 of 15 tasks")); + assert!(text.contains("task-13")); + assert!(text.contains("inspect[31m candidate")); + assert!(!text.contains('\u{1b}')); + assert!(!text.contains("task-0 ")); + + let mut narrow = Terminal::new(TestBackend::new(40, 10)).unwrap(); + narrow + .draw(|frame| render_task_board(frame, &tasks, 0, false)) + .unwrap(); + let narrow_text = buffer_text(narrow.backend().buffer()); + assert!(narrow_text.contains("Showing 1–2 of 15 tasks")); + + let mut detail = Terminal::new(TestBackend::new(96, 24)).unwrap(); + detail + .draw(|frame| render_task_board(frame, &tasks, 13, true)) + .unwrap(); + let detail_text = buffer_text(detail.backend().buffer()); + assert!(detail_text.contains("Rover task detail")); + assert!(detail_text.contains("Attempts: 2")); + assert!(detail_text.contains("Process: exit 3")); + assert!(detail_text.contains("Stdout observed: 44 bytes")); + assert!(detail_text.contains("recorded SHA-256")); + assert!(detail_text.contains("Task error: task error[31m")); + assert!(!detail_text.contains('\u{1b}')); + } + + #[test] + fn read_only_task_provider_hides_unsupported_actions() { + let provider = TaskProvider::read_only_snapshot( + vec![task_summary("task_read_only")], + |_| Err(io::Error::new(io::ErrorKind::NotFound, "no blob")), + false, + ); + assert!(!provider.capabilities.supports(TaskCapabilities::REFRESH)); + assert!(!provider.capabilities.supports(TaskCapabilities::LOGS)); + assert!(!provider.capabilities.supports(TaskCapabilities::CANCEL)); + assert!(!provider.capabilities.supports(TaskCapabilities::EVIDENCE)); + assert!(!provider.capabilities.supports(TaskCapabilities::DIFF)); + + let mut terminal = Terminal::new(TestBackend::new(96, 24)).unwrap(); + terminal + .draw(|frame| { + render_task_board_with_refresh( + frame, + &provider.tasks, + 0, + true, + None, + provider.capabilities, + TaskBoardView { + preferences: &provider.view_preferences, + search_draft: None, + }, + ); + }) + .unwrap(); + let text = buffer_text(terminal.backend().buffer()); + assert!(!text.contains("r refresh")); + assert!(!text.contains("c cancel")); + assert!(!text.contains("o stdout")); + assert!(!text.contains("i evidence")); + } + + #[cfg(unix)] + #[test] + fn attached_task_board_uses_prefix_and_wraps_selection() { + let mut panel = TaskPanelState::Closed; + let mut prefix = false; + let mut selected = 0; + let tasks = vec![ + task_summary("task_a"), + task_summary("task_b"), + task_summary("task_c"), + ]; + let preferences = TaskViewPreferences::default(); + assert!(!attached_task_board_key( + &mut panel, + &mut prefix, + &mut selected, + &tasks, + &preferences, + press(KeyCode::Char('t')) + )); + prefix = true; + assert!(attached_task_board_key( + &mut panel, + &mut prefix, + &mut selected, + &tasks, + &preferences, + press(KeyCode::Char('t')) + )); + assert_eq!(panel, TaskPanelState::List); + assert!(!prefix); + assert!(attached_task_board_key( + &mut panel, + &mut prefix, + &mut selected, + &tasks, + &preferences, + press(KeyCode::Enter) + )); + assert_eq!(panel, TaskPanelState::Detail); + assert!(attached_task_board_key( + &mut panel, + &mut prefix, + &mut selected, + &tasks, + &preferences, + press(KeyCode::Up) + )); + assert_eq!(selected, 2); + assert!(attached_task_board_key( + &mut panel, + &mut prefix, + &mut selected, + &tasks, + &preferences, + press(KeyCode::Down) + )); + assert_eq!(selected, 0); + assert!(attached_task_board_key( + &mut panel, + &mut prefix, + &mut selected, + &tasks, + &preferences, + press(KeyCode::Char('q')) + )); + assert_eq!(panel, TaskPanelState::Detail); + assert!(attached_task_board_key( + &mut panel, + &mut prefix, + &mut selected, + &tasks, + &preferences, + press(KeyCode::Esc) + )); + assert_eq!(panel, TaskPanelState::List); + assert!(attached_task_board_key( + &mut panel, + &mut prefix, + &mut selected, + &tasks, + &preferences, + press(KeyCode::Esc) + )); + assert_eq!(panel, TaskPanelState::Closed); + } + + #[test] + fn parsed_pty_screen_renders_without_terminal_controls() { + let workspace = workspace(); + let PaneNode::Leaf(pane) = workspace.active_tab().root() else { + panic!("fixture has one pane"); + }; + let mut screen = TerminalScreen::new( + rover_execution::terminal::TerminalSize::new(4, 40).unwrap(), + 0, + ) + .unwrap(); + screen + .process_output(b"safe\x1b[31m red\x1b[0m\x1b]52;c;AAAA\x07 visible") + .unwrap(); + let screens = [ScreenText::from_terminal_screen(pane, &screen)]; + let mut terminal = Terminal::new(TestBackend::new(48, 12)).unwrap(); + terminal + .draw(|frame| render_workspace_with_screens(frame, &workspace, &screens)) + .unwrap(); + let text = buffer_text(terminal.backend().buffer()); + assert!(text.contains("safe red")); + assert!(text.contains("visible")); + assert!(!text.contains('\u{1b}')); + assert!(!text.contains("52;c")); + } + + #[cfg(unix)] + #[test] + fn attached_agent_detection_uses_the_live_viewport_and_osc_title() { + let mut screen = TerminalScreen::new( + rover_execution::terminal::TerminalSize::new(4, 40).unwrap(), + 20, + ) + .unwrap(); + screen + .process_output(b"scrollback only\r\n\x1b]0;Amp: Plugin confirmation needed\x07") + .unwrap(); + let detection = detect_foreground_sample(b"amp", &screen); + assert_eq!(detection.agent.as_deref(), Some("amp")); + assert_eq!(detection.state, AgentState::Blocked); + assert!(detection.visibility.blocker); + assert_eq!(detection.evidence[0].source, "foreground_process"); + assert_eq!( + detection.evidence[1].rule_id.as_deref(), + Some("osc_title_plugin_confirmation_blocked") + ); + + screen.process_output(b"\x1b]9;4;1;-1\x07").unwrap(); + let progress_detection = detect_foreground_sample(b"grok", &screen); + assert_eq!(progress_detection.state, AgentState::Working); + assert!(progress_detection.visibility.working); + assert_eq!( + progress_detection.evidence[1].rule_id.as_deref(), + Some("osc_progress_working") + ); + } + + #[cfg(unix)] + #[test] + fn foreground_group_detection_uses_recognized_member_when_leader_is_unknown() { + let mut screen = TerminalScreen::new( + rover_execution::terminal::TerminalSize::new(4, 40).unwrap(), + 20, + ) + .unwrap(); + screen + .process_output(b"\x1b]0;Amp: Plugin confirmation needed\x07") + .unwrap(); + let detection = detect_foreground_group_sample(b"sh", &["amp".to_owned()], &[], &screen); + assert_eq!(detection.agent.as_deref(), Some("amp")); + assert_eq!(detection.state, AgentState::Blocked); + assert_eq!(detection.evidence[0].source, "foreground_process"); + } + + #[cfg(unix)] + #[test] + fn conflicting_agent_names_in_foreground_group_remain_unknown() { + let screen = TerminalScreen::new( + rover_execution::terminal::TerminalSize::new(4, 40).unwrap(), + 20, + ) + .unwrap(); + let detection = detect_foreground_group_sample( + b"sh", + &["amp".to_owned(), "codex".to_owned()], + &[], + &screen, + ); + assert_eq!(detection.agent, None); + assert_eq!(detection.state, AgentState::Unknown); + assert_eq!(detection.reason, "ambiguous_foreground_process_group"); + assert_eq!(detection.evidence.len(), 2); + // The operator must be able to see *which* agents disagreed. + let mut reported = detection + .evidence + .iter() + .map(|evidence| evidence.value.clone()) + .collect::>(); + reported.sort(); + assert_eq!(reported, vec!["amp".to_owned(), "codex".to_owned()]); + assert!(detection + .evidence + .iter() + .all(|evidence| evidence.source == "foreground_process")); + + let leader_wins = + detect_foreground_group_sample(b"amp", &["codex".to_owned()], &[], &screen); + assert_eq!(leader_wins.agent.as_deref(), Some("amp")); + assert_ne!(leader_wins.reason, "ambiguous_foreground_process_group"); + } + + #[cfg(unix)] + #[test] + fn transcript_viewer_skip_rule_preserves_the_last_screen_state() { + let mut screen = TerminalScreen::new( + rover_execution::terminal::TerminalSize::new(10, 80).unwrap(), + 20, + ) + .unwrap(); + screen + .process_output(b"\x1b]0;Codex \xe2\xa0\x8b\x07") + .unwrap(); + let mut current = None; + apply_agent_detection(&mut current, detect_foreground_sample(b"codex", &screen)); + assert_eq!( + current.as_ref().map(|result| result.state), + Some(AgentState::Working) + ); + + screen.process_output(b"\x1b]0;\x07\r\n\xe2\x80\xba previous prompt\r\n\xe2\x86\x91/\xe2\x86\x93 to scroll\r\npgup/pgdn to move\r\nhome/end to jump\r\nq to quit\r\nesc to edit prev").unwrap(); + let skipped = detect_foreground_sample(b"codex", &screen); + assert!(skipped.skip_state_update); + apply_agent_detection(&mut current, skipped); + + let preserved = current.expect("previous state is retained"); + assert_eq!(preserved.state, AgentState::Working); + assert!(preserved.skip_state_update); + assert_eq!(preserved.reason, "screen_state_preserved_for_skip_update"); + assert!(preserved + .evidence + .iter() + .any(|evidence| { evidence.rule_id.as_deref() == Some("transcript_viewer") })); + + let previous_evidence_count = preserved.evidence.len(); + let mut current = Some(preserved); + apply_agent_detection(&mut current, detect_foreground_sample(b"codex", &screen)); + assert_eq!( + current.as_ref().map(|result| result.evidence.len()), + Some(previous_evidence_count) + ); + + let mut different_agent = current.unwrap(); + different_agent.agent = Some("claude".to_owned()); + different_agent.state = AgentState::Unknown; + different_agent.skip_state_update = true; + let mut current = Some(DetectionResult { + state: AgentState::Working, + ..detect_foreground_sample(b"codex", &screen) + }); + apply_agent_detection(&mut current, different_agent); + let changed = current.unwrap(); + assert_eq!(changed.agent.as_deref(), Some("claude")); + assert_eq!(changed.state, AgentState::Unknown); + } + + #[test] + fn test_backend_handles_narrow_frames() { + for (width, height) in [(1, 1), (8, 3), (16, 5)] { + let mut terminal = Terminal::new(TestBackend::new(width, height)).unwrap(); + terminal + .draw(|frame| render_workspace(frame, &workspace())) + .unwrap(); + } + } + + #[test] + fn key_bindings_split_focus_zoom_popup_and_close_layout_panes() { + let mut workspace = workspace(); + assert!(!handle_key(&mut workspace, press(KeyCode::Char('|')))); + assert_eq!(workspace.pane_count(), 2); + let first = match workspace.active_tab().root() { + PaneNode::Split(split) => match split.first() { + PaneNode::Leaf(pane) => pane.id().to_owned(), + PaneNode::Split(_) => panic!("first child must remain the existing leaf"), + }, + PaneNode::Leaf(_) => panic!("split key must split the focused pane"), + }; + assert!(!handle_key(&mut workspace, press(KeyCode::Tab))); + assert_eq!(workspace.active_tab().active_pane(), first); + assert!(!handle_key(&mut workspace, press(KeyCode::Char('z')))); + assert_eq!(workspace.active_tab().zoomed_pane(), Some(first.as_str())); + assert!(!handle_key(&mut workspace, press(KeyCode::Char('p')))); + assert!(workspace.active_tab().popup_pane().is_some()); + assert!(!handle_key(&mut workspace, press(KeyCode::Esc))); + assert!(workspace.active_tab().popup_pane().is_none()); + assert!(!handle_key(&mut workspace, press(KeyCode::Char('x')))); + assert_eq!(workspace.pane_count(), 1); + assert!(handle_key(&mut workspace, press(KeyCode::Char('q')))); + assert!(!handle_key( + &mut workspace, + KeyEvent::new_with_kind( + KeyCode::Char('q'), + KeyModifiers::NONE, + KeyEventKind::Release + ) + )); + } + + #[test] + fn tab_shortcuts_preserve_workspace_order_and_focus() { + let mut workspace = workspace(); + assert!(!handle_key(&mut workspace, press(KeyCode::Char('n')))); + assert_eq!(workspace.tabs().len(), 2); + assert_eq!(workspace.active_tab().title(), "scratch-2"); + assert!(!handle_key(&mut workspace, press(KeyCode::Char('[')))); + assert_eq!(workspace.active_tab().title(), "main"); + assert!(!handle_key(&mut workspace, press(KeyCode::Char(']')))); + assert_eq!(workspace.active_tab().title(), "scratch-2"); + } + + #[cfg(unix)] + #[test] + fn attached_key_translation_preserves_text_controls_and_detach_binding() { + assert_eq!( + key_input_bytes(press(KeyCode::Char('x'))), + Some(b"x".to_vec()) + ); + assert_eq!( + key_input_bytes(KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL)), + Some(vec![0x03]) + ); + assert_eq!(key_input_bytes(press(KeyCode::Enter)), Some(vec![b'\r'])); + assert_eq!( + key_input_bytes(press(KeyCode::Up)), + Some(b"\x1b[A".to_vec()) + ); + assert_eq!( + key_input_bytes(press(KeyCode::Char('é'))), + Some("é".as_bytes().to_vec()) + ); + assert!(is_detach_key(KeyEvent::new( + KeyCode::Char(']'), + KeyModifiers::CONTROL + ))); + assert!(!is_detach_key(press(KeyCode::Char(']')))); + } + + #[cfg(unix)] + #[test] + fn attached_workspace_shortcut_dispatch_applies_prefixed_action() { + let mut workspace = workspace(); + assert_eq!(workspace.tabs().len(), 1); + assert!(!attached_workspace_key( + &mut workspace, + press(KeyCode::Char('n')) + )); + assert_eq!(workspace.tabs().len(), 2); + } + + #[cfg(unix)] + #[test] + fn attached_help_uses_prefix_so_question_marks_reach_the_shell() { + let mut help_open = false; + let mut prefix = false; + assert!(!attached_help_key( + &mut help_open, + &mut prefix, + press(KeyCode::Char('?')) + )); + assert!(!help_open); + + prefix = true; + assert!(attached_help_key( + &mut help_open, + &mut prefix, + press(KeyCode::Char('?')) + )); + assert!(help_open); + assert!(!prefix); + assert!(attached_help_key( + &mut help_open, + &mut prefix, + press(KeyCode::Char('q')) + )); + assert!(help_open); + assert!(attached_help_key( + &mut help_open, + &mut prefix, + press(KeyCode::Esc) + )); + assert!(!help_open); + } + + #[cfg(unix)] + #[test] + fn persistent_tui_restores_saved_layout_and_fails_closed_on_corruption() { + let (path, directory) = private_state_dir(); + let mut saved = workspace(); + saved + .split_active( + SplitAxis::Vertical, + 4_000, + rover_execution::layout::PaneSpec::scratch(), + ) + .unwrap(); + saved.save_to(&directory, "layout.json").unwrap(); + assert_eq!( + load_workspace_or_initial(workspace(), &directory, "layout.json").unwrap(), + saved + ); + + let initial = workspace(); + assert_eq!( + load_workspace_or_initial(initial.clone(), &directory, "new.json").unwrap(), + initial + ); + directory.atomic_write("broken.json", b"{}", 0o600).unwrap(); + assert_eq!( + load_workspace_or_initial(workspace(), &directory, "broken.json") + .unwrap_err() + .kind(), + io::ErrorKind::InvalidData + ); + drop(directory); + fs::remove_dir_all(path).unwrap(); + } + + #[derive(Clone, Debug, Default, Eq, PartialEq)] + struct FakeTerminalState { + raw: bool, + alternate: bool, + paste: bool, + } + + struct FakeTerminalControl { + state: Arc>, + fail_enter: bool, + } + + impl TerminalControl for FakeTerminalControl { + fn enable_raw(&mut self) -> io::Result<()> { + self.state.lock().unwrap().raw = true; + Ok(()) + } + + fn enter_screen(&mut self) -> io::Result<()> { + if self.fail_enter { + return Err(io::Error::other("simulated alternate-screen failure")); + } + let mut state = self.state.lock().unwrap(); + state.alternate = true; + state.paste = true; + Ok(()) + } + + fn restore(&mut self) { + *self.state.lock().unwrap() = FakeTerminalState::default(); + } + } + + #[test] + fn terminal_guard_restores_state_on_normal_exit_unwind_and_partial_setup() { + let state = Arc::new(Mutex::new(FakeTerminalState::default())); + let guard = TerminalGuard::enter_with(FakeTerminalControl { + state: Arc::clone(&state), + fail_enter: false, + }) + .unwrap(); + assert!(state.lock().unwrap().raw); + assert!(state.lock().unwrap().alternate); + assert!(state.lock().unwrap().paste); + drop(guard); + assert_eq!(*state.lock().unwrap(), FakeTerminalState::default()); + + let state = Arc::new(Mutex::new(FakeTerminalState::default())); + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe({ + let state = Arc::clone(&state); + move || { + let _guard = TerminalGuard::enter_with(FakeTerminalControl { + state, + fail_enter: false, + }) + .unwrap(); + panic!("exercise terminal guard unwind"); + } + })); + assert!(result.is_err()); + assert_eq!(*state.lock().unwrap(), FakeTerminalState::default()); + + let state = Arc::new(Mutex::new(FakeTerminalState::default())); + assert!(TerminalGuard::enter_with(FakeTerminalControl { + state: Arc::clone(&state), + fail_enter: true, + }) + .is_err()); + assert_eq!(*state.lock().unwrap(), FakeTerminalState::default()); + } +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..079e408 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "1.88.0" +profile = "minimal" +components = ["clippy", "rustfmt"] From abb6e56ddc189c40d351c478631515d808842ee3 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:41:27 +0530 Subject: [PATCH 02/23] docs(design): add Rust port ADRs and parity/migration plans Track ADR-0001 through ADR-0027 for the Rust port together with the Go to Rust state migration guide, compatibility baseline, dependency audit, parity plan, upstream feature matrix and the upstream source audit that records the Herdr manifest provenance. Co-Authored-By: Claude Opus 5.5 --- .../ADR-0001-rust-tui-product-boundary.md | 65 ++++ .../ADR-0002-rust-workspace-architecture.md | 77 +++++ ...-0003-core-identifiers-digests-and-time.md | 63 ++++ docs/design/ADR-0004-rust-sqlite-adapter.md | 80 +++++ docs/design/ADR-0005-rust-record-json.md | 59 ++++ docs/design/ADR-0006-stack-safe-json.md | 68 ++++ .../ADR-0007-rust-rooted-file-operations.md | 89 ++++++ docs/design/ADR-0008-rust-project-identity.md | 54 ++++ .../design/ADR-0009-rust-process-execution.md | 68 ++++ .../ADR-0010-rust-task-supervisor-state.md | 81 +++++ docs/design/ADR-0011-rust-pty-backend.md | 53 ++++ .../design/ADR-0012-rust-terminal-emulator.md | 49 +++ docs/design/ADR-0013-rust-session-broker.md | 65 ++++ .../ADR-0014-rust-workspace-layout-model.md | 53 ++++ docs/design/ADR-0015-rust-tui-framework.md | 60 ++++ .../ADR-0016-rust-task-briefing-drafts.md | 51 +++ .../ADR-0017-rust-task-brief-contract.md | 62 ++++ .../design/ADR-0018-rust-task-plan-storage.md | 53 ++++ .../ADR-0019-rust-agent-profile-registry.md | 46 +++ .../ADR-0020-native-agent-transcripts.md | 45 +++ .../ADR-0021-agent-detection-evidence.md | 60 ++++ docs/design/ADR-0022-agent-state-rollups.md | 38 +++ .../ADR-0023-agent-lifecycle-authority.md | 38 +++ ...-0024-agent-session-identity-and-resume.md | 48 +++ .../ADR-0025-agent-session-inventory-cli.md | 46 +++ .../ADR-0026-owned-pty-process-sampling.md | 53 ++++ ...-0027-herdr-compatible-screen-manifests.md | 61 ++++ docs/design/GO_RUST_STATE_MIGRATION.md | 58 ++++ docs/design/RUST_COMPATIBILITY_BASELINE.md | 125 ++++++++ docs/design/RUST_DEPENDENCY_AUDIT.md | 216 +++++++++++++ docs/design/RUST_PARITY_PLAN.md | 290 ++++++++++++++++++ docs/design/UPSTREAM_FEATURE_MATRIX.md | 227 ++++++++++++++ docs/design/UPSTREAM_SOURCE_AUDIT.md | 125 ++++++++ 33 files changed, 2626 insertions(+) create mode 100644 docs/design/ADR-0001-rust-tui-product-boundary.md create mode 100644 docs/design/ADR-0002-rust-workspace-architecture.md create mode 100644 docs/design/ADR-0003-core-identifiers-digests-and-time.md create mode 100644 docs/design/ADR-0004-rust-sqlite-adapter.md create mode 100644 docs/design/ADR-0005-rust-record-json.md create mode 100644 docs/design/ADR-0006-stack-safe-json.md create mode 100644 docs/design/ADR-0007-rust-rooted-file-operations.md create mode 100644 docs/design/ADR-0008-rust-project-identity.md create mode 100644 docs/design/ADR-0009-rust-process-execution.md create mode 100644 docs/design/ADR-0010-rust-task-supervisor-state.md create mode 100644 docs/design/ADR-0011-rust-pty-backend.md create mode 100644 docs/design/ADR-0012-rust-terminal-emulator.md create mode 100644 docs/design/ADR-0013-rust-session-broker.md create mode 100644 docs/design/ADR-0014-rust-workspace-layout-model.md create mode 100644 docs/design/ADR-0015-rust-tui-framework.md create mode 100644 docs/design/ADR-0016-rust-task-briefing-drafts.md create mode 100644 docs/design/ADR-0017-rust-task-brief-contract.md create mode 100644 docs/design/ADR-0018-rust-task-plan-storage.md create mode 100644 docs/design/ADR-0019-rust-agent-profile-registry.md create mode 100644 docs/design/ADR-0020-native-agent-transcripts.md create mode 100644 docs/design/ADR-0021-agent-detection-evidence.md create mode 100644 docs/design/ADR-0022-agent-state-rollups.md create mode 100644 docs/design/ADR-0023-agent-lifecycle-authority.md create mode 100644 docs/design/ADR-0024-agent-session-identity-and-resume.md create mode 100644 docs/design/ADR-0025-agent-session-inventory-cli.md create mode 100644 docs/design/ADR-0026-owned-pty-process-sampling.md create mode 100644 docs/design/ADR-0027-herdr-compatible-screen-manifests.md create mode 100644 docs/design/GO_RUST_STATE_MIGRATION.md create mode 100644 docs/design/RUST_COMPATIBILITY_BASELINE.md create mode 100644 docs/design/RUST_DEPENDENCY_AUDIT.md create mode 100644 docs/design/RUST_PARITY_PLAN.md create mode 100644 docs/design/UPSTREAM_FEATURE_MATRIX.md create mode 100644 docs/design/UPSTREAM_SOURCE_AUDIT.md diff --git a/docs/design/ADR-0001-rust-tui-product-boundary.md b/docs/design/ADR-0001-rust-tui-product-boundary.md new file mode 100644 index 0000000..b9c200b --- /dev/null +++ b/docs/design/ADR-0001-rust-tui-product-boundary.md @@ -0,0 +1,65 @@ +# ADR-0001: Rust CLI/TUI product boundary + +- Status: accepted; SDK removal implemented, Rust port in progress +- Date: 2026-09-24 +- Owners: Rover maintainers + +## Context + +The requested destination is a Rust implementation of Rover that adopts the +capabilities of Herdr, Luvus, and Orca ADE. The selected product interfaces are +the command-line interface (CLI) and terminal user interface (TUI). The user +explicitly requested removal of Rover SDKs and clarified: port all behavior to +Rust CLI/TUI, with upstream source reused only after audit. + +Rover is currently implemented in Go, has Python, TypeScript, and Go CLI SDK +directories, and documents security/acceptance contracts that must survive a +rewrite. The upstreams have different languages, surfaces, revisions, and +license histories. Earlier indexed Luvus README and LICENSE snapshots differed; +the pinned `63cb482…` revision audited for this plan has Apache-2.0 in both. +Herdr's current license does not automatically apply to older tagged releases. + +## Decisions + +1. The product surface is CLI and keyboard-operated TUI. Do not add a separate + desktop GUI, web UI, or mobile application. A capability can be represented + through a CLI/TUI workflow only when its acceptance case documents the + equivalent interaction and resulting behavior. +2. The Rust implementation is the target runtime. Rust crates, schemas, and + protocols must be selected through recorded decisions and tested contracts; + language choice alone does not establish compatibility or security. +3. Python, TypeScript, and Go SDK removal is authorized and should be completed + as a separate bounded change. It does not authorize removal of the existing + Go CLI/TUI runtime; that remains gated on Rust parity and migration. +4. Preserve existing Rover CLI, JSON, exit-code, configuration, acceptance, + and security behavior unless a later, explicit compatibility decision names + the change and its migration. +5. Upstream code may be reused only from an immutable, audited revision after + license, copyright, NOTICE, asset, and dependency review. Public availability + does not resolve contradictory license statements. Until resolved, use + independently implemented behavior based on documented interfaces and do + not import disputed source. +6. Port every in-scope behavior into Rust CLI/TUI. Read non-Rust GUI/mobile/cloud + code when it is the authoritative behavior source, but do not add those + applications to Rover. Reuse only individual eligible modules or assets after + recording source commit/path/blob digest, license/NOTICE obligations and a + Rust acceptance test. Implement independently whenever that evidence is not + complete. Do not vendor whole repositories into the product. + +## Consequences + +- All feature claims require a traceable CLI/TUI acceptance case. +- GUI-only behaviors must have a validated terminal equivalent or be listed as + unsupported; they must not be represented by a stub. +- SDK removal and Go runtime removal are separate gated changes. +- License ambiguity blocks source import, but does not block an independent + implementation of the required behavior. +- This ADR does not approve dependency or license changes, external writes, or + releases. + +## References + +- [Rust feature parity plan](RUST_PARITY_PLAN.md) +- [Rover command reference](../CLI.md) +- [Rover security model](../SECURITY_MODEL.md) +- [Rover acceptance map](../acceptance/implementation-map.json) diff --git a/docs/design/ADR-0002-rust-workspace-architecture.md b/docs/design/ADR-0002-rust-workspace-architecture.md new file mode 100644 index 0000000..ae948f7 --- /dev/null +++ b/docs/design/ADR-0002-rust-workspace-architecture.md @@ -0,0 +1,77 @@ +# ADR-0002: Rust workspace and process ownership + +- Status: accepted for implementation foundation; dependency choices remain open +- Date: 2026-09-25 +- Owners: Rover maintainers + +## Context + +Rover must retain its current CLI, JSON, exit-code, state, process supervision, +and evidence contracts while adding terminal workspaces and agent workflows. +The upstreams use multiple processes, protocols, and extension points. The +first Rust foundation must compile without implying that those features exist. +The typed core dependencies are reviewed in ADR-0003. SQLite and JSON record +encoding are reviewed separately in ADR-0004 and ADR-0005; other adapter +dependencies remain gated. + +## Decisions + +1. Use a Cargo workspace organized by domain boundaries. `rover-core` owns + versioned domain types, validation, and canonical repository identity. + `rover-store` owns database migrations, transactional record APIs, atomic + agent/resource reservations, atomic local budget accounting, and rooted Unix + blob/file operations. + `rover-access` owns project and state + scoped bearer grant issuance, authentication, and revocation. These are + library foundations; CLI/TUI wiring and the remaining capabilities remain + later work. `rover-execution` owns argv-only bounded local process + execution under ADR-0009. Planned crate boundaries include + `store`, + `source`, `execution`, `terminal`, `agents`, `assurance`, `workflow`, + `remote`, `plugins`, `tui`, and `cli`. Add each crate when its public + contract and tests are ready; do not create capability stubs. +2. The eventual installed product remains one Rust `rover` executable. An + internal supervisor role owns detached worker and terminal process + lifetimes; CLI and TUI clients request operations over owner-private local + IPC. The supervisor records ambiguous process loss as `LOST` rather than + claiming recovery. Network listeners and remote execution are separate, + explicit capabilities governed by the existing security model. +3. Keep CLI, TUI, and supervisor orchestration in separate crates/modules even + if initially linked into one binary. This keeps rendering and input handling + from becoming process or policy owners. +4. Keep the domain core small. Ratatui/Crossterm, a PTY and terminal + emulator, SQLite, async runtime, crypto, SSH, and browser + libraries each require a separate reviewed contract, supported-platform + statement, license/dependency audit, and tests before addition. rusqlite is + approved separately in ADR-0004. +5. The first compatibility slice ports Rover's ASCII identifier validation as + a typed core value. This does not claim full model, JSON, hash, timestamp, + or command parity. + +## Alternatives considered + +- A single crate for all behavior: rejected because terminal rendering, process + lifetime, storage, and policy need separate owners and test surfaces. +- A library-only Rust replacement with no supervisor role: rejected because + detached tasks and persistent terminal sessions need an explicit lifetime + owner. +- Add the upstream dependency sets wholesale: rejected because dependency + licenses, maintenance, platform scope, features, and Rover acceptance + contracts have not been audited. + +## Consequences + +- Existing Go behavior remains authoritative until an individually mapped Rust + capability passes its compatibility and security acceptance cases. +- There is no Rust CLI/TUI feature claim from the core crate alone. +- Future crates and dependencies require explicit review; unknown dependency + licenses and unsupported platform claims fail closed. + +## References + +- [Rust parity plan](RUST_PARITY_PLAN.md) +- [Core model dependency decision](ADR-0003-core-identifiers-digests-and-time.md) +- [Current Rust dependency audit](RUST_DEPENDENCY_AUDIT.md) +- [Rust process execution decision](ADR-0009-rust-process-execution.md) +- [Compatibility baseline](RUST_COMPATIBILITY_BASELINE.md) +- [Security model](../SECURITY_MODEL.md) diff --git a/docs/design/ADR-0003-core-identifiers-digests-and-time.md b/docs/design/ADR-0003-core-identifiers-digests-and-time.md new file mode 100644 index 0000000..3c02e6a --- /dev/null +++ b/docs/design/ADR-0003-core-identifiers-digests-and-time.md @@ -0,0 +1,63 @@ +# ADR-0003: Core identifiers, digests, and timestamps + +- Status: accepted for the P-021 compatibility slice +- Date: 2026-09-25 +- Owners: Rover maintainers + +## Context + +Rover's Go model creates IDs from 16 operating-system random bytes, renders +SHA-256 digests as lowercase hexadecimal, and formats UTC timestamps as +RFC3339Nano. Rust's standard library does not provide a cross-platform secure +random API, SHA-256, or RFC3339 formatting. Implementing cryptography or +platform entropy access in Rover would add avoidable correctness and security +risk. + +## Decisions + +1. Use `getrandom` 0.4.3 for operating-system cryptographic random bytes. Do + not enable its browser/WASM backend. ID creation is fallible; entropy-source + failure must reach the caller, never produce a predictable fallback. +2. Use RustCrypto `sha2` 0.11.0 for SHA-256. Return the existing lowercase + 64-character hex representation. Do not add a separate hex dependency. +3. Use `time` 0.3.55 with only the `formatting` feature (which enables `std`) + for UTC clock reads and RFC3339 formatting. Do not enable local-offset, + serde, parsing, macros, or unrelated features. Preserve fractional-second + trimming and UTC `Z` output in regression tests. +4. Pin these direct versions exactly in the workspace manifest and retain the + Cargo lockfile. All three direct crates declare MIT OR Apache-2.0. Their + resolved transitive crates must also be inventoried; an unknown license + blocks builds and reuse until resolved. +5. Raise the Rover Rust MSRV to 1.88 because the current `time` release + requires Rust 1.88. This is a published build requirement for the Rust + workspace; it does not change the existing Go toolchain requirement. + +## Alternatives considered + +- Implement SHA-256 in Rover: rejected because cryptographic code should use a + maintained implementation with its known-answer tests. +- Read `/dev/urandom` directly: rejected because it is not a cross-platform + operating-system RNG contract. +- Use wall-clock formatting by hand: rejected because calendar, offset, and + fractional-second formatting require tricky edge-case handling. +- Pin older library releases solely to retain Rust 1.75: rejected because that + would trade away current fixes without an established Rust MSRV policy. + +## Verification requirements + +- SHA-256 known-answer vectors, including empty input and `abc`. +- Generated IDs have the requested valid prefix, 32 lowercase hex entropy + characters, and pass the same validator; generation errors are propagated. +- Timestamp fixtures prove UTC `Z`, RFC3339 validity, nanosecond precision, + and Go-compatible trimming of trailing fractional zeros. +- Inspect `cargo tree` and resolved package metadata for the exact dependency + graph, licenses, and target-specific packages before closing P-019. + +## References + +- [`sha2` 0.11 documentation](https://docs.rs/sha2/0.11.0/sha2/) +- [`getrandom` 0.4 documentation](https://docs.rs/getrandom/0.4.3/getrandom/) +- [`time` 0.3 documentation](https://docs.rs/time/0.3.55/time/) +- [Rust parity plan](RUST_PARITY_PLAN.md) +- [Rust dependency audit](RUST_DEPENDENCY_AUDIT.md) +- [Bundled third-party notices](../../licenses/THIRD_PARTY_NOTICES.md) diff --git a/docs/design/ADR-0004-rust-sqlite-adapter.md b/docs/design/ADR-0004-rust-sqlite-adapter.md new file mode 100644 index 0000000..ac14a87 --- /dev/null +++ b/docs/design/ADR-0004-rust-sqlite-adapter.md @@ -0,0 +1,80 @@ +# ADR-0004: Rust SQLite adapter + +- Status: accepted for the Rust storage foundation +- Date: 2026-09-25 +- Owners: Rover maintainers; implementation owner is `internal/store` / `rover-store` + +## Context + +Rover's Go store uses SQLite for controller-owned JSON records, append-only +events, and idempotency keys. It sets WAL, `synchronous=FULL`, foreign keys, +and a 5-second busy timeout. Schema version 1 consists of `records`, `events`, +and `request_keys`. `Open` rejects a future `PRAGMA user_version`; the Rust +port must preserve that fail-closed behavior and support a later explicit +read-only Go-state migration. See `internal/store/sqlite.go` and +`internal/store/sqlite_test.go`. + +## Decision + +- Use exact `rusqlite` 0.40.2 with default features disabled and only + `bundled` enabled. It provides typed parameters and errors, prepared SQL, + explicit transaction behavior, and SQLite's online-backup interface for a + later task. `bundled` uses the crate's bundled SQLite source rather than a + host-installed SQLite library. +- Keep database connections synchronous and owned by the store. Concurrency, + lock ownership, and write transaction semantics will be specified and tested + in P-023; do not add a pool or async runtime here. +- Use `PRAGMA user_version` as the compatibility version shared with current + Rover. Add a `schema_migrations` ledger with version, stable migration name, + SHA-256 of immutable SQL, and UTC application time. Never rewrite an applied + migration. +- On a legacy v1 database without the ledger, validate the known tables and + columns before inserting the ledger entry. Refuse unknown tables, malformed + schema, unversioned nonempty databases, mismatched ledger hashes, and + versions newer than the binary. +- `rover-store` now configures caller-supplied connections and provides + transactional record, event, mutation, idempotency, delete, and bounded list + APIs. Rooted Unix file operations, blob storage, and state-root admission + are implemented under P-024; database backup remains separate. JSON + stack-growth and typed/raw access contracts are recorded in + ADR-0005 and ADR-0006; their regression evidence is part of P-023. + +## Dependency and build contract + +The upstream crate metadata declares MIT. The SQLite amalgamation included by +its `bundled` feature is public domain according to rusqlite's bundling +documentation. Preserve rusqlite/libsqlite3-sys license files and the bundled +SQLite notice/source attribution found in the resolved crate archive. The +project's pinned Rust 1.88.0 toolchain is the build gate. Bundled SQLite +requires a C compiler. Do not advertise a supported target until native CI +build and migration tests pass for it. + +Only the `bundled` feature is enabled. Do not enable SQLCipher, loadable +extensions, wasm support, or optional integrations without a new review. No +upstream Herdr, Luvus, or Orca application source is copied. + +## Alternatives considered + +- System SQLite via rusqlite defaults: rejected for the Rust product because + the artifact would depend on host-installed SQLite and versions would vary. +- SQLx: rejected for this synchronous local database foundation because the + current store has no async database contract and a pool adds lifecycle and + configuration beyond the migration requirement. +- Hand-written SQLite FFI: rejected because it would recreate parameter + binding, row conversion, error, transaction, and platform-linking code + already maintained by rusqlite. + +## Acceptance evidence + +`rover-store` tests cover migration and transaction behavior, matching +idempotency across independent connections, event append, callback rollback, +payload limits, and connection reopen. CI checks the selected Rust MSRV. +Integration with a complete Rust state-root opener and Go-state import remains +pending. + +## Primary references + +- [rusqlite 0.40.2 manifest](https://github.com/rusqlite/rusqlite/blob/v0.40.2/Cargo.toml) +- [rusqlite 0.40.2 SQLite bundling and license guidance](https://github.com/rusqlite/rusqlite/tree/v0.40.2) +- [rusqlite 0.40.2 transactions](https://docs.rs/rusqlite/0.40.2/rusqlite/struct.Transaction.html) +- [SQLite copyright and public-domain notice](https://www.sqlite.org/copyright.html) diff --git a/docs/design/ADR-0005-rust-record-json.md b/docs/design/ADR-0005-rust-record-json.md new file mode 100644 index 0000000..68c3ee7 --- /dev/null +++ b/docs/design/ADR-0005-rust-record-json.md @@ -0,0 +1,59 @@ +# ADR-0005: JSON record encoding for Rust storage + +- Status: accepted for the P-023 storage API +- Date: 2026-09-25 +- Owners: Rover maintainers; implementation owner is `rover-store` + +## Context + +The Go record store accepts values, serializes each to JSON, rejects payloads +over 16 MiB, and stores the JSON text in `records.payload`. Mutations read a +record and atomically replace it with a new JSON value and an event. The Rust +store needs validated JSON values and byte-size enforcement without introducing +an unbounded parser or silently changing object order. + +## Decision + +- Use exact `serde_json` 1.0.151, with default features disabled and only + `std` and `unbounded_depth` enabled alongside the stack adapter in + ADR-0006. It provides the `Value` representation used by the current + store API. The package declares `MIT OR Apache-2.0`; Rust 1.88 exceeds the + release's declared Rust 1.71 minimum. +- Leave `preserve_order` disabled. `serde_json::Value` then stores object + members in sorted key order, matching Go's deterministic key ordering for + ordinary string-keyed JSON objects. +- Serialize and size-check before writing. Retain Go's 16 MiB maximum and HTML + escaping for `<`, `>`, `&`, U+2028, and U+2029. Store update plus optional + event append remains one SQLite transaction. +- Bound values to Go's 10,000 nested-container maximum on both write and read; + stack growth and safe destruction are specified in ADR-0006. +- Parse stored JSON when returning a stack-safe `RecordValue` or caller-selected + typed value; invalid or oversized + database payloads are errors. Reads never repair or rewrite malformed rows. +- Keep `preserve_order` and arbitrary precision numbers disabled. Typed reads + choose the caller's numeric representation; raw list and mutation APIs retain + the original JSON number tokens. Dynamic Rust `Value` does not coerce all + numbers to `float64` like Go's untyped map decoder. + +## Alternatives considered + +- Raw JSON text only: rejected because the current `Mutate` behavior parses, + transforms, and replaces a record as JSON data. +- Preserve source object key order: rejected because Go's encoder sorts map + keys and order is not semantically meaningful in JSON objects. +- Reject all deep JSON beyond the parser default: rejected because Go accepts + valid records with up to 10,000 nested containers. + +## Acceptance evidence + +Storage regression tests cover typed and raw access, JSON round trips, +deterministic escaping and encoding, Go's nesting bound, payload size, atomic +mutate/event behavior, and rollback/reopen recovery. Exact dependency license +files are checked from locked crate sources. Dynamic Rust `Value` retains its +native number representation; callers needing Go-style or typed numeric +semantics choose a target type or use the raw APIs. + +## Primary references + +- [serde_json 1.0.151 manifest and license/MSRV metadata](https://docs.rs/crate/serde_json/1.0.151/source/Cargo.toml) +- [serde_json 1.0.151 `Value` object ordering](https://docs.rs/serde_json/1.0.151/serde_json/enum.Value.html) diff --git a/docs/design/ADR-0006-stack-safe-json.md b/docs/design/ADR-0006-stack-safe-json.md new file mode 100644 index 0000000..79255c9 --- /dev/null +++ b/docs/design/ADR-0006-stack-safe-json.md @@ -0,0 +1,68 @@ +# ADR-0006: stack-safe deep JSON for Rust storage + +- Status: accepted for P-023 implementation +- Date: 2026-09-25 +- Owners: Rover maintainers; implementation owner is `rover-store` + +## Context + +Rover's Go `encoding/json` accepts JSON nesting through 10,000 levels. The +default `serde_json` recursion limit is 128, which would reject valid existing +records. Simply disabling that limit can overflow the native stack while +parsing, serializing, formatting, cloning, or dropping a deeply nested value. +The store needs an explicit depth bound matching Go plus stack-safe operations. + +## Decision + +- Keep exact `serde_json` 1.0.151 and enable its `unbounded_depth` feature only + together with exact `serde_stacker` 0.1.14. Use the adapter for both + deserialization and serialization; independently scan nesting and reject + beyond Go's 10,000-container bound before recursive serde work begins. +- Use the standard Serde `Serialize` and `DeserializeOwned` contracts for + typed records. Also provide raw JSON access for callers that need to preserve + lexical number representation or decode into their own target types. +- Return parsed dynamic JSON through a Rover wrapper with iterative destruction. + Avoid recursive `Debug`, `Clone`, and implicit conversion APIs on that + wrapper. Typed user-defined values retain their type's own drop behavior. +- Preserve Go's 16 MiB post-HTML-escape byte cap and deterministic key order. + Bound intermediate JSON serialization to 16 MiB because HTML escaping never + shrinks the serialized form. +- `serde_stacker` grows the call stack dynamically. Its `stacker` dependency + uses the `psm` assembly implementation where available; validate the project + host and do not claim runtime stack growth on unsupported targets. The + resolved lock uses `stacker` 0.1.25 and `psm` 0.1.32; PSM's build graph uses + `ar_archive_writer` 0.5.3 (`Apache-2.0 WITH LLVM-exception`). These packages + and their complete declared license files are included in the audited notice + bundle. A C compiler is required to build the stack-switching assembly. +- Keep typed numeric decoding type-directed. A Rust caller can request integer, + floating-point, or another declared target type; raw access preserves the + original JSON token. Do not claim that decoding a generic Go `map[string]any` + into Rust `serde_json::Value` has identical `float64` coercion semantics. + +## Alternatives considered + +- Retain the 128-level cap: rejected because it excludes valid persisted Go + records. +- Disable `serde_json` recursion checks without a stack adapter: rejected + because malformed or deeply nested stored JSON could overflow the process. +- Keep JSON as raw strings only: rejected as the only interface because + mutation and typed store reads need structured deserialization; raw methods + remain available for exact-token handling. + +## Acceptance evidence + +Rust regression tests encode, parse, inspect, and safely drop 10,000 nested +arrays and objects; reject level 10,001; check malformed input and the 16 MiB +encoded limit; and verify typed and raw numeric access. Go 1.26.6's pinned +`maxNestingDepth` source constant and a local `encoding/json` probe confirm the +same inclusive 10,000-container boundary. The audit copies and verifies each +locked license file byte-for-byte. + +## Primary references + +- [Go 1.26.6 JSON nesting limit](https://github.com/golang/go/blob/go1.26.6/src/encoding/json/scanner.go#L148) +- [`serde_json` recursion-limit API](https://docs.rs/serde_json/1.0.151/serde_json/struct.Deserializer.html#method.disable_recursion_limit) +- [`serde_stacker` 0.1.14 stack-safe Serde adapters](https://docs.rs/serde_stacker/0.1.14/serde_stacker/) +- [`serde_stacker` upstream source and dual license](https://github.com/dtolnay/serde-stacker) +- [`stacker` 0.1.25 dependency and platform notes](https://docs.rs/crate/stacker/0.1.25) +- [`psm` 0.1.32 target support and license](https://docs.rs/crate/psm/0.1.32) diff --git a/docs/design/ADR-0007-rust-rooted-file-operations.md b/docs/design/ADR-0007-rust-rooted-file-operations.md new file mode 100644 index 0000000..ec84b99 --- /dev/null +++ b/docs/design/ADR-0007-rust-rooted-file-operations.md @@ -0,0 +1,89 @@ +# ADR-0007: rooted file operations for Rust storage + +- Status: accepted; Unix implementation and focused macOS tests are in place +- Date: 2026-09-25 +- Owners: Rover maintainers; Go behavior owner is `internal/store`, Rust owner is `rover-store` + +## Context + +The Go store keeps content-addressed blobs below `objects/`, verifies SHA-256 +on read, refuses final-component symlinks, bounds reads to 16 MiB, writes +objects and state files through synced temporary files, and applies private +permissions. Path helpers also provide lexical root containment. See +`internal/store/sqlite.go` and `internal/store/sqlite_test.go`. + +The Rust store supports caller-supplied SQLite connections as well as an +explicit Unix `StateRoot` opener. Root admission and opened directory handles +must remain visible contracts; file operations must not accept arbitrary +absolute paths as if containment were established. + +## Decision + +- Add a directory-handle based file API. It opens a caller-selected directory + without following a symlink in its final component and anchors subsequent + operations to that handle. File names accepted by these APIs are single + normal path components; `.`/`..`, separators, and absolute paths are + rejected. The opened directory must have exact mode `0700`. `StateRoot::open` + creates/adopts a dedicated state path, rejects filesystem roots, home/current + directories, user symlink ancestors and unrelated nonempty directories, + creates private `objects`, `tasks`, and `checks` areas, and opens SQLite with + its no-follow flag. The Go-state inventory/import remains separate work. +- On Unix, use exact `rustix` filesystem wrappers for `openat`-style + operations, no-follow opens, exclusive temporary creation, link/rename, + metadata, permission changes, and `fsync`. No raw unsafe syscall code is + introduced in Rover. The reviewed crate version and complete license notice + are recorded in `RUST_DEPENDENCY_AUDIT.md`. +- Content-addressed writes use lowercase SHA-256 names, temporary files opened + exclusively with mode `0600`, file sync before publication, no-replace link + semantics, verification of an already-existing object, temporary cleanup, + and directory sync. Writes preserve Go's current lack of an input-size cap; + reads reject non-regular files, files larger than 16 MiB, and digest + mismatches. The separate state-root admission contract will define how + large-file creation is bounded by its callers. +- Atomic file replacement creates a same-directory temporary file, applies + the caller-selected mode to the open handle, writes and syncs it, renames it + over the target entry, and syncs the containing directory. Bounded reads + reject non-regular files and no-follow open the final component. Root + containment is a lexical path-component check; it is not represented as a + substitute for directory-handle anchoring. +- Implement and validate Unix behavior first, matching the existing Go + `O_NOFOLLOW` contract. Do not advertise file-store support on other targets + until a native implementation and tests exist; platform support is tracked + by P-060. +- Temporary names contain a process identifier and monotonic counter and are + created with exclusive semantics. Cleanup is best effort after an error; + successful publication always includes the required directory sync. + +## Alternatives considered + +- Path-based check-then-open: rejected because a symlink can be swapped between + validation and use. +- Raw libc/OS FFI in Rover: rejected because it adds unsafe code and + platform-specific syscall maintenance. +- A new filesystem abstraction that follows links: rejected because its + default path behavior would make the no-follow contract less explicit. +- An input-size cap on blob writes: rejected for this compatibility slice + because Go `Blob` currently accepts any slice size while `ReadBlob` enforces + 16 MiB. A future explicit schema/contract change may tighten this. + +## Acceptance evidence + +Focused Rust tests cover valid digest round-trip, duplicate concurrent +creation, digest mismatch, invalid digest, symlink and non-regular rejection, +16 MiB read boundary, containment including sibling-prefix confusion, atomic +replacement, exact file and directory modes, descriptor anchoring, state-root +reopen, rejection-before-mutation, symlink safety, and bounded-read behavior. +They pass on the current macOS/arm64 host. `cargo check --all-targets` also +type-checks `rover-store` for `x86_64-unknown-linux-gnu`; because the machine has +no Linux C toolchain, that check used the host SQLite pkg-config metadata and is +compile evidence only. Native Linux execution and wider target coverage remain +subject to P-060; Windows support is not advertised. + +## Primary references + +- `internal/store/sqlite.go`: `Blob`, `ReadBlob`, `AtomicFile`, `BoundedFile`, + `IsWithin`, and `PrivateDir` behavior. +- `internal/store/sqlite_test.go`: blob tamper, symlink/path, and permission + regression contracts. +- [`rustix` filesystem API](https://docs.rs/rustix/latest/rustix/fs/): safe + wrappers for descriptor-relative Unix filesystem operations. diff --git a/docs/design/ADR-0008-rust-project-identity.md b/docs/design/ADR-0008-rust-project-identity.md new file mode 100644 index 0000000..81b891b --- /dev/null +++ b/docs/design/ADR-0008-rust-project-identity.md @@ -0,0 +1,54 @@ +# ADR-0008: canonical repository identity and project scope + +- Status: accepted for P-025 implementation +- Date: 2026-09-25 +- Owners: Rover maintainers; Go behavior owner is `internal/access`, Rust owner is `rover-core` + +## Context + +Go derives `ProjectID` from SHA-256 of a canonical repository path and derives +the control audience from the canonical state-root path, a NUL separator, and +that repository path. Its path rule is exact: empty input remains empty; +otherwise `filepath.Abs` is followed by `filepath.EvalSymlinks` when that fully +resolves, and a cleaned absolute path is used if resolution fails. Grants must +not authenticate under a different project or state root. See +`internal/access/grants.go` and `internal/access/grants_test.go`. + +## Decision + +- Add a Rust `RepositoryIdentity` value in `rover-core` that stores the + canonical path and its `Sha256Digest` project ID. No new dependency is + needed. +- Preserve the Go resolution fallback exactly, including empty input and + unresolved paths beneath symlinked ancestors. Do not replace it with a + stricter “must exist” or “must canonicalize” rule; callers may identify a + repository before it exists. +- Add control-audience derivation using the canonical state-root bytes, + `NUL`, and canonical repository-path bytes, with the existing + `rover-control/v1:` prefix. This keeps grants bound to both project and + state store. +- Keep identity separate from authorization. A digest identifies a project; + it does not grant access. Grant issuance, expiry, tool allowlists, revocation, + and authentication remain owned by the access layer and must check both the + audience and project digest. +- Cross-project tests must prove that path spelling variants for one existing + repository coalesce, a symlink alias coalesces only when full resolution + succeeds, unresolved paths follow the Go fallback, and distinct projects or + state roots never share an audience. + +## Alternatives considered + +- Hash raw user input: rejected because trailing separators, dot segments, and + existing symlink aliases would split one filesystem project into several + identities. +- Require every repository path to exist: rejected because current Go accepts + unresolved paths and callers construct configs for future paths. +- Use project ID alone as audience: rejected because it would allow a bearer + token to cross state roots that happen to contain the same repository path. + +## Acceptance evidence + +Port the Go path rule and digest framing into Rust unit tests. Compare stable +SHA-256 results with the Go implementation's `model.Digest` behavior. Add grant +contract tests before marking P-025 complete; this ADR alone is not evidence of +cross-project isolation. diff --git a/docs/design/ADR-0009-rust-process-execution.md b/docs/design/ADR-0009-rust-process-execution.md new file mode 100644 index 0000000..6c7b084 --- /dev/null +++ b/docs/design/ADR-0009-rust-process-execution.md @@ -0,0 +1,68 @@ +# ADR-0009: Rust local process execution + +- Status: accepted for the P041 implementation slice +- Date: 2026-09-25 +- Owners: `rover-execution` domain; security model remains owned by Rover maintainers + +## Context + +Rover's current executor starts explicit argv without implicitly inserting a +shell, clears inherited environment except for a documented base and caller +allowlist, creates private HOME/temp/cache paths, bounds retained stdout and +stderr separately to 1 MiB while counting all bytes, and supports timeout and +caller cancellation. On Linux and macOS, timeout/cancellation kills the +executor's process group. This is local advisory execution under the current OS +user, not a sandbox. Existing Go behavior remains authoritative until Rust +acceptance coverage closes the contract. + +## Decision + +1. Add a `rover-execution` crate as the process execution domain owner. It uses + `std::process::Command`, passes argv directly, and clears the inherited + environment before adding the documented environment and explicit pass-env + names. +2. Use the existing audited `rustix` 1.1.4 dependency on Unix for safe process + group termination and descriptor-rooted private output files. Enable only + its already-reviewed `fs`, `std`, and `process` features. No new registry + package is introduced. +3. Represent user cancellation with a cloneable atomic cancellation token; + timeout and cancellation are checked while polling the child. Captured + output retains at most 1 MiB per stream, counts all observed bytes, records + truncation, hashes retained bytes, and persists exclusive mode-0600 logs. + Exit codes and process-error text preserve Go's exit/signal cases; executable + identity hashing reads at most the first 128 MiB, matching Go's bounded + prefix hash. +4. Do not report a local process as sandboxed. Restricted Docker (tracked in + P-100), PTY, detached supervision, and Windows process-tree termination + remain separate contracts and are not claimed by this crate slice. +5. Process-tree cleanup is implemented and tested for Linux/macOS first. + Unsupported targets must fail explicitly and advertise no process + cancellation guarantee until an OS-specific adapter and tests exist. + +## Alternatives considered + +- `std::process::Command::output`: rejected because it buffers unbounded output + and does not provide bounded live capture or cooperative cancellation. +- Shell-string execution: rejected because it changes argv interpretation and + inserts an implicit shell. +- Add a new async/process dependency: rejected for this synchronous contract; + the existing standard library plus audited `rustix` is sufficient on the + initial Unix targets. +- Claim Windows process-tree cancellation from `Child::kill`: rejected because + it only terminates the direct child and does not match Rover's timeout + cleanup semantics. + +## Consequences and acceptance + +This ADR does not claim process launch is shipped. P041 is complete only after +compatibility tests cover argv validation, clean environment/pass-env, process +results, timeout, cancellation including descendants, output bounds, durable +logs, and unsupported target behavior. The complete Go-to-Rust acceptance map +and platform matrix remain release gates. + +## References + +- [Rust parity plan](RUST_PARITY_PLAN.md) +- [Rust dependency audit](RUST_DEPENDENCY_AUDIT.md) +- [Security model](../SECURITY_MODEL.md) +- `internal/execution/process.go`, `process_unix.go`, and `process_test.go` diff --git a/docs/design/ADR-0010-rust-task-supervisor-state.md b/docs/design/ADR-0010-rust-task-supervisor-state.md new file mode 100644 index 0000000..834d092 --- /dev/null +++ b/docs/design/ADR-0010-rust-task-supervisor-state.md @@ -0,0 +1,81 @@ +# ADR-0010: Rust task supervisor state transitions + +- Status: accepted for the transactional lifecycle slice +- Date: 2026-09-25 +- Owners: `rover-execution` owns supervisor state transitions; `rover-store` owns atomic persistence + +## Context + +Rover's Go task runner records task claim, process ownership, cancellation +intent, periodic heartbeats, and `LOST` reconciliation in the existing task +record and event stream. A stale heartbeat does not prove the worker is gone. +PID reuse, terminal state changes, and a heartbeat racing with reconciliation +must not cause an active task to be marked `LOST` or restarted. + +## Decision + +1. Preserve the Go task JSON fields and `task.*` event names while mutating the + existing `task` record. The Rust supervisor layer does not introduce a + parallel task schema. +2. Claim is a one-way `QUEUED` transition. A pre-existing cancellation request + makes the task `CANCELLED` before process launch; any other prior status + rejects a second claim. +3. Heartbeats update `heartbeat` and `updated_at` without appending event rows. + Heartbeat reads and returns the persisted cancellation request so the worker + can stop its owned work. +4. Cancellation is durable intent and is rejected after a terminal status. +5. Reconciliation waits at least ten seconds after a valid heartbeat, then + requires the platform process adapter to prove the recorded PID is gone or + reused. It rechecks status, PID, and the exact observed heartbeat inside the + SQLite transaction before writing `LOST` and `task.lost`. This conditional + mutation prevents a concurrent heartbeat from being overwritten and avoids + a false event. Ambiguity never triggers restart; workspace and evidence are + retained. +6. `Store::mutate_with_event` is the shared primitive for conditional state + changes whose event depends on the record read in the same transaction. + It emits no event when the callback declines to make a transition. +7. Detached task launch uses an absolute executable and explicit argv, a private + exclusive `supervisor.log` in the persisted task workspace parent, null + stdin, a separate process group, and only PATH, HOME, and declared + `pass_env` values. An in-process reaper thread waits for the direct worker + while Rover remains alive. A second dispatch cannot replace the log or + silently start another worker. A post-spawn persistence failure returns the + PID and warns callers to inspect the task before retrying. +8. `HeartbeatGuard` owns the 250 ms heartbeat loop. It observes durable + cancellation intent and cancels the worker token on cancellation or store + failure; stopping the guard joins the thread and reports persistence errors. +9. Confirmed `LOST` transitions release leases owned by that task. Repeated + reconciliation also retries owner-scoped cleanup for already-LOST tasks, + covering an interrupted cleanup without taking another task's leases. + Normal task-finalization release and client reconnect remain open. This + slice does not provide CLI/TUI commands and is not advertised as shipped. + +## Dependency and license review + +No registry package or upstream source was added. `rover-execution` uses the +already pinned `serde_json` 1.0.151 and `time` 0.3.55 packages and their existing +MIT OR Apache-2.0 notices. The time feature set is the workspace-reviewed +`formatting`, `parsing`, and `std` set. License and notice checks remain part +of `make rust-check`. + +## Acceptance + +Tests cover one-way claim, malformed cancellation data failing closed, +pre-launch cancellation, durable cancellation, managed heartbeat cancellation +and error propagation, terminal-state protection, stale/fresh heartbeat +boundaries, ambiguous process presence, conditional `LOST` persistence, +confirmed-loss-only lease release, race-safe dispatch and event behavior, +private exclusive logs, duplicate launch refusal, child survival after launch +returns and after launcher process exit, child reaping, and spawn failure +recording. Nineteen supervisor tests, including the macOS PID-reuse ambiguity +test, pass on macOS. Linux identity and zombie test cases are +present and cross-compile, but a native Linux run remains required because the +current owned host is macOS; +cross-compilation is not runtime evidence. + +## References + +- [Rust parity plan](RUST_PARITY_PLAN.md) +- [Dependency and notice audit](RUST_DEPENDENCY_AUDIT.md) +- `internal/tasks/tasks.go`, `internal/tasks/identity_linux.go`, and `internal/tasks/identity_darwin.go` +- `crates/rover-store/src/lib.rs` diff --git a/docs/design/ADR-0011-rust-pty-backend.md b/docs/design/ADR-0011-rust-pty-backend.md new file mode 100644 index 0000000..144b0eb --- /dev/null +++ b/docs/design/ADR-0011-rust-pty-backend.md @@ -0,0 +1,53 @@ +# ADR-0011: Rust native PTY backend + +Status: accepted for the local PTY lifecycle slice; multi-client brokering, +session persistence, terminal emulation, and native Windows runtime validation +remain open. + +## Context + +The pinned Herdr and Orca feature inventories require interactive terminal +processes on supported hosts. Rover's Rust execution crate previously had no +PTY API. The product boundary remains a Rust CLI/TUI; this is not a GUI or a +remote terminal service. + +## Decision + +- Use `portable-pty` 0.9.0, pinned in `Cargo.lock`. Its MIT license and + transitive package licenses are included in `licenses/THIRD_PARTY_NOTICES.md` + and checked by `make rust-check`. +- Use the crate's native Unix PTY implementation and Windows ConPTY backend. + Rover's wrapper in `rover_execution::pty` exposes argv-only spawn, blocking + byte input/output, dimension resize, child PID observation, and explicit + close/reap. It clears the inherited environment, accepts only explicitly + supplied valid environment pairs, and requires an absolute executable and + existing absolute working directory. +- Reject zero or greater-than-1000 row/column dimensions. Pixel dimensions are + not yet part of Rover's interface. +- Dropping or closing a session terminates and reaps the direct child and + closes the PTY handles. This API does not claim to terminate arbitrary + descendants, sandbox the child, retain scrollback, reattach a client, or + persist a terminal across Rover restarts. +- Keep the third-party crate's unsafe platform code behind its audited API; + Rover's workspace continues to forbid unsafe Rust. + +## Evidence and remaining limits + +Four native macOS tests exercise validation, child output, input round-trip, +resize, and close/reap. The full workspace cross-compiles for Linux x86_64. +The isolated PTY module plus `portable-pty` dependency cross-compiles for +Windows x86_64; full-workspace Windows compilation is currently blocked by +the macOS host's missing MSVC `lib.exe` and Windows SDK headers required by +unrelated bundled C dependencies. These checks do not prove native runtime +behavior. Native Linux and Windows PTY tests remain required. Windows relies +on the operating-system ConPTY implementation and therefore requires a +Windows version that supports ConPTY. + +## References + +- [`portable-pty` 0.9.0 API](https://docs.rs/portable-pty/0.9.0/portable_pty/) +- [`PtySystem::openpty`](https://docs.rs/portable-pty/0.9.0/portable_pty/trait.PtySystem.html) +- [`MasterPty` read, write, and resize](https://docs.rs/portable-pty/0.9.0/portable_pty/trait.MasterPty.html) +- [`PtySize`](https://docs.rs/portable-pty/0.9.0/portable_pty/struct.PtySize.html) +- [Rust dependency and notice audit](RUST_DEPENDENCY_AUDIT.md) +- [Rust parity plan](RUST_PARITY_PLAN.md) diff --git a/docs/design/ADR-0012-rust-terminal-emulator.md b/docs/design/ADR-0012-rust-terminal-emulator.md new file mode 100644 index 0000000..1eb2976 --- /dev/null +++ b/docs/design/ADR-0012-rust-terminal-emulator.md @@ -0,0 +1,49 @@ +# ADR-0012: Rust terminal screen core + +Status: accepted for the bounded local screen-model slice. Rendering, interactive +input routing, durable sessions, and native Linux/Windows runtime behavior remain +open. + +## Context + +Rover needs to interpret untrusted PTY bytes before the TUI renders or exports +terminal content. Passing escape sequences straight through would let a child +process control the user's terminal. The model must also bound screen and history +memory, handle Unicode cell widths and resizing, and suppress clipboard or other +outward terminal actions. + +## Decision + +- Use `alacritty_terminal` 0.26.0, pinned in `Cargo.lock`, under its declared + Apache-2.0 license. Default features are disabled. Exact package notices and + the 107-package locked inventory are included in the Rust dependency audit. +- Keep the dependency behind `rover_execution::terminal::TerminalScreen`. Rover + does not copy terminal-emulator source from Herdr, Luvus, or Orca. +- Limit visible dimensions to 1,000 rows and 1,000 columns, with a 250,000-cell + viewport budget. Cap scrollback at 10,000 lines and 1,000,000 cells; reduce + the history line limit for wider viewports. Limit each parser call to 64 KiB. +- Disable OSC 52 clipboard handling. The screen model captures only terminal + bell events through a coalescing flag; all other outward terminal events are + discarded. The TUI relays a bell to the host terminal only while that pane is + unfocused. Expose filtered cell text only; replace control and bidirectional + formatting characters before the caller renders or exports it. +- Treat this as an emulator state model, not a renderer or complete terminal + application. Selection, copy/paste, key and mouse input, remote transport, + session persistence, and full terminal protocol compatibility are separate + work with their own acceptance contracts. + +## Evidence and remaining limits + +Eight focused native macOS unit tests cover bounded input and dimensions, +scrollback caps, resize trimming, wide-character resize and erase handling, +control-sequence suppression, styling, bidi filtering, and bell coalescing. +The TUI separately tests active-pane suppression. These tests establish +the current wrapper's behavior on this host; they are not a complete VT +conformance suite or security certification. Native Linux and Windows runtime +checks and integration with Rover's eventual renderer remain required. + +## References + +- [`alacritty_terminal` 0.26.0 API](https://docs.rs/alacritty_terminal/0.26.0/alacritty_terminal/) +- [Rust dependency and notice audit](RUST_DEPENDENCY_AUDIT.md) +- [Rust parity plan](RUST_PARITY_PLAN.md) diff --git a/docs/design/ADR-0013-rust-session-broker.md b/docs/design/ADR-0013-rust-session-broker.md new file mode 100644 index 0000000..07bce4f --- /dev/null +++ b/docs/design/ADR-0013-rust-session-broker.md @@ -0,0 +1,65 @@ +# ADR-0013: Rust local terminal session broker + +Status: accepted for the Unix server-owned session slice, stable named client +discovery, and initial CLI daemon integration. Windows server transport, +persisted restart metadata, and process-tree ownership remain open. + +## Context + +Herdr's local broker keeps a PTY process alive when its attached client detaches, +replays a bounded output tail after reconnect, rejects a second attached writer, +and ends the process when the server stops. Named sessions must be isolated by +namespace. Rover's Rust PTY wrapper previously owned its process only for the +lifetime of one client call. + +## Decision + +- Add `rover_execution::sessions::SessionServer` on Unix. The server process + owns each `PtySession`; a client disconnect releases its writer slot without + stopping the PTY. Explicit session stop or server drop closes/reaps the direct + child. Process-tree termination is not yet guaranteed. +- Key sessions by validated `(namespace, name)` tuples. A name may be reused in + another namespace, while replacement in the same namespace is refused. + Derive socket basenames from the first 128 bits of SHA-256 over the + namespace/NUL/name tuple, so a client in another process can resolve the + endpoint without exposing names or requiring the server's in-memory map. +- Require a private state directory, bind a Unix socket with mode 0600, refuse + stale socket takeover, and enforce the platform socket path limit. This is a + same-user local interface, not a remote or multi-user service. +- The initial `rover tui` launcher stores session sockets in a mode-0700 + per-user directory under `/tmp`, keyed by the admitted state-root path, to + stay within the Unix socket path limit. The canonical state path still owns + durable layout and database data; socket state is transient and stale + sockets are not removed or taken over automatically. +- Keep one writer attached per session. Input is capped at 16 KiB, terminal + dimensions at 1,000 by 1,000, each wire frame at 64 KiB, and replay history at + 32 KiB. A slow client is disconnected when output cannot drain within the + bounded write timeout; reconnect receives the retained tail. +- Encode byte payloads as standard base64 JSON, matching Go's `[]byte` frame + encoding. No Herdr, Luvus, or Orca implementation source is copied. +- Session registry metadata exists only while the server process is alive. + `SessionClient::connect_named` supports attach/reconnect by key while that + process remains alive. Reopening sessions after server restart and rendering + PTY bytes through Rover's terminal screen model are later contracts. Raw + session bytes must not be rendered as trusted terminal text without the + P-044 screen model. + +## Evidence and remaining limits + +Focused macOS tests cover namespace isolation and duplicate rejection, client +detach/reconnect by session key with continued process input, an actual child +process attaching by key without the server's in-memory map, one-writer +ownership, base64 wire compatibility, replay capacity, and input bounds. The +socket tests need permission to create/chmod local Unix-domain sockets in the +test sandbox; they pass on the native macOS host when run with that capability. +They do not establish Linux runtime behavior. The implementation is currently +Unix-only and has no Windows named-pipe server. + +## References + +- [Herdr PTY broker and attach implementation](../../internal/execution/pty_darwin.go) +- [Herdr interactive attach client](../../internal/execution/attach.go) +- [PTY backend decision](ADR-0011-rust-pty-backend.md) +- [Terminal screen core decision](ADR-0012-rust-terminal-emulator.md) +- [Rust dependency and notice audit](RUST_DEPENDENCY_AUDIT.md) +- [Rust parity plan](RUST_PARITY_PLAN.md) diff --git a/docs/design/ADR-0014-rust-workspace-layout-model.md b/docs/design/ADR-0014-rust-workspace-layout-model.md new file mode 100644 index 0000000..7f0ab52 --- /dev/null +++ b/docs/design/ADR-0014-rust-workspace-layout-model.md @@ -0,0 +1,53 @@ +# ADR-0014: Rust terminal workspace layout model + +Status: accepted for the serializable workspace state model and Unix private +state-file persistence API. Terminal UI framework selection, rendering, +keyboard controls, automatic launch restoration, and golden interaction tests +remain open. + +## Context + +Rover needs a Rust representation for Herdr-style workspaces, tabs, split +panes, zoom, popup overlays, and scratch terminals. The product boundary is +CLI/TUI only. Layout records must be bounded and validated before they can be +restored or used to resolve live sessions. + +## Decision + +- Define `rover_execution::layout::Workspace` as versioned JSON state with a + validated namespace and stable workspace, tab, split, and pane IDs. +- Keep ordered tabs and per-tab active focus, zoom, popup, title, and split + tree. Splits are binary and retain child order, axis, and a first-child ratio + in basis points. Closing a pane promotes its sibling subtree. +- A session pane stores a validated name and absolute working directory; + scratch panes have neither. A pane reference does not assert that the + session process exists. The session broker resolves live processes. +- Bound state to 128 tabs, 256 panes, 16 split levels, 256-byte titles, and + ratios strictly between 0 and 10,000 basis points. JSON input is capped at + 4 MiB and pre-scanned for a maximum nesting depth of 96 before recursive + deserialization. Deserialization then validates schema, IDs, duplicate + identifiers, tree shape, bounds, and focus/overlay references before + returning a workspace. +- On Unix, save through `SafeDir::atomic_write` with mode `0600`, and load via + no-follow bounded reads capped at 4 MiB plus one byte. The caller supplies an + already-admitted private directory; layout code does not choose a state path. +- `rover_tui::run_persistent` restores a valid saved layout or uses the supplied + initial workspace only when the file is absent, then saves after each key + event. Corrupt or unsafe state is reported to the caller. +- Keep this crate module independent of any selected TUI library. Do not treat + persistence primitives as automatic launch restoration or shipped UI. + +## Evidence and remaining limits + +Focused unit tests cover nested splitting and subtree collapse, stable IDs, +tab lifecycle, focus, resize, zoom/popup, invalid values, JSON round trips, +corrupt references, raw-size/depth bounds, atomic state-file round trips, exact +file mode, oversize input, malformed JSON, and symlink refusal. P-046 remains +open until a CLI launcher uses the persistent TUI entry point and +terminal-size-aware layout acceptance tests pass. No upstream source is copied. + +## References + +- [Rust parity plan](RUST_PARITY_PLAN.md) +- [Rust session broker](ADR-0013-rust-session-broker.md) +- [Terminal screen core](ADR-0012-rust-terminal-emulator.md) diff --git a/docs/design/ADR-0015-rust-tui-framework.md b/docs/design/ADR-0015-rust-tui-framework.md new file mode 100644 index 0000000..8d35ea0 --- /dev/null +++ b/docs/design/ADR-0015-rust-tui-framework.md @@ -0,0 +1,60 @@ +# ADR-0015: Rust TUI framework and terminal backend + +Status: accepted for the interactive workspace shell. + +## Context + +Rover's product boundary is CLI and TUI only. The Rust port needs a maintained +renderer with deterministic offscreen testing, a cross-platform terminal +backend, and explicit raw-mode restoration. It must render the validated +workspace model without treating session output as trusted screen text. + +## Decision + +- Use Ratatui 0.30.2 for layout and widget rendering. Its published MSRV is + Rust 1.88, matching Rover's pinned toolchain. Use its `TestBackend` for + deterministic frame tests. +- Use Crossterm 0.29.0 for input, resize, raw mode, and alternate-screen + handling. Ratatui 0.30.2 selects Crossterm 0.29 by default; pin the same + exact version directly so event types and terminal state are not duplicated. +- Disable default features on Rover's direct dependencies and enable Ratatui's + `crossterm` backend plus Crossterm's `events`, `bracketed-paste`, and + `windows` features. Ratatui's backend integration also activates Crossterm's + `derive-more` feature through its dependency graph. The resolved graph has a + single Crossterm 0.29.0 and does not enable its `osc52` clipboard feature or + Ratatui's calendar widget. +- Put presentation and input mapping in `rover-tui`, separate from the future + CLI parser and the session process owner. Restore raw mode, alternate screen, + cursor visibility, and bracketed paste through an RAII guard on normal return, + I/O error, or unwind. +- Render only validated workspace metadata in the current increment. PTY bytes + must pass through Rover's terminal screen model before any later screen pane + is rendered. + +## Alternatives + +- Direct Crossterm drawing would require Rover to build its own cell buffer, + split layout, clipping, diff, and offscreen test machinery. +- Termion is not an option for Rover's Windows support envelope. Ratatui also + documents Termwiz and Termina backends, but neither is selected because the + current PTY and terminal input paths already use Crossterm semantics. +- Cursive uses a different rendering model and adds a curses dependency; it is + not selected for this pane-oriented layout model. + +## Dependency and test contract + +Both selected crates declare MIT licenses; the locked dependency audit must +bundle and hash their exact license and author files and reject any unresolved +transitive SPDX expression. Keep the lockfile committed. Tests cover the +workspace render buffer, narrow dimensions, key-to-action behavior, terminal +state restoration, and one Crossterm version in the resolved graph. No live +terminal or provider result is inferred from `TestBackend`. + +## References + +- [Ratatui installation and MSRV](https://ratatui.rs/installation/) +- [Ratatui backend/version guidance](https://ratatui.rs/concepts/backends/) +- [Ratatui feature flags](https://ratatui.rs/installation/feature-flags/) +- [Crossterm 0.29.0 manifest and feature set](https://docs.rs/crate/crossterm/0.29.0/source/Cargo.toml.orig) +- [Ratatui TestBackend API](https://docs.rs/ratatui/0.30.2/ratatui/backend/struct.TestBackend.html) +- [Rust parity plan](RUST_PARITY_PLAN.md) diff --git a/docs/design/ADR-0016-rust-task-briefing-drafts.md b/docs/design/ADR-0016-rust-task-briefing-drafts.md new file mode 100644 index 0000000..2f28f15 --- /dev/null +++ b/docs/design/ADR-0016-rust-task-briefing-drafts.md @@ -0,0 +1,51 @@ +# ADR-0016: Repository-bound Rust task briefing drafts + +- Status: accepted for the local draft slice +- Date: 2026-09-26 +- Owners: `rover-tui` owns draft editing; `rover-cli` owns persistence + +## Context + +The Luvus orchestration form documents a task title, path globs, dependencies, +quality gate and multiline prompt. Rover's Rust binary currently has a PTY and +task supervisor, but it has no Rust task-contract dispatcher or agent capability +registry. A draft editor must not imply that saving a draft creates a queued +task, starts a worker, validates path leases, or runs the quality gate. + +## Decision + +1. A draft stores title, newline-separated path globs, newline-separated + dependency identifiers, quality-gate text, and a multiline prompt. +2. `Ctrl-B d` opens repository-local create/edit/delete. Saving is explicit; + deletion and discarding edits require confirmation. +3. Drafts remain inert local data. They are not converted to task records, + dispatched to a worker, interpreted as shell commands, or submitted to an + agent. The future task service must validate paths, dependency identities, + worker mode, agent capabilities and gate argv before launch. +4. The record is capped at 32 drafts; UTF-8 byte caps are 120 for title, 8 KiB + for paths, 4 KiB for dependencies, 1 KiB for quality-gate text, and 16 KiB + for prompt. Line breaks are allowed in the four multiline fields. Other + control characters and bidi format controls are refused. +5. Schema v1 (`title`, `prompt`) loads with empty values for the new fields. + Saves write schema v2 with all five fields. Repository path and digest-keyed + record identity must match the active canonical repository; malformed or + cross-project records fail closed. +6. `Manual`/`Now` start mode and agent choice are intentionally absent until + worker dispatch and the agent capability registry exist. They are not + represented by placeholder choices. + +## License and provenance + +No upstream source or asset is copied. The field set and interaction contract +are independently implemented from the documented Luvus orchestration form; +no upstream dependency or license notice is introduced by this slice. + +## Acceptance + +TUI tests cover create/edit/delete, multiline input, save and discard behavior, +and verify drafts do not create tasks. CLI tests cover schema v1 migration to +v2, round-trip equality, project binding, supported text, bidi/control +rejection, count limits and each field's UTF-8 byte limit. `make rust-check` +must pass. Full task creation, task state transitions, path leases, quality-gate +execution, `Manual`/`Now`, agent selection and worker dispatch remain in P-061, +P-071 through P-074. diff --git a/docs/design/ADR-0017-rust-task-brief-contract.md b/docs/design/ADR-0017-rust-task-brief-contract.md new file mode 100644 index 0000000..636e0c2 --- /dev/null +++ b/docs/design/ADR-0017-rust-task-brief-contract.md @@ -0,0 +1,62 @@ +# ADR-0017: Rust task brief and dependency graph contract + +- Status: accepted for validation and graph-planning foundation +- Date: 2026-09-26 +- Owner: `rover-core` owns portable task-domain validation; `rover-store` and + `rover-execution` continue to own persistence and process lifecycle. + +## Context + +The TUI persists repository-bound task briefing drafts with a title, path +globs, dependencies, quality-gate text, and prompt. Those drafts are editable +and may be incomplete. Rover's existing execution record and supervisor own a +separate lifecycle contract. Go's current task validator rejects nonempty +`depends_on` because its alpha scheduler does not implement dependency +scheduling. A Rust task feature must validate user input and graph structure +without silently claiming that text is executable or that a graph has been +scheduled. + +## Decision + +1. `rover_core::TaskBrief` is the execution-neutral form contract. Draft + persistence may hold incomplete values; task creation must call + `TaskBrief::validate`. +2. Titles are nonblank and capped at 120 bytes. Path globs are newline + separated, capped at 8 KiB and 128 entries, relative, traversal-free, and + limited to `*`, `**`, and `?` wildcard behavior. Dependencies are + newline-separated Rover IDs, capped at 4 KiB and 128 IDs; duplicates and + invalid IDs fail validation. Quality-gate text remains opaque and capped + at 1 KiB. Prompts are nonblank and capped at 16 KiB. Controls other than + line feed in multiline fields and bidi formatting characters are rejected. +3. `validate_task_graph` resolves a proposed in-memory graph against the task + set supplied by its caller. Missing IDs, duplicate task/dependency IDs, + self-dependencies, cycles, and graphs above 10,000 nodes fail closed. A + valid graph returns deterministic lexical tie-broken topological order. +4. Validation is not scheduling or dispatch. This ADR does not add task-record + persistence, readiness transitions, worker execution, retry history, path + leases, or quality-gate execution. Those remain explicit P-071 through + P-074 implementation work. The existing Go-compatible supervisor schema + remains unchanged. +5. Quality-gate text is not parsed, invoked, or represented as a verified + check. Future gate execution requires its own typed argv/timeout/output + contract and acceptance tests. + +## Compatibility and OSS provenance + +The brief mirrors the fields in the current Rover TUI draft. Relative glob +constraints follow Rover's documented existing config path safety rules. +Graph validation supplies a Rust capability missing from the current Go +alpha; it is a Rover implementation, not a claim about current Go behavior. +No Herdr, Luvus, or Orca source or assets were copied. Serde was already a +locked Rover workspace dependency and its existing license bundle remains +covered by `make rust-check`'s lockfile notice audit. + +## Acceptance evidence + +`rover-core` regression tests cover valid globs, traversal/absolute/unsupported +patterns, duplicate and malformed dependency IDs, stable topological order, +missing and self dependencies, duplicate task/dependency IDs, and cycles. +The focused task tests passed three consecutive runs. The full +`make rust-check CARGO='cargo +1.88.0'` gate passed, including clippy, all Rust +workspace tests, dependency audit tests, and dependency license checks. Native +Linux execution remains a separate platform requirement. diff --git a/docs/design/ADR-0018-rust-task-plan-storage.md b/docs/design/ADR-0018-rust-task-plan-storage.md new file mode 100644 index 0000000..0d0aa36 --- /dev/null +++ b/docs/design/ADR-0018-rust-task-plan-storage.md @@ -0,0 +1,53 @@ +# ADR-0018: Rust project-bound task-plan storage + +- Status: accepted for durable planning and manual lifecycle tracking +- Date: 2026-09-26 +- Owner: `rover-tasks` owns task-plan behavior; `rover-store` owns atomic + record/event persistence; `rover-execution` owns runnable process records. + +## Context + +Rover's saved task briefings need a durable destination and dependency state. +The existing `task` record is a Go-compatible runnable execution record and +must not be filled with incomplete argv/agent settings just to make a TUI +draft appear executable. A task plan should preserve intent independently +until Rover can construct and validate an actual worker launch contract. + +## Decision + +1. Store plans as `task_plan` records using schema `rover/task-plan/v1`. Every + record includes the canonical repository path and project digest; list and + get operations enforce both values. +2. Create validates the `TaskBrief`, requires every dependency to exist in the + same project, rejects malformed stored graphs, and stores the record and + creation event through `Store::create_once`. +3. Status is `READY` when every prerequisite succeeded, otherwise `WAITING`. + Readiness is derived from current durable states, so a waiting plan becomes + startable after prerequisites succeed without relying on a stale cached + flag. Failed or cancelled prerequisites report `BLOCKED` readiness. +4. Manual start, retry, and finish transitions are durable and append events. + The attempt history has a ten-attempt bound. Manual outcome notes are + stored with `authority: manual`; they are user assertions, never provider, + process, test, or quality-gate evidence. +5. This service does not launch workers, execute quality gates, mutate source + worktrees, or choose an agent. The TUI can explicitly convert a saved draft + into a plan and list plans; it does not expose start/retry/finish actions or + imply that any worker ran. P-071/P-072 and the execution-backed TUI task + workflow remain open. Capabilities are not advertised as running or + dispatched by this service. + +## Compatibility and OSS provenance + +The plan record uses Rover's generic SQLite record/event store without +changing its schema migration version or Go-compatible `task` records. Serde, +serde_json, rusqlite, and Rover store/core are already reviewed workspace +dependencies. No upstream code or assets were copied. Their existing locked +license and bundled-notice audit remains authoritative. + +## Acceptance + +`rover-tasks` tests cover project isolation, task creation, missing dependency +rejection, waiting/readiness changes, prerequisite failure/cancellation, +manual lifecycle events, retries, and the ten-attempt bound. Full workspace +clippy, tests, dependency auditing, and license verification are required by +`make rust-check CARGO='cargo +1.88.0'`. Native Linux execution remains open. diff --git a/docs/design/ADR-0019-rust-agent-profile-registry.md b/docs/design/ADR-0019-rust-agent-profile-registry.md new file mode 100644 index 0000000..ca127a5 --- /dev/null +++ b/docs/design/ADR-0019-rust-agent-profile-registry.md @@ -0,0 +1,46 @@ +# ADR-0019: Rust agent profile registry + +Status: accepted for the declarative profile, invocation-builder, and read-only +listing slice. Process launch, provider protocol parsing, discovery, permission +mediation, native session recovery, and live-provider validation remain +separate work. + +## Context + +Rover has four declared Go profiles: generic headless, generic PTY, Codex CLI, +and Claude CLI. Listing a profile must describe the integration surface without +implying provider availability, live conformance, or permission mediation. +Unknown names must fail closed instead of falling back to a generic process. + +## Decision + +- Add `rover-agents` as a domain crate containing the four Go-compatible + descriptors and strict profile lookup. +- Preserve the Go validation labels verbatim, including its Linux PTY + qualification and live-account testing requirement for native CLIs. +- Port Go's exact generic objective substitution and native Codex/Claude argv + templates. Keep prompts after `--`, validate executable/model/tool values, + and never select Claude's bypass permission mode. +- Keep every descriptor's permission mediation false; the registry does not + add any provider execution or permission-control behavior. +- Expose read-only profile listing and exact descriptor lookup through the Rust + `agents` and `agent capabilities` CLI commands. The command does not launch + providers or inspect credentials. +- Do not add third-party dependencies beyond workspace `serde`, already present + in the audited lockfile. + +## Verification and limits + +Crate tests cover the four descriptors, disclosure values, unknown-profile +failure, generic objective substitution, native argv, prompt argument +separation, and malformed option rejection. CLI tests cover JSON shapes, +schema envelope, successful lookup, and unknown lookup. Bounded native +transcript parsing is tracked separately in ADR-0020. Process discovery, live +validation, and user-selectable TUI worker dispatch remain in +P-062/P-070/P-072. + +## References + +- [Rust parity plan](RUST_PARITY_PLAN.md) +- [Upstream feature matrix](UPSTREAM_FEATURE_MATRIX.md) +- Go reference owner: `internal/agents/agents.go` diff --git a/docs/design/ADR-0020-native-agent-transcripts.md b/docs/design/ADR-0020-native-agent-transcripts.md new file mode 100644 index 0000000..a660772 --- /dev/null +++ b/docs/design/ADR-0020-native-agent-transcripts.md @@ -0,0 +1,45 @@ +# ADR-0020: Native agent transcript parsing + +Status: accepted for bounded Codex/Claude JSONL fixture parsing. Live provider +conformance and process integration remain open. + +## Context + +Codex and Claude CLI adapters emit structured JSONL with session IDs, terminal +events, provider-authored content, and optional usage. Corrupt, conflicting, or +incomplete streams must not look successful. Provider output remains an +unverified provider claim, never observed Rover verification evidence. + +## Decision + +- Parse only `codex-exec` and `claude-print`; unknown adapters retain the Go + compatibility behavior of returning an empty, provenance-labelled result. + Profile selection separately fails closed before dispatch. +- Bound transcripts and individual event lines to 1 MiB and captured claims to + 256. Ignore blank lines, count unknown event types, and accept only exact + byte-identical duplicates carrying the same nonempty UUID. +- Require Codex thread identity before terminal completion and stable identity + through its events. Require Claude session identity and a boolean + `is_error` on its terminal result. +- Reject malformed JSON, missing terminal results, identity changes, + conflicting event IDs, and any nonduplicate event after terminal status. +- Retain provider usage only for nonnegative signed 64-bit integer fields and + retain nonnegative provider-reported Claude cost as an estimate. +- Preserve the source label `provider-emitted claims/usage; not independent + verification` on results. +- Use already-locked `serde_json`; no new third-party package is introduced. + +## Verification and limits + +Fixture tests cover both adapters' success/failure, identity, duplicate event, +malformed JSON, event ordering, size and claim bounds, usage/cost filtering, +and provenance. They do not establish live provider compatibility. Rust rejects +invalid UTF-8 JSON bytes; the Go JSON decoder may replace invalid bytes inside +JSON strings, so that malformed-input edge is intentionally stricter and must +not be treated as fixture conformance evidence. + +## References + +- [Rust parity plan](RUST_PARITY_PLAN.md) +- [Agent profile registry](ADR-0019-rust-agent-profile-registry.md) +- Go reference owner: `internal/agents/agents.go` diff --git a/docs/design/ADR-0021-agent-detection-evidence.md b/docs/design/ADR-0021-agent-detection-evidence.md new file mode 100644 index 0000000..fb418f4 --- /dev/null +++ b/docs/design/ADR-0021-agent-detection-evidence.md @@ -0,0 +1,60 @@ +# ADR-0021: Agent detection from explicit process and screen evidence + +Status: accepted for the bounded library contract and owned-PTY sampling slice. +Bundled manifests, lifecycle authority, and complete Herdr matcher parity +remain open. + +## Evidence and scope + +Herdr's current [agent documentation](https://herdr.dev/docs/agents/) says it +identifies the foreground process first, then evaluates a live bottom-buffer +snapshot with detection manifests. It describes blocked as a match against a +known visible approval/question/permission prompt; a known agent with no screen +rule match falls back to idle. This Rust slice implements that documented +boundary independently; it does not claim source-level equivalence to the +pinned Herdr commit. The local upstream repository is a partial clone whose +detection blobs were unavailable when inspected offline, so exact matcher +syntax and bundled per-agent patterns have not been independently verified. + +## Decision + +- Accept platform-supplied foreground executable identity and a live screen + snapshot; do not infer identity from arbitrary substrings, child-process + guesses, or historical/scrollback text. +- Built-in identity mappings are restricted to declared native profiles whose + executable names are known (`codex` and `claude`). Other process names need a + valid local manifest with exact basename aliases. +- Screen manifests use bounded literal substring rules with explicit + priorities. Same-priority rules that disagree produce `unknown`; lower + priority matches cannot override the highest priority. This is intentionally + not a port of Herdr's full TOML matcher language. +- A state fallback to `idle` requires both a recognized process and a valid + manifest plus a supplied live screen with no matching rule. Missing process, + screen, or manifest, invalid screen controls, and ambiguous mappings remain + `unknown`. +- Return confidence, stable reason codes, process basename, manifest source and + version, and rule IDs. Never copy raw screen contents into explain evidence. +- Bound screen input to 64 KiB; manifests to 128, process names to 32 per + manifest, rules to 256, and literal markers to 256 bytes each. Reject control + and bidirectional formatting characters in metadata and markers. +- On Unix, sample only the foreground process-group leader reported by the + Rover-owned PTY. Do not enumerate child processes or infer a wrapper's agent. + Linux binds `/proc//exe` to the kernel boot ID, PID, and process start + time before and after reading it. macOS makes two bounded `/bin/ps` queries + for start time, process group, and command; mismatches, command failures, + oversized output, and unsupported platforms produce no sample. +- The local session protocol accepts `sample_foreground` only from its active + attached writer and returns `foreground_sample` with a basename or empty + evidence. The attached TUI evaluates process identity but has no bundled + screen manifests yet, so recognized agents display with state `unknown`. + Empty samples also remain unknown. + +## Verification and limits + +Fixtures cover process basenames and Windows `.exe` normalization, unknown and +ambiguous identity, explicit screen matches, priority/conflict handling, +documented idle fallback, absent evidence, hostile controls, metadata +validation, bounds, safe explanation output, and request/reply over an owned +PTY session. Native sampling has been exercised on macOS; Linux sampling still +requires a Linux verification run. No full agent manifest set, lifecycle +hooks, wrapper/child detection, or complete Herdr matcher port is included. diff --git a/docs/design/ADR-0022-agent-state-rollups.md b/docs/design/ADR-0022-agent-state-rollups.md new file mode 100644 index 0000000..37a72b0 --- /dev/null +++ b/docs/design/ADR-0022-agent-state-rollups.md @@ -0,0 +1,38 @@ +# ADR-0022: Agent state rollups + +Status: accepted for the Rust CLI/TUI implementation + +## Context + +Herdr documents pane, tab, and workspace status rollups where blocked takes +priority, working indicates active work, and an unviewed completion remains +visible as done. Luvus documents blocked, working, done, and idle states, with +completion derived separately from current activity. Rover also needs to show +stale observations without presenting them as current state. + +## Decision + +`rover-agents` provides pure pane, tab, and workspace aggregation functions. +Inputs are explicit snapshots with Unix-millisecond observation times and an +unviewed-completion bit owned by the event/view state layer. Freshness is +calculated with a caller-supplied positive window; future timestamps are +treated as age zero to tolerate wall-clock skew. + +For fresh observations, state precedence is blocked, working, done, idle, then +unknown. A completion is done only when an idle observation explicitly carries +`done_unseen`; viewing is the responsibility of the caller, which clears that +bit. Stale observations are excluded from precedence and counted. The rollup +state is stale only when every observation in scope is stale; otherwise the +fresh state is shown and stale counts remain available. Empty scopes are +unknown. Duplicate pane snapshots and invalid completion/state combinations +are rejected. + +## Consequences and limits + +This is aggregation logic, not lifecycle authority, event persistence, native +sampling, or TUI presentation. No duration, status, or completion is inferred +from silence. The caller must choose a freshness window and supply consistent +timestamps. The implementation intentionally does not copy upstream source; +the behavior is based on documented user-visible semantics. See the [Herdr +agent status documentation](https://herdr.dev/docs/agents/) and [Luvus agent +guide](https://luvus.dev/docs/guides/agents/). diff --git a/docs/design/ADR-0023-agent-lifecycle-authority.md b/docs/design/ADR-0023-agent-lifecycle-authority.md new file mode 100644 index 0000000..1766183 --- /dev/null +++ b/docs/design/ADR-0023-agent-lifecycle-authority.md @@ -0,0 +1,38 @@ +# ADR-0023: Sequenced agent lifecycle authority + +Status: accepted for the Rust CLI/TUI implementation + +## Context + +Herdr documents source-scoped lifecycle reports, optional monotonically +increasing sequence numbers, ignored reports whose sequence is not newer, and +explicit authority release when an agent exits. While lifecycle authority is +active, Herdr does not also use screen-manifest fallback. Luvus documents +hook-reported lifecycle events for supported agents while retaining screen +detection as the fallback. + +## Decision + +`rover-agents::LifecycleAuthority` is a bounded in-memory registry owned by a +single pane. Rover requires a sequence on every report and release so delayed +events cannot restore state after release. The last sequence remains recorded +after release. Active lifecycle reports for idle, working, and blocked +override screen estimates. Releasing the source makes screen fallback +available again. + +Rover accepts at most 32 distinct sources per registry lifetime. If multiple +sources are simultaneously active, the state is unknown and screen detection +remains suppressed; Rover does not compare independent source sequences or +choose an arbitrary winner. Completion/unread state remains owned by the +separate rollup/event-view layer. + +## Consequences and limits + +This implements only local state ordering and authority resolution. It does +not install hooks, send socket messages, persist events, bind session identity, +or wire the result into a TUI. The mandatory sequence and source cap are Rover +contract choices for bounded, deterministic behavior. The multi-source +conflict result is an explicit fail-closed policy. See [Herdr integration +guidance](https://herdr.dev/docs/integrations/), +[Herdr socket API](https://herdr.dev/docs/socket-api/), and +[Luvus agent guide](https://luvus.dev/docs/guides/agents/). diff --git a/docs/design/ADR-0024-agent-session-identity-and-resume.md b/docs/design/ADR-0024-agent-session-identity-and-resume.md new file mode 100644 index 0000000..92414a5 --- /dev/null +++ b/docs/design/ADR-0024-agent-session-identity-and-resume.md @@ -0,0 +1,48 @@ +# ADR-0024: Exact agent session identity and explicit resume + +Status: accepted for the bounded Rust session-binding slice + +## Context + +Herdr documents native session references from integrations and opt-in +automatic restart restore, gated by valid supported references and a +configuration setting. Luvus documents exact session IDs where discovery by +project directory is unsafe, and resumes through provider-native commands. +The current Rover agent registry only has reviewed Codex and Claude CLI +invocation profiles. Transcript identities are provider-emitted claims, not +independent verification. + +## Decision + +The Rust session binding stores an exact ID, adapter, evidence origin, +canonical project identity, repository path, and pane ID in Rover's local +record store. It supports only codex-exec and claude-print. Loading verifies +record schema, project digest/path, pane key, adapter, and bounded ID +characters. Resume argv is built only when a caller explicitly requests it +with the same adapter. The builder preserves the existing read-only or +workspace-write Codex sandbox and Claude permission mode. A caller can +explicitly clear a pane's binding; deletion is recorded in the event store. + +Rover does not scan provider databases, select latest sessions, infer a +binding from directory similarity, or restart an agent automatically. +Unrecognized providers and path-valued session references fail closed. This +keeps uncertain ownership out of the persisted pane binding. + +## Consequences and limits + +The attached TUI offers the active pane's binding through Ctrl-B then `a`. +It displays the exact session identity and requires a second confirmation +before sending the provider's interactive resume command to Rover's persistent +project shell. The user can also confirm removal of the Rover binding; this +does not stop an already-running agent. Shell arguments are quoted as separate +POSIX shell words, and control characters are rejected. The provider still +applies its own permission and sandbox configuration; Rover does not mediate +those settings. This is not an installed hook, automatic restore scheduler, +duplicate-session detector, or live provider test. The pinned upstream source snapshots are partial clones; +their session-resume blobs could not be retrieved because network access to +the promisor remote was unavailable. No upstream source was copied. Behavioral +references are the current published [Herdr session restore +documentation](https://herdr.dev/docs/session-state/), [Luvus session resume +guide](https://luvus.dev/docs/guides/agents/), [Codex CLI resume +examples](https://github.com/openai/codex/issues/14343), and [Claude CLI +reference](https://docs.anthropic.com/en/docs/claude-code/cli-usage). diff --git a/docs/design/ADR-0025-agent-session-inventory-cli.md b/docs/design/ADR-0025-agent-session-inventory-cli.md new file mode 100644 index 0000000..8579981 --- /dev/null +++ b/docs/design/ADR-0025-agent-session-inventory-cli.md @@ -0,0 +1,46 @@ +# ADR-0025: Project-scoped agent session inventory + +Status: accepted for the stored-identity inventory slice + +## Context + +Herdr and Luvus list live agents and expose status, read, prompt, wait, and +attach operations. Rover stores exact Codex/Claude session identities for +explicitly bound project panes. The attached TUI now receives a foreground +process sample from its owned PTY, but the generic CLI inventory does not yet +join those observations and lifecycle-event transport is not present. +Treating a saved identity as a live agent or reporting its state as +idle/working would invent evidence. + +## Decision + +- Add `rover agent session bind --repo PATH --pane ID --adapter ADAPTER + --id EXACT_ID [--state PATH]` to bind a user-selected exact ID to a pane + present in that project's persisted Rover workspace. The pane may be the + literal `active`, which resolves to the workspace's current active pane. + This records `ExplicitUser` provenance and never scans provider databases. +- Add `rover agent session list --repo PATH [--state PATH]` to list only exact + stored bindings for the selected canonical repository. +- Add `rover agent session status --repo PATH --pane ID [--state PATH]` to + retrieve one project's exact pane binding. +- Sort by stable pane ID. Refuse malformed matching records and collections + larger than the store query bound rather than returning partial data. +- Return `binding_status: bound` separately from `agent_state: unknown`, with a + reason that no live process or lifecycle observation is available. +- Add `rover agent session clear --repo PATH --pane ID [--state PATH]` to remove + only Rover's identity association. The command does not stop an agent. +- Do not expose prompt, wait, read, or attach as generic agent operations yet. + The attached TUI's explicit interactive resume flow remains a provider + session operation, not proof of live-agent discovery. + +## Consequences and limits + +The CLI gives users and scripts explicit bind/clear operations plus an exact, +project-scoped inventory of persisted session identities without reading +provider databases or claiming that those sessions are running. The store +query is capped at 1,000 session records across the state root, so an oversized +global collection fails closed. Attached TUI process identity is a separate +live observation and its state remains unknown. Live list/status, +prompt, wait, terminal read, and direct attach still require authority-aware +lifecycle transport and pane I/O contracts. See P-062, P-064, and P-067 in +`RUST_PARITY_PLAN.md` and the sampling contract in ADR-0026. diff --git a/docs/design/ADR-0026-owned-pty-process-sampling.md b/docs/design/ADR-0026-owned-pty-process-sampling.md new file mode 100644 index 0000000..510c511 --- /dev/null +++ b/docs/design/ADR-0026-owned-pty-process-sampling.md @@ -0,0 +1,53 @@ +# ADR-0026: Sampling foreground processes from owned PTYs + +Status: accepted for Unix attached-session identity display + +## Context + +P-062 needs live foreground-process evidence for agent identity. Sampling an +arbitrary PID supplied by a UI client would cross the session boundary and +could inspect a process unrelated to the attached pane. A saved session ID +also does not prove that the session is currently running. + +## Decision + +- The session server reads the foreground process-group leader from the master + PTY it owns. It does not accept a PID from the client. It samples executable + basenames for members of that foreground group; this does not enumerate + descendants in other groups or infer wrappers from argv. +- Linux reads `/proc//exe` between two process identity checks. The + identity is bound to boot ID, PID, and `/proc//stat` start time. +- macOS runs `/bin/ps` directly with a numeric PID, a cleared environment, + fixed `LC_ALL=C`, no shell, null stdin/stderr, and bounded stdout. Two + observations must agree on process start time, process group, and command; + the reported group must be the sampled PTY foreground leader. +- Linux enumerates numeric `/proc` entries with a 4096-entry scan limit and a + 64-distinct-name limit. It binds each selected process to `/proc` start time + and verifies the group again after reading `/proc//exe`. macOS uses a + cleared-environment `/bin/ps -A` listing capped at 256 KiB, then requires two + matching start-time/group/command observations for each selected PID. Both + samplers return no group list when a bound is exceeded or selected evidence + is unstable. Unsupported systems, denied inspection, malformed output, and + oversized output produce an empty sample. Empty means unknown; it does not + mean idle or stopped. +- Only the currently attached single writer may request a sample. The reply + uses the existing bounded JSON/base64 session frame. Its existing `data` + field remains the group leader basename; an additive `processes` field + carries distinct validated group-member basenames, never PIDs or full + command lines. +- The TUI evaluates group member names against the bounded Herdr-compatible + manifest matcher and visible screen evidence. A recognized group leader has + priority; conflicting recognized non-leader identities remain unknown. A + known manifest identity may be detected; this sampler does not confer + lifecycle authority. + +## Verification and limits + +Regression tests cover the owned PTY sampler, session request/reply framing, +foreground group detection, leader priority, and ambiguous non-leader +identity. These PTY tests passed on the current macOS host; Linux runtime +verification remains pending. This does not provide cross-session process +inventory, wrapper argv inference, Letta-specific filtering, wait/prompt/read/ +attach operations, or lifecycle authority. See ADR-0021, ADR-0025, and +P-062/P-067/P-167 in +`RUST_PARITY_PLAN.md`. diff --git a/docs/design/ADR-0027-herdr-compatible-screen-manifests.md b/docs/design/ADR-0027-herdr-compatible-screen-manifests.md new file mode 100644 index 0000000..9644d23 --- /dev/null +++ b/docs/design/ADR-0027-herdr-compatible-screen-manifests.md @@ -0,0 +1,61 @@ +# ADR-0027: Herdr-compatible screen manifest evaluation + +Status: accepted for bounded bundled manifest parsing and screen-rule evaluation + +## Context + +The pinned Herdr revision `c411883ec639c9893ed9c33021c890485e91727b` provides +22 bundled TOML screen manifests and a matcher language with nested gates, +regex, line-regex, region selection, priorities, OSC title/progress fields, +visible-state flags, and a skip-state-update flag. Rover's existing literal +matcher cannot execute these rules. The source is Apache-2.0; exact manifest +digests and required attribution are recorded in +`UPSTREAM_SOURCE_AUDIT.md` before those data files are bundled. + +## Decision + +- Parse the pinned TOML format using exact `toml 0.8.23`, with unknown fields + rejected, source bytes capped at 64 KiB per manifest, at most 128 rules, + gate depth 8, 512 gates, 32 matchers per gate, 1,024 total matchers, and + 512 Unicode scalar values per matcher. +- Compile regular expressions with exact `regex 1.12.4`. Apply a bounded + regex automaton size, screen snapshots capped at 64 KiB, and fixed rule/gate + counts. This engine uses a bounded, non-backtracking regex implementation. +- Evaluate `contains`, `regex`, and `line_regex` as conjunctions within each + gate; `all` is conjunctive, a nonempty `any` is disjunctive, and a matching + `not` gate rejects its parent gate. Recursion only evaluates already + validated, bounded manifests. +- Preserve upstream rule ordering: the highest priority matching rule wins; + the first matching rule wins a priority tie. Preserve its region selectors, + visible flags, `skip_state_update`, and Codex-specific unknown fallback. +- Evaluate only a live visible-screen snapshot and explicitly collected OSC + title/progress values. Do not use scrollback, arbitrary child guesses, or + shell history as detection evidence. +- Keep remote update, user override, hot-reload, and integration installation + outside this slice. Bundled manifests are immutable data from the audited + commit; no background network fetch is introduced. + +## Dependency review + +- `regex 1.12.4` declares `MIT OR Apache-2.0`, Rust 1.65, and uses linear-time + matching with bounded pattern/screen sizes. It supplies the manifest's + regex/line-regex operators without an ad hoc or backtracking engine. +- `toml 0.8.23` declares `MIT OR Apache-2.0`, Rust 1.66, and supplies strict + TOML-to-Serde decoding. Parsing is isolated behind input and nesting bounds. +- Both crates are already cached locally, are compatible with Rover's Rust + 1.88 toolchain, and have their resolved transitive notices captured by + `make rust-notices` and verified by `make rust-check`. +- A custom TOML parser or regex engine was rejected because it would increase + parser correctness and denial-of-service risk while failing to match the + upstream rule language. + +## Verification and limits + +Regression tests cover every gate operator and region selector, rule +priority/tie behavior, invalid TOML/regex, complexity bounds, all 22 pinned +manifests, Codex fallback, OSC regions, skip-state behavior, and unsafe +evidence. The attached TUI now supplies the live viewport, OSC title, and +bounded OSC 9;4 progress payloads to the evaluator for audited direct +executable mappings. Full behavior still requires lifecycle-authority +integration, expanded process-group and wrapper recognition, and the remaining +CLI/TUI agent surfaces. See P-062 and P-166/P-167 in `RUST_PARITY_PLAN.md`. diff --git a/docs/design/GO_RUST_STATE_MIGRATION.md b/docs/design/GO_RUST_STATE_MIGRATION.md new file mode 100644 index 0000000..2deaccc --- /dev/null +++ b/docs/design/GO_RUST_STATE_MIGRATION.md @@ -0,0 +1,58 @@ +# Go to Rust State Import and Rollback + +The Rust migration importer is currently a store API, not a CLI command or a +runtime cutover. The Go CLI/TUI remains authoritative. Importing creates a +separate Rust state root and does not edit, rename, or delete the Go source +root. + +## Before import + +1. Stop Rover and every worker that can write to the Go state root. The + `source_quiesced` option is an operator assertion; the importer cannot prove + that no external writer exists. +2. Make a separate filesystem backup of the Go state root and record its + location. Keep the original root in place and writable by the current Go + version. +3. Run `LegacyStateInventory::inspect` and retain its report. Resolve every + database or object blocker before proceeding. +4. Review the listed task/check runtime trees and unmanaged root entries. The + importer excludes those files. Set `accept_excluded_runtime_data` only after + deciding that their continued retention in the original root and omission + from the Rust destination are acceptable. +5. Choose a distinct, private destination path. The importer rejects source + and destination paths that overlap and refuses unrelated populated + destinations. + +## Import and verification + +Call `LegacyStateInventory::import_to` with both explicit options set only +after the preceding review. Keep the returned receipt with the inventory +report. A repeated call for an unchanged source and destination verifies the +receipt, imported records, events, request keys, and content-addressed objects +and returns `already_imported`. It refuses a changed source snapshot or altered +destination data. + +The source remains the rollback copy. Runtime files under `tasks/` and +`checks/`, plus unmanaged root entries, remain only in that original source; +the importer does not copy them. Grants are revoked in the destination, leases +are released, and task/workflow process state is removed or marked `LOST` so +that import cannot resume an old process automatically. + +## Rollback + +1. Stop every Rust process using the destination state root. +2. Restore the application's state-root configuration to the original Go + state path. Do not point Go at the Rust destination. +3. Start the Go Rover version that owns the source schema and confirm it opens + the original state. Do not run Go and Rust writers against the same root. +4. Keep the Rust destination and migration receipt for investigation until the + operator decides whether to archive or remove them. The importer performs + no automatic rollback deletion. + +If verification fails, do not cut over. Preserve both roots and the error +details. Since import does not modify the source, a retry can use a new, +separate destination after the cause is resolved; do not overwrite an unrelated +or partially modified destination. + +This procedure documents state handling only. A future product cutover still +needs an explicit CLI/TUI workflow, compatibility policy, and release gate. diff --git a/docs/design/RUST_COMPATIBILITY_BASELINE.md b/docs/design/RUST_COMPATIBILITY_BASELINE.md new file mode 100644 index 0000000..0109cea --- /dev/null +++ b/docs/design/RUST_COMPATIBILITY_BASELINE.md @@ -0,0 +1,125 @@ +# Rover compatibility baseline for Rust + +Date: 2026-09-25. The Go implementation remains the production source of truth. +The Rust core has begun implementation; this index does not claim CLI, TUI, +storage, or feature parity. + +## Public CLI command families + +Preserve the command spellings and aliases below until Rust golden-contract +tests establish an approved change. The detailed flags and examples are in +[docs/CLI.md](../CLI.md); dispatch is in `internal/cli/cli.go` and +`internal/cli/advanced.go`. + +| Family | Commands / aliases | +|---|---| +| Setup and diagnostics | `version`, `doctor`, `init`, `help` | +| Inspect and verify | `inspect`, `verify`, `check` | +| Tasks and sessions | `task run`, `do`, `status`/`st`, `logs`/`lg`, `cancel`, `ui`, `tui`, `attach`, `limits`, `agent list`, `agent capabilities` | +| Workflows | `workflow`/`wf` (`run`, `status`, `cancel`) | +| Review and records | `report`/`rep`, `review`, `outcome`, `events`, `export`, `publish` | +| Source and assurance | `replay`, `diff`, `prove regression`, `mutate` | +| Context and instructions | `context search|bundle`, `memory put|list|delete`, `integrate` | +| Protocol and access | `mcp`, `serve`, `grant create|list|revoke` | +| Remote CLI | `remote node-add|node-list|node-delete|tools|call` | +| Maintenance and evidence | `backup`, `backup-check`, `restore`, `attest keygen|sign|verify` | +| Learning | `learn recommend|dataset|evaluate|promote|show|revoke` | + +The current CLI also accepts `__workflow` and `__worker` internal dispatch +commands. They are implementation details, not stable user commands; audit any +Rust equivalents against supervisor launch behavior before removing them. + +## Serialized contracts + +| Contract | Current source of truth | Compatibility requirement | +|---|---|---| +| Common Rover records | `model.Schema` (`rover/v1alpha1`), `docs/API.md` | Preserve field names/types and explicit unknown/error semantics; version intentional changes. | +| Project check configuration | `schemas/config.schema.json`, `internal/config` | Strict JSON; reject unknown/duplicate fields, multiple documents, malformed/oversized/deep input. | +| Task request | `schemas/task.schema.json`, `internal/model`, `docs/CLI.md` | Preserve argv, timeouts, attempts, reservations, interactive/session and agent-option semantics. | +| Workflow request | `schemas/workflow.schema.json`, `internal/workflow`, `docs/CLI.md` | Preserve graph validation, idempotency, dependencies, bounded parallelism, frozen config and conservative integration. | +| Counterfactual request | `schemas/regression.schema.json` | Preserve exact base/candidate and named-test semantics; ambiguity stays inconclusive. | +| Mutation request | `schemas/mutation.schema.json` | Preserve finite, uniquely matched mutations and explicit per-mutant outcomes. | +| MCP | `docs/CLI.md`, `internal/mcp`, `internal/service` | Current documented protocol is MCP 2025-11-25; stdio line framing and JSON HTTP subset, no SSE/OAuth/browser CORS claim. | +| Acceptance inventory | `docs/acceptance/implementation-map.json` | Preserve A01-A40 IDs and honest per-platform scope until each has a Rust mapping. | + +## Current Rust coverage + +`crates/rover-core` currently implements the `rover/v1alpha1` marker, validated +IDs, fallible legacy-compatible ID generation, typed SHA-256 digests, and typed +UTC timestamps with RFC3339Nano-compatible formatting. Its 13 unit tests cover +ID boundaries, digest vectors and validation, timestamp normalization, and +fraction trimming. The crate does not yet serialize Rover records or expose +the existing Go CLI commands. See [ADR-0003](ADR-0003-core-identifiers-digests-and-time.md) +and [the parity plan](RUST_PARITY_PLAN.md). + +## Current package disposition + +This is the initial disposition for Rover-owned packages. "Port" means preserve +observable contracts in Rust, not translate every Go file line for line. +Implementation starts only after each domain's dependency/security contract is +recorded and its acceptance owner is known. + +| Current Go package/path | Planned disposition | +|---|---| +| `cmd/rover`, `internal/cli` | Port public CLI/TUI entry points and JSON/exit behavior; retain old binary during parity period. | +| `internal/model`, `internal/wire` | Port typed records, IDs/digests, strict wire parsing and versioned serialized contracts. | +| `internal/store`, `internal/archive` | Port transactional persistence, artifacts, budget/reservations, backup and restore with migrations. | +| `internal/source` | Port safe Git capture, immutable snapshots, diffs, candidate integration and path restrictions. | +| `internal/execution`, `internal/terminal`, `internal/tasks` | Port bounded process/PTY/session supervision, cancellation, task lifecycle and recovery. | +| `internal/agents`, `internal/integration` | Port declared adapters, event handling, native session metadata and reversible integration management. | +| `internal/workflow` | Port DAG validation, idempotency, concurrency, handoffs, repair bounds and final integration verification. | +| `internal/assurance` | Port strict check interpretation, replay, counterfactuals, mutation, decision semantics and evidence binding. | +| `internal/access`, `internal/service`, `internal/mcp` | Port local/API authority, grants, project boundaries, MCP contracts and remote CLI protocol. | +| `internal/config`, `schemas/` | Preserve strict config/task/workflow/regression/mutation schemas; version reviewed changes. | +| `internal/contextstore` | Port bounded snapshot search/bundles and expiring scoped notes. | +| `internal/attestation` | Port explicit-key signatures and verification; retain current limitations. | +| `internal/learning` | Port bounded evaluation/proposal behavior only with preserved dataset separation and explicit promotion. | +| `internal/install`, `internal/publish` | Re-evaluate as CLI diagnostics/local advisory output; do not imply signed installer or protected CI. | +| `internal/testutil` | Replace with Rust fixture builders and deterministic owned workers; not shipped as runtime. | +| `sdk/python`, `sdk/typescript`, `sdk/go`, `sdk/README.md` | Removed by explicit user direction; historical results remain dated evidence. | +| `scripts/demo.py`, `scripts/demo_extended.py` | Port to deterministic Rust CLI integration/demo harnesses; retain scripts only until equivalent coverage runs. | +| `.github/workflows`, `Makefile`, dependency manifests | Keep the existing Go runtime gates while adapting them for Rust CI, license, SBOM, and release gates; SDK targets have been removed. | +| Current docs and acceptance records | Update to Rust only as each capability is verified; retain dated Go results as historical. | + +This disposition does not mark any Rust port complete. + +## Exit and decision codes + +Documented verification exit codes are 0 accepted under supplied policy, 1 +required check failed, 2 error/inconclusive, and 3 checks satisfied but human +review remains required. Read/report commands return their own operation status; +callers must inspect the serialized `decision`, not infer acceptance from a +zero exit alone. Rust contract tests must capture command-specific errors, +cancellation, malformed input, and JSON output, not only the four verification +codes. + +## Security and data invariants to port + +- Local execution is not a sandbox and carries the host user's authority. +- A failed, missing, malformed, truncated, or zero-test required result cannot + silently count as a pass; an empty verification plan is inconclusive. +- Verification binds retained source/config/tool identities; candidate edits do + not silently replace baseline checks. +- Ambiguous worker loss is `LOST`; do not blindly re-submit side effects. +- Grants are scoped/expiring/revocable; secrets are not serialized into logs or + repository task files. HTTP non-loopback requires TLS and browser Origins are + rejected. +- Paths, object hashes, resource limits, SQLite transactions, backup integrity, + and private file permissions remain enforced. +- Human review, provider assertion, command completion, and evidence acceptance + are separate facts. + +## Acceptance baseline + +`docs/acceptance/implementation-map.json` contains A01-A40. Its rows and test +references were mechanically checked on 2026-09-25: all 40 IDs occur exactly +once and every referenced path exists. Existing Go tests are evidence about the +current Go implementation, not proof that a future Rust port meets those rows. +Rust work must create a per-row Rust test mapping before Go removal. + +## Validation boundary + +This document was checked against `README.md`, `STATUS.md`, `docs/CLI.md`, +`docs/API.md`, `docs/ARCHITECTURE.md`, `docs/SECURITY_MODEL.md`, the five JSON +schemas, `internal/cli` dispatch, and the acceptance map. The check is an +inventory, not a test run or a claim of Rust parity. diff --git a/docs/design/RUST_DEPENDENCY_AUDIT.md b/docs/design/RUST_DEPENDENCY_AUDIT.md new file mode 100644 index 0000000..0198d73 --- /dev/null +++ b/docs/design/RUST_DEPENDENCY_AUDIT.md @@ -0,0 +1,216 @@ +# Rust dependency snapshot + +Audit date: 2026-09-25. This snapshot covers the current Rust workspace, +including the `rover-store` transactional JSON record adapter and its bundled +SQLite amalgamation. It is not a complete Rover product SBOM while Go remains the +published CLI/TUI implementation, and it is not a vulnerability audit. The +locked package license/author files are bundled in +[`licenses/THIRD_PARTY_NOTICES.md`](../../licenses/THIRD_PARTY_NOTICES.md), but +the bundle is byte-checked by `scripts/rust_dependency_audit.py`. CI now pins +the Rust 1.88.0 MSRV toolchain for the workspace gates. `time` 0.3.55 declares +this minimum Rust version; the `rusqlite` release was compiled and tested with +the same pinned toolchain. The stack-safe JSON adapter also uses `psm` and +`ar_archive_writer`, which declare Rust 1.88.0 and are covered by this pin. + +## Direct dependencies + +| Crate | Locked version | License expression | Use | +|---|---:|---|---| +| `alacritty_terminal` | 0.26.0 | Apache-2.0 | VT terminal state machine and bounded scrollback grid; default features disabled. OSC 52 clipboard handling and outward events are disabled in Rover. ADR-0012 records the security limits. | +| `base64` | 0.22.1 | MIT OR Apache-2.0 | Standard base64 encoding for the local terminal socket frames; matches Go `[]byte` JSON wire format. Default features disabled. | +| `getrandom` | 0.4.3 | MIT OR Apache-2.0 | OS-backed cryptographic entropy for IDs; no `wasm_js` feature. | +| `portable-pty` | 0.9.0 | MIT | Native Unix PTY and Windows ConPTY create/read/write/resize/close backend; dependency contract and scope are in ADR-0011. | +| `regex` | 1.12.4 | MIT OR Apache-2.0 | Bounded, non-backtracking Herdr-compatible screen-manifest regex and line-regex matching; default Unicode/performance features. ADR-0027 bounds input, matcher sizes, and automaton size. | +| `rusqlite` | 0.40.2 | MIT | Synchronous SQLite connection, schema migration, and record APIs; default features disabled, only `bundled` enabled. | +| `rustix` | 1.1.4 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | Safe Unix descriptor-relative file operations in `rover-store` and process-group termination in `rover-execution`; default features disabled, only `fs`, `process`, and `std` enabled. | +| `serde` | 1.0.229 | MIT OR Apache-2.0 | Typed record and session-frame serialization/deserialization contracts; default features disabled, `std` and `derive` enabled. | +| `serde_json` | 1.0.151 | MIT OR Apache-2.0 | JSON parsing/encoding; only `std` and `unbounded_depth` enabled, with `serde_stacker` and an explicit 10,000-level limit. `rover-execution` uses the same locked package for transactional task record transitions. | +| `toml` | 0.8.23 | MIT OR Apache-2.0 | Strict bounded deserialization of audited Herdr detection manifests; default features disabled, `parse` only. ADR-0027 records why a general parser is required. | +| `serde_stacker` | 0.1.14 | MIT OR Apache-2.0 | Dynamically growing stack adapter for deep typed JSON operations. | +| `sha2` | 0.11.0 | MIT OR Apache-2.0 | SHA-256 implementation; pure Rust. | +| `time` | 0.3.55 | MIT OR Apache-2.0 | UTC clock and RFC3339 formatting/parsing for task heartbeat reconciliation; only `formatting`, `parsing`, and `std` enabled. | + +## Locked package license metadata + +`cargo metadata --format-version 1 --locked --offline` reports 163 registry +packages, all with a declared license field, plus six local workspace crates. +Ten crates use legacy `MIT/Apache-2.0` or `Apache-2.0/MIT` labels; their +license alternatives are normalized to SPDX `OR` expressions. The terminal emulator adds 38 locked registry packages, including target-specific +Windows support packages. The PTY backend previously added 19 packages, including target-specific Windows ConPTY +dependencies. Two small Windows GNU import-library packages omit license files +from their crate archives; the audit takes the exact MIT and Apache license +texts from the `winapi` crate at the same upstream repository and matching +SPDX expression, then records and byte-checks those bundled files for each +locked package. Cargo metadata includes target-specific and optional packages; +the active normal/build dependency graphs are in the verification record +below. + +The Ratatui/Crossterm TUI dependency set adds the renderer, widget, and event +backend packages. Its reviewed SPDX expressions include `MIT OR Apache-2.0 OR +CC0-1.0` (`fast-srgb8`), `Zlib` (`foldhash`), and `Apache-2.0 OR BSL-1.0` +(`ryu`). The exact CC0, Zlib, Apache, and Boost Software License files are +retained in the per-package bundle; `BSL-1.0` here is the SPDX identifier for +Boost Software License 1.0. Crossterm's OSC 52 feature is disabled in Rover. + +| Package | Version | License expression | +|---|---:|---| +| `aho-corasick` | 1.1.5 | Unlicense OR MIT | +| `alacritty_terminal` | 0.26.0 | Apache-2.0 | +| `anyhow` | 1.0.104 | MIT OR Apache-2.0 | +| `ar_archive_writer` | 0.5.3 | Apache-2.0 WITH LLVM-exception | +| `arrayvec` | 0.7.8 | MIT OR Apache-2.0 | +| `atomic-waker` | 1.1.2 | Apache-2.0 OR MIT | +| `base64` | 0.22.1 | MIT OR Apache-2.0 | +| `bitflags` | 1.3.2 | MIT OR Apache-2.0 | +| `bitflags` | 2.13.2 | MIT OR Apache-2.0 | +| `block-buffer` | 0.12.1 | MIT OR Apache-2.0 | +| `cc` | 1.4.7 | MIT OR Apache-2.0 | +| `cfg-if` | 1.0.5 | MIT OR Apache-2.0 | +| `cfg_aliases` | 0.1.1 | MIT | +| `concurrent-queue` | 2.5.0 | Apache-2.0 OR MIT | +| `const-oid` | 0.10.2 | Apache-2.0 OR MIT | +| `cpufeatures` | 0.3.1 | MIT OR Apache-2.0 | +| `crossbeam-utils` | 0.8.23 | MIT OR Apache-2.0 | +| `crypto-common` | 0.2.2 | MIT OR Apache-2.0 | +| `cursor-icon` | 1.2.0 | MIT OR Apache-2.0 OR Zlib | +| `deranged` | 0.5.8 | MIT OR Apache-2.0 | +| `digest` | 0.11.3 | MIT OR Apache-2.0 | +| `downcast-rs` | 1.2.1 | MIT OR Apache-2.0 | +| `errno` | 0.3.14 | MIT OR Apache-2.0 | +| `fallible-iterator` | 0.3.0 | MIT OR Apache-2.0 | +| `fallible-streaming-iterator` | 0.1.9 | MIT OR Apache-2.0 | +| `fastrand` | 2.5.0 | Apache-2.0 OR MIT | +| `filedescriptor` | 0.8.3 | MIT | +| `find-msvc-tools` | 0.1.13 | MIT OR Apache-2.0 | +| `futures-io` | 0.3.34 | MIT OR Apache-2.0 | +| `getrandom` | 0.4.3 | MIT OR Apache-2.0 | +| `hermit-abi` | 0.5.3 | MIT OR Apache-2.0 | +| `home` | 0.5.12 | MIT OR Apache-2.0 | +| `hybrid-array` | 0.4.15 | MIT OR Apache-2.0 | +| `itoa` | 1.0.18 | MIT OR Apache-2.0 | +| `lazy_static` | 1.5.0 | MIT OR Apache-2.0 | +| `libc` | 0.2.189 | MIT OR Apache-2.0 | +| `libsqlite3-sys` | 0.38.2 | MIT | +| `linux-raw-sys` | 0.12.1 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | +| `lock_api` | 0.4.14 | MIT OR Apache-2.0 | +| `log` | 0.4.34 | MIT OR Apache-2.0 | +| `memchr` | 2.8.3 | Unlicense OR MIT | +| `miow` | 0.6.1 | MIT OR Apache-2.0 | +| `nix` | 0.28.0 | MIT | +| `num-conv` | 0.2.2 | MIT OR Apache-2.0 | +| `object` | 0.39.1 | Apache-2.0 OR MIT | +| `parking_lot` | 0.12.5 | MIT OR Apache-2.0 | +| `parking_lot_core` | 0.9.12 | MIT OR Apache-2.0 | +| `pin-project-lite` | 0.2.17 | Apache-2.0 OR MIT | +| `piper` | 0.2.5 | MIT OR Apache-2.0 | +| `pkg-config` | 0.3.34 | MIT OR Apache-2.0 | +| `polling` | 3.11.0 | Apache-2.0 OR MIT | +| `portable-pty` | 0.9.0 | MIT | +| `powerfmt` | 0.2.0 | MIT OR Apache-2.0 | +| `proc-macro2` | 1.0.107 | MIT OR Apache-2.0 | +| `psm` | 0.1.32 | MIT OR Apache-2.0 | +| `quote` | 1.0.47 | MIT OR Apache-2.0 | +| `r-efi` | 6.0.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | +| `redox_syscall` | 0.5.18 | MIT | +| `regex-automata` | 0.4.18 | MIT OR Apache-2.0 | +| `regex-syntax` | 0.8.11 | MIT OR Apache-2.0 | +| `rusqlite` | 0.40.2 | MIT | +| `rustix` | 1.1.4 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | +| `rustix-openpty` | 0.2.0 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | +| `scopeguard` | 1.2.0 | MIT OR Apache-2.0 | +| `serde` | 1.0.229 | MIT OR Apache-2.0 | +| `serde_core` | 1.0.229 | MIT OR Apache-2.0 | +| `serde_derive` | 1.0.229 | MIT OR Apache-2.0 | +| `serde_json` | 1.0.151 | MIT OR Apache-2.0 | +| `serde_stacker` | 0.1.14 | MIT OR Apache-2.0 | +| `serial2` | 0.2.38 | BSD-2-Clause OR Apache-2.0 | +| `sha2` | 0.11.0 | MIT OR Apache-2.0 | +| `shared_library` | 0.1.9 | Apache-2.0 OR MIT | +| `shell-words` | 1.1.1 | MIT OR Apache-2.0 | +| `shlex` | 2.0.1 | MIT OR Apache-2.0 | +| `signal-hook` | 0.4.4 | MIT OR Apache-2.0 | +| `signal-hook-registry` | 1.4.8 | MIT OR Apache-2.0 | +| `smallvec` | 1.16.1 | MIT OR Apache-2.0 | +| `stacker` | 0.1.25 | MIT OR Apache-2.0 | +| `syn` | 2.0.119 | MIT OR Apache-2.0 | +| `syn` | 3.0.6 | MIT OR Apache-2.0 | +| `thiserror` | 1.0.69 | MIT OR Apache-2.0 | +| `thiserror-impl` | 1.0.69 | MIT OR Apache-2.0 | +| `time` | 0.3.55 | MIT OR Apache-2.0 | +| `time-core` | 0.1.9 | MIT OR Apache-2.0 | +| `time-macros` | 0.2.32 | MIT OR Apache-2.0 | +| `typenum` | 1.20.1 | MIT OR Apache-2.0 | +| `unicode-ident` | 1.0.26 | (MIT OR Apache-2.0) AND Unicode-3.0 | +| `unicode-width` | 0.2.2 | MIT OR Apache-2.0 | +| `vcpkg` | 0.2.15 | MIT OR Apache-2.0 | +| `vte` | 0.15.0 | Apache-2.0 OR MIT | +| `winapi` | 0.3.9 | MIT OR Apache-2.0 | +| `winapi-i686-pc-windows-gnu` | 0.4.0 | MIT OR Apache-2.0 | +| `winapi-x86_64-pc-windows-gnu` | 0.4.0 | MIT OR Apache-2.0 | +| `windows-link` | 0.2.1 | MIT OR Apache-2.0 | +| `windows-sys` | 0.59.0 | MIT OR Apache-2.0 | +| `windows-sys` | 0.61.2 | MIT OR Apache-2.0 | +| `windows-targets` | 0.52.6 | MIT OR Apache-2.0 | +| `windows_aarch64_gnullvm` | 0.52.6 | MIT OR Apache-2.0 | +| `windows_aarch64_msvc` | 0.52.6 | MIT OR Apache-2.0 | +| `windows_i686_gnu` | 0.52.6 | MIT OR Apache-2.0 | +| `windows_i686_gnullvm` | 0.52.6 | MIT OR Apache-2.0 | +| `windows_i686_msvc` | 0.52.6 | MIT OR Apache-2.0 | +| `windows_x86_64_gnu` | 0.52.6 | MIT OR Apache-2.0 | +| `windows_x86_64_gnullvm` | 0.52.6 | MIT OR Apache-2.0 | +| `windows_x86_64_msvc` | 0.52.6 | MIT OR Apache-2.0 | +| `winreg` | 0.10.1 | MIT | +| `zmij` | 1.0.23 | MIT | + +The locked `r-efi` package is a target-specific `getrandom` dependency for UEFI, +not part of Rover's terminal OS targets. Where it is distributed, the declared +license expression permits MIT or Apache-2.0 as alternatives to LGPL-2.1-or- +later. `unicode-ident` declares Unicode-3.0 in addition to its MIT/Apache +choice. Preserve the notices for the selected licenses in any binary/source +release. The rusqlite `bundled` feature compiles SQLite 3.53.2 from the +`libsqlite3-sys` crate into the binary; its exact public-domain blessing is +retained at `licenses/third-party/libsqlite3-sys-0.38.2/SQLITE_BLESSING.txt`. +The SQLite amalgamation is an indirect bundled dependency, not copied into the +Rover source tree. No Herdr, Luvus, or Orca application source is copied. + +## Verification and remaining gates + +- `cargo tree --locked --offline --target -e normal,build` checks + Linux, macOS, and Windows x86_64 dependency edges. These are inventory + targets, not a claim that Rover has passed native build/behavior tests on all + three operating systems. +- Cargo metadata returned no packages with a missing declared license field. +- All 163 locked registry packages have declared licenses; 304 license/author + files and the bundled SQLite blessing are included with SHA-256 records. + `make rust-notices` refreshes files from resolved crate sources, or from a + same-repository matching-license fallback when a target-only archive omits + them; `make rust-check` verifies the bundled bytes. +- The dependency set was fetched from crates.io and compiled from the lockfile; + SHA-256 known-answer and timestamp compatibility tests pass. +- `make rust-check` runs the unit tests for the audit tool, verifies all locked + package license expressions against an explicit allowlist, compares every + bundled license/author file byte-for-byte with the locked crate source, and + checks `cargo tree` for the configured Linux, macOS, and Windows x86_64 target + triples. It also verifies the lockfile/metadata package set matches. +- `make rust-sbom` emits CycloneDX 1.7 JSON with lockfile checksums, normalized + SPDX expressions, the package inventory, embedded SQLite source checksum, + and dependency relationships for the configured target triples. SBOM generation uses the official + [CycloneDX 1.7 JSON schema](https://cyclonedx.org/schema/bom-1.7.schema.json) + identifier; this script does not yet download or execute JSON Schema + validation. +- P-019 remains open because no Rust vulnerability scanner is wired yet. + Hosted CI has not run for this change, so the pinned 1.88.0 result is pending + hosted confirmation. + +## Primary references + +- [`sha2` 0.11.0](https://docs.rs/sha2/0.11.0/sha2/) +- [`getrandom` 0.4.3](https://docs.rs/getrandom/0.4.3/getrandom/) +- [`time` 0.3.55](https://docs.rs/time/0.3.55/time/) +- [`r-efi` 6.0.0 license metadata](https://docs.rs/crate/r-efi/6.0.0) +- [`rusqlite` 0.40.2](https://docs.rs/rusqlite/0.40.2/rusqlite/) +- [`rustix` 1.1.4](https://docs.rs/rustix/1.1.4/rustix/) +- [`rusqlite` 0.40.2 SQLite bundling and licensing](https://github.com/rusqlite/rusqlite/tree/v0.40.2) +- [`serde_stacker` 0.1.14](https://docs.rs/serde_stacker/0.1.14/serde_stacker/) +- [`stacker` 0.1.25](https://docs.rs/crate/stacker/0.1.25) +- [SQLite public-domain notice](https://www.sqlite.org/copyright.html) diff --git a/docs/design/RUST_PARITY_PLAN.md b/docs/design/RUST_PARITY_PLAN.md new file mode 100644 index 0000000..6b94175 --- /dev/null +++ b/docs/design/RUST_PARITY_PLAN.md @@ -0,0 +1,290 @@ +# Rover Rust Port and Feature Parity Plan + +Status: implementation in progress. Initial plan date: 2026-09-24; source pins updated 2026-09-25. + +## User-approved direction + +- Rebuild Rover in Rust and keep product interfaces to CLI and TUI. +- Remove the Python, TypeScript, and Go SDKs. +- Bring the published feature sets of Herdr, Luvus, and Orca ADE into Rover. +- Preserve existing Rover behavior unless a specific removal is recorded. +- User clarified: port all behavior into Rust CLI/TUI; reuse upstream source only when an exact-path audit permits it. + +## Scope and conflicts to resolve + +Target capability parity through CLI/TUI, not pixel-for-pixel reproduction of desktop interaction. Drag/drop maps to a path picker or explicit file command; browser design selection maps to DOM inspection, selector targeting, and retained evidence; mobile companion semantics map to remote CLI/TUI access. These equivalents count only after acceptance tests pass. No separate desktop, web, or mobile UI is included under the chosen product surfaces. + +Orca ADE contains substantial Electron/React/TypeScript, mobile, and cloud code. These are behavior references to port into Rust CLI/TUI, not additional Rover applications. No upstream source is copied before path-level license and attribution audit. + +At the pinned revisions in `UPSTREAM_SOURCE_AUDIT.md`, Herdr is Apache-2.0, Luvus README and root LICENSE both state Apache-2.0, and Orca ADE root LICENSE is MIT. Rover is currently MIT. Luvus's earlier indexed README/metadata conflict is resolved for that exact pinned commit only. Each nested license, notice, asset and dependency still requires an import-specific audit. This is an engineering plan, not legal advice. + +## Primary source inventory + +Pinned commit IDs, root license/README hashes, and detected nested license-file hashes are recorded in `UPSTREAM_SOURCE_AUDIT.md`. Behavior implementation may proceed on mapped contracts; each upstream code/asset reuse remains gated by its exact-file audit. + +- Herdr: https://github.com/herdrdev/herdr ; https://herdr.dev/docs/quick-start/ ; https://herdr.dev/docs/agents/ ; https://herdr.dev/docs/integrations/ ; https://herdr.dev/docs/persistence-remote/ ; https://herdr.dev/docs/connecting-machines/ ; https://herdr.dev/docs/configuration/ ; https://herdr.dev/docs/socket-api/ +- Luvus: https://github.com/RizRiyz/luvus ; https://luvus.dev/docs/ ; https://luvus.dev/docs/guides/orchestration/ ; https://luvus.dev/docs/getting-started/installation/ ; https://luvus.dev/docs/extend/using-modules/ ; https://luvus.dev/docs/extend/writing-modules/ +- Orca ADE: https://github.com/stablyai/orca ; https://www.onorca.dev/ ; https://www.onorca.dev/docs/ssh ; https://www.onorca.dev/docs/recipes/remote-worktrees ; https://www.onorca.dev/docs/notifications ; https://github.com/stablyai/orca/blob/main/docs/site/content/docs/cli/overview.mdx ; https://github.com/stablyai/orca/blob/main/mobile/README.md +- Rover baseline: README.md, STATUS.md, docs/ARCHITECTURE.md, docs/CLI.md, docs/SECURITY_MODEL.md, docs/acceptance/implementation-map.json, internal/. + +## Capability inventory + +Herdr: persistent server/client and named sessions; workspaces, tabs, split/zoom/popup panes; keyboard/mouse controls; detach/reattach, restore, scrollback/history replay, supported native-agent resume and experimental live handoff; agent detection/state/integrations/direct attach/agent control; worktree provenance; notifications; CLI/socket API; plugins/event hooks; themes/keybindings/sidebar configuration; SSH attach, saved machines, multi-machine visibility/reconnect; platform-specific behavior. + +Luvus: persistent terminal sessions and status sidebar; task board, drafts, dependencies, retries and history; worktree or shared-workspace workers; path leases; quality/merge gates; scheduled/one-shot automations and misfire recovery; reviewed unattended profiles and explicit access modes; Git/PR/issue workflows; file browser/viewer/editor/diff; remote SSH; notifications; themes/keybindings/localization; CLI/socket/UHP; language-neutral modules with preview, ref pinning, event hooks and module-owned config/state. + +Orca ADE: parallel worktrees/fan-out comparison; terminal splits and persistent scrollback; agent profiles/account/usage visibility; editor/file browser/file/image handoff/diff comments; GitHub/Linear tasks, PRs, issues, boards, CI/conflicts/review; Markdown/image/PDF previews; embedded browser, DOM/design inspection and computer-use controls; local/SSH/self-hosted/ephemeral remote modes, reconnect/port forwarding; notification inbox/unread state; CLI automation for worktrees/terminals/browser/UI; schedules/artifacts/skills; mobile companion semantics mapped to CLI/TUI. + +Existing Rover behavior to retain: source capture/diff/composition, tasks/process supervision/PTy, DAG workflows, strict config/scoped grants, structured Go/JUnit/SARIF/check interpretation, replay/counterfactual/mutation checks, evidence/review, MCP/remote CLI, context/memory, backup/restore, Ed25519 attestation, learning/evaluation. Each behavior must have a Rust acceptance mapping before Go removal. + +## Rust architecture decisions required + +Use a Rust workspace and stable serialized contracts. Candidate crates: model, store, source, execution, terminal, agents, assurance, workflow, remote, plugins, tui, cli. Finalize boundaries in ADRs. Select TUI, PTY, SQLite, async, SSH, browser, notification, and test dependencies only after an ADR records alternatives, license, supported platforms, maintenance, security assumptions, and acceptance evidence. Use SQLite migrations and an explicit Go-to-Rust state importer; never reinterpret old state silently. + +## 165-task backlog + +Tasks follow technical dependencies. A task may proceed when its own contract and safety prerequisites are recorded; a blocked source import does not block an independent Rust port. Each task is complete only when its named artifact/behavior and acceptance evidence exist. + +### P0 — source, license, compatibility audit +- [x] P-001 Freeze CLI/TUI-only, Rust-only, and SDK-removal boundary in an ADR. +- [x] P-002 Record Rover commands, JSON contracts, exit codes, config schemas, and security invariants. +- [ ] P-003 Inventory Herdr features against pinned docs/source with observable acceptance case per feature. (In progress: immutable commit is pinned; the feature matrix now maps documented capability groups to paths present in that tree and preserves documented optional/experimental distinctions, including off-by-default pane-history, integration-dependent native resume, and best-effort handoff. Current live docs were checked 2026-09-26. Per-feature source behavior review and acceptance-case closure remain.) +- [ ] P-004 Inventory Luvus features against pinned docs/source with observable acceptance case per feature. (In progress: immutable commit/license is pinned; the matrix now maps capability groups to exact source paths and records the orchestration guide's task/lease/gate/restart claims plus its beta automation view and the module guide's explicit no-SDK/no-plugin-runtime behavior. Current live docs were checked 2026-09-26. Per-feature source behavior review and acceptance-case closure remain.) +- [x] P-005 Resolve Luvus README/LICENSE/metadata disagreement at a pinned commit; block import until resolved. (README and root LICENSE both Apache-2.0 at `63cb48263f347093f952dc43bbe0c2d468609567`; older revisions remain separate.) +- [ ] P-006 Inventory Orca ADE features; separate shipped, optional, cloud, mobile, and marketing claims. (In progress: immutable source commit is pinned; the matrix maps major capability groups to paths in `src/`, `cloud/`, `mobile/`, and `native/`, and labels repository presence separately from README/docs/release claims. The pinned README claims for mobile distribution, cloud relay, broad CLI-agent compatibility, and renderer behavior are not treated as independently verified runtime facts. Per-feature source behavior review and claim/acceptance closure remain.) +- [x] P-007 Pin immutable commit IDs, repo URLs, root license texts, and detected nested notice/license-file hashes for all upstreams; see `UPSTREAM_SOURCE_AUDIT.md`. (Full dependency/asset clearance remains P-008/P-019/P-147.) +- [ ] P-008 Produce file-level NOTICE/attribution/trademark plan for full-source reuse. +- [x] P-009 Map source reuse versus Rust ports, including Orca's non-Rust/mobile/cloud product areas; see ADR-0001 and `UPSTREAM_SOURCE_AUDIT.md`. +- [x] P-010 Map every current Rover command, schema, acceptance row, and package to keep/port/remove. +Gate: no unpinned or license-unclear source is copied; every baseline behavior has disposition. + +### P1 — Rust workspace and engineering foundation +- [x] P-011 Approve Rust crate/process ownership ADR. (`ADR-0002-rust-workspace-architecture.md`; library and adapter dependencies remain separately gated.) +- [x] P-012 Approve terminal UI/rendering/input framework ADR. (`ADR-0015` selects Ratatui 0.30.2 and the matching Crossterm 0.29.0 backend, narrows enabled features, records terminal restoration and untrusted-screen contracts, and reviews the alternative paths. The new `rover-tui` crate has deterministic TestBackend and keyboard tests; locked license/author notices cover the resolved dependency graph. Native terminal behavior and CLI integration remain later acceptance tasks.) +- [ ] P-013 Approve PTY/terminal-emulator/platform support ADR. +- [x] P-014 Approve SQLite driver/migrations/backup ADR. (`ADR-0004-rust-sqlite-adapter.md`; P-023 record/event APIs are implemented; P-024 rooted Unix file APIs are in progress; state-root admission and backup remain later work.) +- [ ] P-015 Approve async runtime/supervisor/shutdown ADR. +- [ ] P-016 Approve CLI parser/JSON/exit-code compatibility ADR. +- [x] P-017 Create locked Rust workspace and crate skeleton. (`Cargo.toml`, `Cargo.lock`, and `crates/rover-core`; reviewed dependencies are documented in ADR-0003 and `RUST_DEPENDENCY_AUDIT.md`.) +- [ ] P-018 Establish fmt, clippy, unit, integration, fuzz, and platform CI gates. +- [ ] P-019 Add SBOM, dependency-license, unknown-license rejection, and vulnerability checks. +- [ ] P-020 Define redacted errors, structured logs, tracing IDs, bounded logs, and diagnostics. +Gate: clean reproducible build with auditable dependencies and no unsupported platform claims. + +### P2 — data model, storage, migration +- [x] P-021 Port typed IDs, hashes, timestamps, and validation. (`rover-core` tests cover the existing ID grammar, compatible legacy ID generation, canonical digest parsing, SHA-256 known answers, and UTC RFC3339Nano formatting.) +- [x] P-022 Implement versioned SQLite schema migrations and migration ledger. (`rover-store` creates v1 transactionally, checks checksums, proves rollback on injected failure, refuses unknown/future schemas, and adopts validated existing Rover v1 databases without changing records, events, or idempotency keys.) +- [x] P-023 Port transactional records, event append, idempotency, and recovery semantics. (`rover-store` provides atomic typed/raw put/get/list/mutate/create-once/events/delete APIs; tests cover Go's 10,000-level JSON limit, encoding/size/HTML escaping, atomic event/key rollback, independent-connection idempotency, panic/callback rollback, reopen recovery, and Go-compatible connection pragmas.) +- [ ] P-024 Port bounded blobs, digest checks, containment, atomic file writes, and permissions. (`ADR-0007` and `rover_store::files::StateRoot` cover Unix state-root admission, private directories, a no-follow SQLite open, and descriptor-anchored blob/atomic/bounded operations; macOS tests and a Linux target compile check pass, while native Linux execution is still pending.) +- [x] P-025 Port project/repository identity and cross-project isolation. (`ADR-0008`, `rover_core::RepositoryIdentity`, and `rover-access` port Go-compatible canonical path fallback, SHA-256 project ID, state+project audience framing, grant record JSON, issue validation, bearer token encoding, authentication denial behavior, and revoke mutation. Rust tests cover state/project separation and revocation; CLI list/revoke project presentation and runtime wiring remain in later CLI tasks.) +- [x] P-026 Port agent/port/database reservations and owner-safe cleanup. (`rover-store::resources` now persists the Go-compatible agent slot and named resource lease records, enforces capacity and exclusive named reservations atomically across independent SQLite connections, supports same-owner re-acquisition, and releases only the requested owner's rows without timeout takeover; contract, validation, and race tests pass.) +- [x] P-027 Port atomic budget charging, caps, usage reporting, and reset. (`rover-store::budget` implements the Go settings/budget wire record, uncapped default, versioned cap/charge/reset operations, atomic rejection, per-owner totals, and compatibility with omitted/null legacy fields; store-local cross-connection cap contention tests pass. CLI usage/config wiring remains for later port tasks.) +- [x] P-028 Build read-only Go-state inventory and dry-run migration report. (`rover-store::legacy::LegacyStateInventory::inspect` uses a read-only, query-only SQLite snapshot; classifies Go v1, Rust v1, empty, and incompatible databases; reports table counts, stable source digests, invalid record blockers, verified content-addressed objects, and metadata-only task/check tree plus unmanaged-root inventory. It never writes or creates a destination. Tests compare source trees before/after and cover supported, empty, malformed, tampered, and incompatible states; CLI presentation remains in later tasks.) +- [x] P-029 Import Go records/events/artifacts/grants idempotently into a new Rust state root. (`LegacyStateInventory::import_to` requires an explicit quiesced-source assertion and opt-in acknowledgement for excluded task/check runtime trees or unmanaged root entries; it refuses inventory blockers and overlapping roots, opens the source read-only, copies validated records/events/request keys/content-addressed objects into a new Rust root, revokes grants, releases leases, and strips process state while marking nonterminal executions LOST. A durable source-path/snapshot receipt makes repeat imports idempotent; runtime trees are retained only in the original source and are not imported.) +- [x] P-030 Verify migration hashes/counts; retain original state and rollback instructions. (Receipt retries now revalidate the source snapshot, every copied payload, exact timestamps for unsanitized records, exact source event rows, exact request keys, generated sanitation events, receipt row/event, counts, and the complete content-addressed object set with blob digest checks. Regression tests tamper with records, events, request keys, and add a valid unexpected object, and verify rejection. `docs/design/GO_RUST_STATE_MIGRATION.md` records preflight, source retention, excluded runtime data, and rollback to the unchanged Go state path. `make rust-check`, Go check/race/demo, and Linux cross-target compilation pass.) +Gate: fixtures migrate losslessly; source state is never destructively modified. + +### P3 — source capture, Git, worktrees +- [x] P-031 Port committed and working-tree capture with explicit non-atomic labels. (`rover-source::capture` reads committed blobs with Git plumbing and captures WORKTREE through two equal content reads, returning sorted snapshot metadata plus digest-keyed frozen bytes. It matches Go snapshot identity JSON, labels committed and non-atomic working-tree consistency, opts into untracked files explicitly, and persists records/blobs through `capture_into_state` on platforms with the current Unix `StateRoot`. Six fixture tests cover exact commit bytes, filter non-execution, worktree/untracked behavior, identity compatibility, root separation, and persistence. Non-Unix state-root persistence remains part of platform work; the pure capture API is platform-neutral.) +- [x] P-032 Port unsafe path/symlink/submodule/LFS/case-collision/size rejection. (`rover-source` rejects noncanonical/escaping paths, non-UTF-8 Git paths, nested `.git`, symlink and special-file inputs, Git symlink/gitlink entries, unresolved LFS pointers, case-folded collisions, files over 8 MiB, snapshots over 128 MiB, and more than 10,000 files. Tests exercise path, symlink, gitlink, LFS, collision, and size boundaries; case-collision behavior has a filesystem fixture where distinct case spellings are representable plus a deterministic platform-independent collision test.) +- [x] P-033 Port immutable snapshot materialization and hash verification. (`rover-source` validates snapshot identity, Git object ID, path tree, case-fold collisions, modes, digests, and file/aggregate limits; checks every content-addressed blob before creating output; and materializes through Unix descriptor-relative no-follow directories and exclusive file creation with exact modes. `inputs_unchanged` verifies content hash, byte length, and executable mode. Tests cover exact nested bytes/modes, modified-input rejection, invalid metadata/tree conflicts, existing-file preservation, and missing-blob refusal before destination creation. `make rust-check`, `GOWORK=off make check`, `GOWORK=off make race`, `GOWORK=off make demo` (12 scenarios), and locked offline Linux x86_64 all-target cross compilation pass.) +- [x] P-034 Port candidate identity, safe diff, patch export, and input-change detection. (`Snapshot` IDs remain bound to repository plus canonical file metadata; `compare_snapshots` validates both identities and returns path-sorted added/deleted/modified rows with Go-compatible categories and observed policy/test-deletion findings. `diff` imports verified content into a private temporary bare Git database with scrubbed environment and no checkout, then exports bounded binary patch bytes; `inputs_unchanged` compares every frozen input's digest, size, and mode. A fixture applies the binary patch to the base and verifies the resulting candidate ID; comparison tests verify ordering and findings. Source tests passed three consecutive runs; `make rust-check`, `GOWORK=off make check`, `GOWORK=off make race`, `GOWORK=off make demo` (12 scenarios), and locked offline Linux x86_64 all-target cross compilation pass.) +- [x] P-035 Port worktree create/list/inspect/remove with dirty-worktree safeguards. (`rover-source` creates detached no-checkout Git worktrees from validated snapshots, initializes the index, then materializes only verified immutable blobs; it lists registered worktrees from NUL-delimited porcelain, inspects source changes against a frozen base, and removes only exact registered non-main paths after checking tracked, untracked, and ignored contents are clean. Git's final removal runs without force. Tests cover filter non-execution, listing/detached HEAD, dirty detection, dirty and ignored-file refusal with data preserved, clean removal, main-worktree refusal, duplicate destination refusal, and malformed porcelain fields. The worktree lifecycle test passed three consecutive runs; `make rust-check`, `GOWORK=off make check`, `GOWORK=off make race`, `GOWORK=off make demo` (12 scenarios), and locked offline Linux x86_64 all-target cross compilation pass.) +- [x] P-036 Port base refs, branch reservations, repository worktree roots, and provenance. (`resolve_reference` preserves Go's argv-only `rev-parse --verify --end-of-options ^{commit}` behavior and rejects option/tree refs. `branch_reservation_resource` validates Git branch syntax and hashes the canonical repository path plus qualified branch ref into an ID accepted by the existing atomic named-resource lease API; contention and cross-repository separation are tested. `StateRoot::task_workspace_path` creates only a private per-task parent and leaves Git's worktree leaf absent. Worktree inspection now records the frozen base snapshot ID alongside the observed candidate snapshot, commit, path, and diff. The ref/lease and task-root tests each passed three consecutive runs; `make rust-check`, `GOWORK=off make check`, `GOWORK=off make race`, `GOWORK=off make demo` (12 scenarios), and locked offline Linux x86_64 all-target cross compilation pass.) +- [x] P-037 Port parent/child worktree lineage and exact-base semantics. (`replace_worktree_snapshot` admits only a registered detached task worktree whose HEAD equals the frozen base commit and whose path is exactly `/tasks//workspace`; it requires same-repository snapshots, a clean base worktree, compatible file/directory layouts, and verified initial blobs before mutation. It removes base files through descriptor-relative no-follow operations, materializes initial bytes, and preserves the base commit as HEAD. Inspection records the base snapshot identity and candidate snapshot. Tests cover child content with unchanged exact-base HEAD, dirty-base refusal without byte changes, incompatible layout rejection, and safe rooted deletion including symlink/traversal refusal. Exact-base and dirty-refusal tests each passed three consecutive runs; `make rust-check`, `GOWORK=off make check`, `GOWORK=off make race`, `GOWORK=off make demo` (12 scenarios), and locked offline Linux x86_64 all-target cross compilation pass.) +- [x] P-038 Port disjoint-change composition and fail-closed conflict behavior. (`compose_snapshot` validates and persists sorted inventories only after verifying every referenced object; `overlay_snapshot` applies explicitly selected unique paths; `merge_snapshots` requires each candidate to share the repository and exact base commit, accepts disjoint or byte-identical edits, and rejects differing edits, delete/edit conflicts, and invalid merged trees without guessing. Tests verify modified/deleted/added file composition, selected overlays, identical edits, conflicting edits, delete-versus-edit conflicts, and exact-base mismatch. The composition test passed three consecutive runs; `make rust-check`, `GOWORK=off make check`, `GOWORK=off make race`, `GOWORK=off make demo` (12 scenarios), and locked offline Linux x86_64 all-target cross compilation pass.) +- [x] P-039 Port final integrated snapshot verification and retention. (`verify_retained_snapshot` loads the durable snapshot record, validates its identity/tree/limits, and verifies every content-addressed blob against digest and byte length before reporting checked file/byte totals. The merger test verifies a persisted integrated snapshot, then removes one object and confirms integrity verification rejects the incomplete retained artifact. Rover has no automatic snapshot/object pruning; this port preserves snapshots until a separately reviewed retention policy is implemented. `make rust-check`, `GOWORK=off make check`, `GOWORK=off make race`, `GOWORK=off make demo` (12 scenarios), and locked offline Linux x86_64 all-target cross compilation pass.) +- [x] P-040 Add crash/race tests for capture, locks, worktree admission, and cleanup. (`rover-source` and `rover-store` domain owners now cover capture concurrent with atomic file replacement (successful captures always validate byte length and SHA-256), per-destination OS-locked worktree admission, duplicate concurrent cleanup, and OS-lock release during owner panic/unwind. The three focused regression tests each passed three consecutive runs; `make rust-check` (109 Rust unit tests, seven dependency-audit tests, license audit), `GOWORK=off make check`, `GOWORK=off make race`, `GOWORK=off make demo` (12 scenarios), and locked offline Linux x86_64 all-target cross compilation pass. Plain `make check` fails before tests because the adjacent Go workspace excludes this root module; setting `GOWORK=off` runs the intended repository module checks.) +Gate: no path escape, guessed merge, or unverifiable candidate. + +### P4 — process execution and session runtime +- [ ] P-041 Port argv-only process launch, scrubbed environment, timeouts, cancellation, bounded output. (In progress: `rover-execution` implements the Unix argv/environment/output/timeout/cancellation path and writes bounded mode-0600 logs while the process runs; 14 tests cover output and executable hashes, Go-compatible exit/error fields, exact and over-limit bounds, clean/pass-env, path resolution, spawn failure, prefix-limited executable hashing, pre-cancellation, exclusive logs, setup-deadline preflight, process-group timeout/cancellation, and descendant termination. The focused suite passed three serial runs; `make rust-check` passed 124 Rust unit tests, seven dependency-audit tests, and the notice audit. Locked offline Linux x86_64 all-target cross compilation passed. `GOWORK=off make check`, `GOWORK=off make race`, and `GOWORK=off make demo` passed; the demo covered 12 scenarios. ADR-0009 and the `rustix` feature/license inventory record the decisions. Non-Unix execution and Rust CLI/TUI integration remain open; do not claim P041 complete yet.) +- [ ] P-042 Port detached supervisor ownership, heartbeats, reconnect, and LOST-on-ambiguity. (In progress: `rover_execution::supervisor` transactionally ports one-way claim, pre-launch cancellation, heartbeat/cancel observation, cancellation requests, a managed 250 ms heartbeat loop, and stale `LOST` reconciliation with a ten-second grace, process-presence proof, and an in-transaction PID/heartbeat recheck. Detached argv launch writes a private exclusive per-task log, isolates the worker process group, reaps the direct child, records PID/identity, and refuses a duplicate launch through exclusive log creation; dispatch failure is persisted as `ERROR`. Confirmed `LOST` transitions release only that task's resources, and repeated reconciliation retries owner-scoped cleanup. A subprocess test proves a worker survives after its launcher process exits. Linux identity binds boot ID/PID/start time and checks zombies/reuse; macOS keeps PID reuse ambiguous and only treats OS-reported `ESRCH` as gone. Nineteen focused supervisor tests pass on macOS in three consecutive runs; the current `make rust-check` passes 147 Rust unit tests plus nine dependency-audit tests and notice checks; locked offline Linux x86_64 all-target cross compilation passes. Linux process behavior still needs a native runtime. ADR-0010 records the state transition contract and existing dependency/notice review. Client reconnect, normal task-finalization resource release, and CLI/TUI integration remain open.) +- [ ] P-043 Implement Linux/macOS/Windows PTY create/read/write/resize/close where supported. (In progress: `rover_execution::pty` uses reviewed `portable-pty` 0.9.0 for Unix and Windows ConPTY, with argv-only spawn, cleared explicit environment, bounded dimensions, byte I/O, resize, and direct-child close/reap. Four native macOS tests pass three consecutive runs. The Linux workspace all-target cross-check and an isolated Windows PTY-module all-target cross-check pass; full-workspace Windows cross-compilation cannot run on this macOS host because MSVC `lib.exe` and Windows SDK headers are absent. The 69-package dependency/license notice audit passes. Native Linux and Windows runtime evidence remains open; see ADR-0011. Session persistence and terminal emulation are later tasks.) +- [ ] P-044 Implement terminal emulator control-sequence safety and bounded scrollback. (In progress: `rover_execution::terminal` uses audited `alacritty_terminal` 0.26.0 with OSC 52 disabled and no outward event listener; it enforces 250,000 visible cells, 1,000,000 scrollback cells, 10,000 history lines, and 64 KiB parser chunks, exports filtered cell text, and replaces bidi formatting/control characters. Seven regression tests passed three consecutive runs and cover styling/control stripping, OSC52/title/query/DCS suppression, requested and cell-budget history caps, resize trimming after accumulated history, wide-glyph/erase resize safety, chunk/dimension caps, and bidi sanitization. The TUI renderer accepts only text from this screen model and adds its own control/bidi and 8 MiB filters. Linux workspace all-target cross-compilation and isolated Windows terminal-module all-target cross-compilation pass. Continuous PTY-to-screen updates and native Linux/Windows runtime evidence remain open; see ADR-0012.) +- [ ] P-045 Implement persistent server/client sessions and named namespaces. (In progress: Unix `rover_execution::sessions::SessionServer` owns named PTYs by `(namespace, name)`, binds private mode-0600 sockets under a mode-0700 directory, enforces one attached writer, carries bounded/base64 Go-compatible frames, keeps a 32 KiB replay ring, and leaves a child alive after client detach until explicit session/server stop. Socket paths use a 128-bit SHA-256 key digest; `SessionClient::connect_named` resolves a session from its key without an in-memory server handle, supporting cross-process attach while the owner process remains alive. The cross-process test verifies child input/output, detach, replay on reconnect, and refusal after stop. `rover-cli` starts a detached owner for a project-scoped login shell, reconnects by key, and uses a per-user private short socket directory under `/tmp`; its integration test verifies named attach and normal daemon shutdown after the shell exits. The bounded TUI bridge streams output through Rover's terminal screen. Owner crash/restart restoration, Windows named-pipe support, process-tree stop behavior, and native end-to-end TUI acceptance remain open. `make rust-check CARGO='cargo +1.88.0'` passed all 192 Rust unit/integration tests, ten dependency-audit tests, strict Clippy, formatting, and locked offline license checks. See ADR-0013.) +- [ ] P-046 Implement workspace/tab/split/zoom/popup/scratch pane lifecycle and layouts. (In progress: `rover_execution::layout` defines versioned JSON workspace state with ordered tabs, nested binary splits, stable IDs, per-tab focus/zoom/popup, scratch/session panes, resize, close, and validated bounds. Input and output JSON are capped at 4 MiB; input depth is capped at 96 before recursive decode. Workspace validation enforces 128 tabs, 256 panes, and 16 split levels. Unix save/load APIs use an admitted private `SafeDir`, atomic mode-0600 writes, and no-follow reads capped at 4 MiB plus one byte; storage and corruption tests passed three consecutive runs. `rover_tui::run_persistent` restores standalone layouts; attached TUI persists workspace shortcuts through `run_attached_session_persistent`; `rover-cli` wires project-scoped save/restore. Full focus-aware mouse/keyboard interaction, move/resize controls, and terminal-golden acceptance remain open; see ADR-0014 and ADR-0015.) +- [ ] P-047 Implement detach/reattach, restart restore, bounded history replay, explicit restore result. +- [ ] P-048 Implement multiple clients with deterministic focus and terminal-size ownership. +- [ ] P-049 Implement keyboard prefix controls, configurable bindings, mouse where supported, help. (In progress: `?` opens keyboard help in standalone mode; Ctrl-B then `?` opens it while attached so ordinary question marks continue to reach the shell. Help captures keys until `?` or Esc closes it. Two renderer/input regression tests passed three consecutive runs. User-configurable bindings and mouse controls remain open.) +- [ ] P-050 Implement copy/search/selection/scrollback export and safe paste. +Gate: session state and process survival/recovery match documented guarantees. + +### P5 — TUI workbench and Herdr/Luvus interaction +- [ ] P-051 Implement TUI sidebar for projects, workspaces, tabs, panes, agents, and unread state. (In progress: `rover-tui` renders active workspace identity, ordered tabs, nested split panes, active focus, and popup state; it maps keyboard input to select/create tabs, cycle pane focus, split, zoom, close, and quit. The Rust `rover tui` launcher now connects project-scoped login-shell sessions and persists layouts. The Unix attach API renders `TerminalScreen` output, sends translated key input, preserves Ctrl-] detach, uses Ctrl-B-prefixed workspace controls, and tracks the selected pane's split size. The footer and `?` overlay document controls. Terminal state uses a tested RAII restoration guard. The project/agent/unread sidebar, configurable bindings, and interactive native terminal acceptance remain open.) +- [ ] P-052 Implement task/workflow board, detail panes, logs, evidence, diff, and attach views. (In progress: `rover tui` reads up to 1,000 durable `task` records through the Rust store, validates required Go-compatible fields and IDs, filters to the canonical project path, and refuses malformed/oversized results rather than showing partial state. Ctrl-B then `t` opens a read-only project snapshot with bounded scrolling and sanitized task text; `r` reloads the records through the same project-filtered store path and preserves selection by task ID where possible. Failed/oversized refreshes leave the prior snapshot visible and show a sanitized result. `c` opens a confirmation; `y` persists a cancellation request through the Rust supervisor store transaction, which refuses terminal tasks and records the event. The UI states that prior external actions are not undone. Enter opens persisted objective/status/candidate/attempt/process outcome/byte-count/hash metadata. In detail, `o`/`e` lazily load stdout/stderr by recorded digest from the bounded blob store; the TUI independently checks the complete bytes against SHA-256, sanitizes terminal controls and bidi formatting, clips the display to 64 KiB, and supports scrolling. Digest mismatch/invalid hash and blob read failures render as unavailable output. `i` loads the linked investigation record, validates its schema and repository/candidate binding to the task, bounds check/unknown/finding counts, sanitizes display text, and labels all fields as recorded assessment evidence rather than independent verification. `d` loads linked base/candidate snapshots, verifies retained source objects, enforces project identity, compares validated inventories, and presents a bounded, scrollable path-change list; the complete applicable patch remains in the `rover diff` command. Regression tests cover log digest validation, sanitization, clipping, navigation, refresh selection/errors/shortcut scope, cancellation confirmation, evidence rendering/navigation and project/candidate binding, snapshot verification/project binding, diff navigation, stale candidate rejection, and safe display. Workflow panes, further task mutations, and interactive native acceptance remain open.) +- [ ] P-053 Implement layout presets, pane focus/move/resize/zoom, and restore. +- [ ] P-054 Implement themes, status bars, sidebar layouts, low-color mode, and keyboard help. (In progress: an in-app keyboard-help overlay documents current standalone and attached controls and consumes input until dismissed; renderer and key-routing regressions pass three consecutive runs. Themes, richer status bars, sidebar layouts, and low-color support remain open.) +- [ ] P-055 Implement TUI task filtering/search/sort and persistent view preferences. (In progress: `/` edits a bounded UTF-8 query matching task ID/status/objective/candidate/error; Enter applies and saves, Esc discards. `s` cycles deterministic Updated/Status/Objective ordering. The task provider continues to use raw stable record indexes so filtered/sorted display cannot retarget cancel, log, evidence, or diff actions. Rust-store preferences are keyed by canonical repository digest and validate schema, exact repository path, sort value, and query byte bound when loaded. Filtering/order, preference writes and round-trip loading, malformed record rejection, search cancellation, byte limits, and sanitized bounded rendering have regression coverage; the focused tests passed three consecutive runs. On the owned macOS environment, `make rust-check CARGO='cargo +1.88.0'`, `GOWORK=off make check`, `GOWORK=off make race`, and all 12 `GOWORK=off make demo` scenarios pass. Interactive manual review and Linux-owned execution remain open.) +- [ ] P-056 Implement TUI file tree, Git coloring, hidden-file quick-open, and bounded viewer. (In progress: `SafeDir` lists a directory with no-follow descriptor traversal and revalidates every returned name against the opened directory; symlinks and special entries are shown but cannot be entered or opened. The Rust source browser maps Git porcelain status to added/modified/deleted/renamed/untracked/conflict labels, including deleted-path rows, supports hidden-file recursive quick open while excluding `.git`, limits scans to 10,000 entries and 48 levels, and previews at most 256 KiB through a no-follow regular-file open. Attached TUI adds Ctrl-B then `f` for the tree and Ctrl-B then `.` for quick open; `h`, Backspace, `r`, search, selection, sanitized preview, and Git colors are covered by store/source/TUI regressions. Source-browser regressions passed three consecutive runs. Native interactive review and Linux-owned execution remain open.) +- [ ] P-057 Implement editor handoff, safe inline edits, atomic save, dirty prompts, and diff preview. (In progress: `SafeDir::atomic_replace_regular_file_path` provides rooted no-follow replacement, preserves existing mode, caps writes, syncs the temp file and parent, and refuses symlink/special targets. `rover-source::save_repository_file` adds an 8 MiB cap and compares current bytes with the starting SHA-256. Attached TUI wires that reader/saver: `e` opens valid UTF-8 text up to 8 MiB, Ctrl-S saves, Ctrl-D renders bounded unified hunks with three context lines and accurate line ranges, Ctrl-E suspends the TUI and opens `VISUAL`/`EDITOR` (default `vi`) against a 0600 copy in a 0700 temporary directory, and Esc prompts before discarding. Diff work is capped at 10,000 lines per side and one million LCS cells; display is capped at 512 changed lines per side and 64 hunks. Paths/content are sanitized, and no-final-newline edits are identified. External-editor bytes return to the unsaved TUI buffer; repository writes still go through Ctrl-S and the stale check. The temp directory is descriptor-opened for bounded no-follow readback; CLI tests cover modified output, nonzero exit, oversized input, and symlink substitution. Stale-save failures retain the dirty buffer; the editor renders bounded line windows, and control-character files are refused. TUI regressions cover multi-hunk context, insertion/deletion, newline-only edits, sanitized paths, output caps, save and dirty prompts. Native interactive editor/terminal review and Linux-owned execution remain open.) +- [ ] P-058 Implement command palette, prompt/task drafts, reusable notes, and saved commands. (In progress: the attached palette, repository-bound notes, confirmed saved commands, and multiline task-brief editor remain available. Task drafts persist with v1-to-v2 migration and are not dispatched. The TUI now confirms conversion of a saved draft into a durable task plan and offers a project-loaded plan list; no worker starts. Path globs are validated at conversion and stored but not applied; quality-gate text remains opaque. TUI/CLI regressions cover draft storage, migration, field bounds, project binding, plan confirmation, project-loaded listing, and no dispatch. Still open: Manual/Now worker start, adapter-backed agent choice, actual task execution and gate evaluation, independent automation drafts, native interactive review, and Linux-owned execution.) +- [ ] P-059 Implement terminal notifications/sounds with active-pane suppression. (In progress: the bounded terminal emulator captures only coalesced BEL events; the attached TUI relays BEL through the host terminal only when the originating pane is unfocused. Regressions cover BEL parsing/coalescing, control-sequence BEL suppression, and focus transitions. Configurable sound/notification policy, quiet hours, inbox/unread state, and platform-native notification delivery remain open.) +- [ ] P-060 Add TUI snapshots/golden tests for narrow, wide, colorless, and Unicode terminals. +Gate: every supported action is keyboard reachable and view output cannot inject terminal control. + +### P6 — agents and integrations +- [ ] P-061 Port declared agent profiles/capabilities; unknown profiles fail closed. (In progress: new `rover-agents` carries the four Go-compatible descriptors, preserves validation labels, advertises no permission mediation, and rejects unknown names. Rust CLI supports read-only `agents` listing and `agent capabilities ` lookup. The crate builds generic objective substitution and Codex/Claude argv with argument-bound prompts, reviewed permission modes, and Go-compatible executable/model/tool validation. Process discovery, credentials, user-selected TUI worker dispatch, and native platform/live-provider validation remain open; see ADR-0019.) +- [ ] P-062 Implement process/screen agent discovery with evidence and confidence. (In progress: `rover-agents::detect_agent` accepts explicit foreground-process and live bottom-buffer evidence, resolves exact declared executable basenames or validated local aliases, and returns identity/state confidence, safe evidence, and reason codes. The independent Herdr-format evaluator is implemented under ADR-0027 with the 22 byte-audited pinned manifests, nested gates, all upstream selectors/operators, bounded TOML/regex input, and Codex fallback. The Unix owned-PTY sampler uses the exact foreground process-group leader, Linux process-start identity or repeated bounded macOS `ps` observations, and fails closed when sampling is unavailable. The attached TUI evaluates the live viewport, OSC title, and OSC 9;4 progress for pinned direct executable mappings; lifecycle-authority integration, wrapper/child inference, and broader process-group enumeration remain open.) +- [ ] P-063 Implement working/blocked/done/idle/unknown/stale state rollups. (In progress: `rover-agents` adds deterministic pane/tab/workspace aggregation with explicit unviewed completion, configurable freshness, stale and unknown counts, blocked-pane navigation IDs, and duplicate/inconsistent snapshot rejection. State precedence is blocked > working > done > idle > unknown; stale observations do not override fresh state and produce `stale` when all observations are stale. Six focused regressions cover precedence, viewed completion, freshness, filtering, invalid records, and clock boundaries. Lifecycle authority, persisted events, UI wiring and unread transitions remain open; see ADR-0022.) +- [ ] P-064 Implement lifecycle event integrations with sequence ordering and authority release. (In progress: `rover-agents::LifecycleAuthority` accepts bounded source-scoped idle/working/blocked reports and release events, ignores sequence numbers not greater than that source's latest accepted sequence, preserves high-water marks after release, and suppresses screen estimates while one or more sources remain active. Multiple active sources resolve to unknown rather than choosing an incomparable sequence. Five focused regressions cover precedence/release, delayed events, equal sequence, source conflict, and bounds. Hook installation, socket transport, durable event history, expiry/agent-exit wiring, and TUI integration remain open; see ADR-0023.) +- [ ] P-065 Implement native session identity and opt-in agent-specific resume. (In progress: `rover-agents` persists validated exact Codex/Claude session IDs bound to a canonical project and pane, with evidence-origin metadata and a conversion from parsed transcript identity. The attached TUI offers the active pane's binding through Ctrl-B then `a`, displays its source and exact ID, and requires explicit confirmation before quoting and sending the interactive provider resume command to Rover's persistent shell. It also supports confirmed removal of Rover's binding without stopping a running agent. Control characters are rejected; provider permission/sandbox behavior remains provider-owned. Focused regressions cover project/pane binding, transcript provenance, exact-ID resume, shell-argument separation, mismatch, invalid identities, clear, TUI confirmation/cancel, and control rejection. Integration hooks, lifecycle restore orchestration, session path references, duplicate-active-session checks, other native providers, and live-provider verification remain open; see ADR-0024. Pinned upstream resume source blobs were unavailable in the partial clone, so no claim of source-level parity is made.) +- [ ] P-066 Port Codex/Claude JSONL events and preserve fixture/live validation distinction. (In progress: `rover-agents::parse_transcript` ports the bounded Go event contract for session identity, completion/failure, claims, usage, cost, UUID deduplication, unknown events, and terminal ordering. All 11 parser/registry tests, including nullable-field compatibility, passed three consecutive runs. Full Rust workspace verification and Go compatibility, race, and demo gates pass. Fixtures are explicitly not live-provider certification; see ADR-0020.) +- [ ] P-067 Implement generic agent list/status/wait/prompt/read/attach interfaces. (In progress: `rover agent session bind` stores a user-selected exact identity only for a pane present in the project's saved workspace; list/status expose a bounded, deterministic, project-scoped inventory, and clear removes only Rover's binding. Output distinguishes `binding_status: bound` from `agent_state: unknown`. Attached PTYs now return a process sample through the single active writer, and the TUI periodically displays identity while leaving state unknown without manifests/lifecycle evidence. Regression tests cover workspace pane validation, bind/clear, inventory isolation, PTY sampling and protocol framing. Live inventory joins across sessions, wait, prompt, read, and direct agent attach still require lifecycle transport plus pane I/O authority; see ADR-0025 and ADR-0026.) +- [ ] P-068 Implement agent-to-agent control with explicit target and human-visible effects. +- [ ] P-069 Implement integration install/status/update without silent permanent-config edits. +- [ ] P-070 Test each agent adapter; unsupported features are absent from capability output. +Gate: no guessed state, false provider certification, or unreviewed permission mediation. + +### P7 — task orchestration and automations +- [ ] P-071 Port project-bound tasks, drafts, status machine, dependencies, and retry history. (In progress: rover-core::TaskBrief defines byte-bounded title/path/dependency/gate/prompt validation; validate_task_graph rejects missing/duplicate/self/cyclic edges and returns deterministic topological order. rover-tasks persists canonical-project-bound plan records, derives transitive readiness from prerequisite outcomes, and records manual status/attempt history with a ten-attempt cap. The TUI confirms draft-to-plan creation and loads a project-scoped plan list; ADR-0017/0018 define these contracts. Actual worker dispatch, quality-gate evaluation, TUI lifecycle actions, and Linux-owned execution remain open.) +- [ ] P-072 Implement task workers in isolated worktree or explicit shared workspace. +- [ ] P-073 Implement glob path leases with atomic admission and stale-owner recovery. +- [ ] P-074 Port quality gates with argv execution, bounds, retained output, retry, review. +- [ ] P-075 Implement serialized merge gate in integration worktree; abort cleanly on conflict. +- [ ] P-076 Port workflow DAG, handoffs, repair bounds, composition, and final verification. +- [ ] P-077 Implement fan-out comparisons and explicit winner selection. +- [ ] P-078 Implement one-shot/recurring schedules, timezone/DST, idempotency, and misfire policy. +- [ ] P-079 Implement approved unattended adapters and explicit read/workspace/full access. +- [ ] P-080 Implement context/compaction gate without treating context as task progress. +Gate: crash/restart cannot duplicate runs, skip dependencies, or corrupt integration. + +### P8 — assurance, evidence, and review +- [ ] P-081 Port baseline config pinning, strict validation, and empty-plan inconclusive decision. +- [ ] P-082 Port Go/JUnit/SARIF/exit-code interpreters with malformed/missing/truncated semantics. +- [ ] P-083 Port check runner with cancellation, timeout, source-unchanged checks, bounded outputs. +- [ ] P-084 Port accepted/blocked/inconclusive/review-required decisions and exit codes. +- [ ] P-085 Port counterfactual regression proof against exact base and candidate. +- [ ] P-086 Port finite mutation campaigns with unique-match and per-mutant outcomes. +- [ ] P-087 Port exact replay bound to source/config/tool identity. +- [ ] P-088 Port report/diff/review/export and candidate-bound human assertion. +- [ ] P-089 Port Ed25519 keys/sign/verify with explicit trusted public keys. +- [ ] P-090 Port evidence hashes, event trail, retention, redaction, and backup integrity. +Gate: no model assertion becomes observed fact; evidence is tied to exact inputs. + +### P9 — remote control and authority +- [ ] P-091 Port MCP stdio and protocol negotiation with bounded JSON-RPC/cancellation. +- [ ] P-092 Port scoped HTTP MCP, TLS, loopback, Origin rejection, request bounds, duplicate-ID checks. +- [ ] P-093 Port project/audience/tool grants, token hashing, expiry, revocation, private token files. +- [ ] P-094 Port remote CLI node profiles, CA pinning, timeout ambiguity, no blind retry. +- [ ] P-095 Implement SSH machine profiles, host-key checks, named sessions, independent reconnect. +- [ ] P-096 Implement remote terminal stream/input/resize without replaying writes after disconnect. +- [ ] P-097 Implement remote worktrees and agents with explicit server-user authority disclosure. +- [ ] P-098 Implement port forwarding only through explicit user-selected endpoints. +- [ ] P-099 Implement local/SSH/self-hosted/ephemeral runtime adapters only when functional. +- [ ] P-100 Port Rover's restricted Docker executor with pinned image digests, argv-only invocation, no-network/read-only/capability restrictions, cleanup confirmation, and no local fallback; require a live-validated backend before any untrusted-code isolation claim. +Gate: remote operations are authenticated, scoped, non-replaying, and honestly labeled. + +### P10 — Git, review, and repository UX +- [ ] P-101 Implement multi-repository registry, base-ref search, and workspace switching. +- [ ] P-102 Implement status/branch/commit/log/diff operations in TUI and CLI. +- [ ] P-103 Implement provider abstraction for GitHub/Linear issues, PRs, boards, and CI. +- [ ] P-104 Implement explicit write confirmation and reconciliation for external changes. +- [ ] P-105 Implement diff annotations, batched review feedback, and agent handoff. +- [ ] P-106 Implement conflict review and branch/PR status tracking without automatic merge. +- [ ] P-107 Implement Markdown/image/PDF preview with bounded parsing/resource limits. +- [ ] P-108 Implement attachments by path selection with MIME/size checks/private staging. +- [ ] P-109 Implement repository search across files, commands, tasks, agents, and context. +- [ ] P-110 Add provider contract tests and fixtures; no live write claims from fixtures. +Gate: external writes remain explicit, reviewable, and reconcilable. + +### P11 — browser/design/computer use equivalents +- [ ] P-111 Implement local browser/dev-server lifecycle with owned-process tracking. +- [ ] P-112 Implement browser page open/close/list and explicit origin policy. +- [ ] P-113 Implement DOM/accessible-tree snapshots and selector-based inspection. +- [ ] P-114 Implement element-to-agent context handoff with captured source and provenance. +- [ ] P-115 Implement screenshot/artifact capture where OS/browser backend supports it. +- [ ] P-116 Implement UI interaction commands behind explicit permission and audit trail. +- [ ] P-117 Implement terminal-accessible render preview; do not advertise pixel design mode without it. +- [ ] P-118 Add browser isolation, content bounds, prompt-injection, and hostile-page tests. +- [ ] P-119 Document features that have only textual TUI equivalents and validate each. +- [ ] P-120 Mark unsupported GUI-only behavior rather than silently stubbing it. +Gate: no unsupported browser/computer-use capability is advertised. + +### P12 — modules, notifications, settings, usage +- [ ] P-121 Implement plugin/module manifest, argv actions, event hooks, panes, and settings. +- [ ] P-122 Implement module preview, pinned source revision, digest, upgrade diff, safe uninstall. +- [ ] P-123 Implement module-owned config/state and explicit unsandboxed authority notice. +- [ ] P-124 Implement skills import/export with provenance, version pins, and safe display. +- [ ] P-125 Implement persistent notification inbox, unread/mark-unread/snooze/jump actions. +- [ ] P-126 Implement themes/keybindings/sidebar/scrollback/notification settings with atomic updates. +- [ ] P-127 Implement locale framework and selected translations with complete key coverage. +- [ ] P-128 Implement sound/system/terminal notification backends with opt-out and tests. +- [ ] P-129 Implement provider usage/rate-limit displays only from documented provider data. +- [ ] P-130 Implement account switching only through supported CLI/session interfaces and confirmation. +Gate: extensions are not sandboxed unless they truly are; provider data is never fabricated. + +### P13 — CLI parity and SDK removal +- [ ] P-131 Port every public Rover command/alias to Rust with golden JSON and exit-code tests. +- [ ] P-132 Port CLI task/workflow/remote/agent/session commands matching TUI actions. +- [x] P-133 Remove Python/TypeScript/Go SDK source after the user's explicit removal instruction. (`sdk/` deleted; `internal/model` now guards the absence of the SDK source tree. The current Go CLI/TUI remains until its separate Rust parity gate.) +- [x] P-134 Remove SDK CI jobs, Make targets, docs, version checks, manifest rows, and current references. (Removed SDK test jobs/target, current README and CI references, and regenerated the source manifest. Dated validation/audit records retain old SDK results as history.) +- [x] P-135 Preserve historical validation/audit records as dated history, not current claims. (Old release validation files remain untouched; current README, status, CI docs, and active plan identify them as historical.) +- [ ] P-136 Remove Go runtime only after every existing Go acceptance item has Rust coverage. +- [ ] P-137 Add Go state read-only fallback and documented migration support period. +- [ ] P-138 Add command/API contract diff tool to flag compatibility drift. +Gate: no current docs or workflows reference removed SDKs; legacy data remains recoverable. + +### P14 — additional documented protocol and interaction details +- [ ] P-139 Implement versioned UHP 1.0 registry, owner-only IPC, snapshots, event streams, exact input, semantic waits, and bounded cancellation. +- [ ] P-140 Implement concurrent clients with independent viewport/layout state and explicit terminal input ownership. +- [ ] P-141 Implement saved remote machine/session profiles and a keyboard-accessible compact selector for narrow terminals. +- [ ] P-142 Implement agent title/token/cost/context usage fields only where the adapter/provider reports them, including freshness and unknown states. +- [ ] P-143 Implement pane/tab move, swap, rename, reorder, and persisted layout operations with stable identities. +- [ ] P-144 Implement configurable per-pane scrollback memory, persistence limits, search, and copy mode. +- [ ] P-145 Implement explicitly targeted agent message/send-keys/wait/fork actions with adapter capability checks and audit history. +- [ ] P-146 Implement prefix/mouse/link behavior contracts, including passthrough ownership and terminal size/focus control. +- [ ] P-147 Implement CLI browser `snapshot`, `click`, and `fill` equivalents only over captured, bounded page/DOM state. +- [ ] P-148 Implement element-to-agent design handoff that retains HTML/CSS/screenshot provenance and explicit selected file/image inputs. +- [ ] P-149 Implement account switching/usage display only through supported local provider interfaces, with confirmation and no secret collection. +- [ ] P-150 Implement full quick-open index across worktrees, files, agents, commands, and repository context with bounded indexing. +- [ ] P-151 Implement complete repo preview types and resource limits for Markdown, images, PDFs, and linked repository docs. +- [ ] P-152 Implement module panes/sidebar docks/startup hooks/widgets with order, lifecycle, bounds, and authority attribution. +- [ ] P-153 Reconcile every matrix row to an individual acceptance case, pinned upstream evidence, Rust test ID, or explicit unsupported disposition. +Gate: every additional current-documentation feature row has a per-feature acceptance disposition; no protocol or UI equivalent is inferred from a similarly named feature. + +### P15 — release, validation, and parity closure +- [ ] P-154 Package supported OS binaries with checksums, signatures, SBOM, and rollback. +- [ ] P-155 Run Rust fmt/clippy/unit/integration/fuzz/race-equivalent CI on the pinned toolchain. +- [ ] P-156 Run complete Linux/macOS/Windows build matrix on owned hosts. +- [ ] P-157 Run deterministic fixture demos without provider accounts. +- [ ] P-158 Run restart, multi-agent, SSH disconnect, schedule, lease, and merge-conflict E2E suites. +- [ ] P-159 Reconcile A01-A40 acceptance IDs to Rust tests and honest statuses. +- [ ] P-160 Audit all platform and provider capability claims against observed tests. +- [ ] P-161 Independently review process, PTY, remote, plugin, browser, crypto, and storage code. +- [ ] P-162 Audit source-copy provenance, license notices, third-party assets, and SBOM. +- [ ] P-163 Complete Go-to-Rust data migration/rollback rehearsal on copied state. +- [ ] P-164 Make final cutover only after Rust parity gates pass; do not auto-delete Go first. +- [ ] P-165 Publish migration/release notes listing tested features, partials, and remaining limits. +- [ ] P-166 Wire visible terminal-screen snapshots and OSC title/progress events from the TUI to the bounded Herdr evaluator; map its identity/state/visibility/skip result into Rover evidence and lifecycle authority without treating scrollback as live evidence. (In progress: terminal and TUI pass only the live viewport, bounded OSC title, and OSC 9;4 progress payloads across fragmented PTY chunks and BEL/ST terminators to all 22 audited direct executable mappings. Evidence is retained; same-agent transcript-viewer skip rules preserve the previous status and do not accumulate duplicate evidence, while an identity change resets to the new result. Lifecycle-source transport and visibility application remain open.) +- [ ] P-167 Port platform-owned foreground process-group enumeration and wrapper argv recognition for Linux/macOS; preserve bounded sampling, PID reuse protection, explicit Letta filtering, and unknown results where attribution is ambiguous. (In progress: the owned PTY now returns bounded, PID-stability-checked executable basenames from its foreground group on Linux and macOS through an additive session-frame field, and the TUI checks recognized group members after the leader while preserving leader priority and returning unknown for conflicting member identities. PTY/runtime and TUI tests pass on the current macOS host. Wrapper argv parsing, Letta-specific filtering, and Linux runtime verification remain open.) +Gate: feature matrix, acceptance matrix, license audit, migration, platform evidence, and release provenance all agree. + +## Three verification passes + +1. Source traceability: map every inventoried feature to a pinned upstream source path and observable acceptance case. Marketing-only statements stay flagged. +2. Architecture/security/license: check task ownership/order against Rover architecture/security; audit every imported file, copyright, license, dependency, authority boundary, and unsupported isolation claim. Unclear licenses block import. +3. Count/sequence/coverage: mechanically count unique task IDs (167), confirm dependencies and phase gates, map every existing Rover command/schema/acceptance item, map every competitor capability to a task or explicit CLI/TUI equivalent, and verify each gate is objectively testable. + +## Verification ledger + +- Pass 1: partial, updated 2026-09-25. `UPSTREAM_SOURCE_AUDIT.md` pins all three repository commits, root license hashes, detected nested notices, and broad capability source roots; the feature matrix maps documented groups to tasks and acceptance conditions. Per-feature source paths and shipped/optional/cloud claim classification remain outstanding; inventory completeness is not asserted. +- Pass 2: partial, updated 2026-09-25. Rover architecture/security invariants are cross-indexed in `RUST_COMPATIBILITY_BASELINE.md`; crate/process boundaries are recorded in ADR-0002. All listed upstream root/nested license hashes were checked against pinned blobs. The current Rust dependency set has a version/license snapshot and exact notice files in `RUST_DEPENDENCY_AUDIT.md` and `licenses/THIRD_PARTY_NOTICES.md`; one legacy Cargo `MIT/Apache-2.0` spelling is normalized to its reviewed SPDX alternative expression. A local locked audit verifies the license allowlist and exact bundled notice bytes, refreshes notices, and emits a CycloneDX 1.7 SBOM including bundled SQLite; the gates are wired into CI, with hosted execution pending. Vulnerability scanning remains pending. File-level upstream asset/attribution review also remains outstanding. No Herdr, Luvus, or Orca application source has been copied. +- Pass 3: mechanical checks passed on 2026-09-25. Confirmed 167 unique consecutive task definitions (P-001..P-167), all 102 distinct feature-matrix task references resolve, A01-A40 match between acceptance spec and implementation map, and all 23 referenced acceptance test files exist. This does not establish complete upstream feature coverage or close per-feature traceability. +- P-001 completed: `ADR-0001-rust-tui-product-boundary.md` records the accepted interface, runtime, SDK-removal, compatibility, and import boundaries. +- P-002 completed: `RUST_COMPATIBILITY_BASELINE.md` indexes CLI commands, serialized contracts, exit semantics, acceptance IDs, and security invariants. +- P-010 completed: `RUST_COMPATIBILITY_BASELINE.md` includes a package-level keep/port/remove disposition; physical SDK and Go removal remains gated by later parity tasks. +- P-003/P-004/P-006 have initial feature matrices and pinned broad source roots; all remain open until every feature has exact source evidence, an observable case, and shipped/optional/cloud classification. +- P-139..P-153 were added after a second source-to-plan coverage comparison exposed UHP, independent remote viewports, pane memory and operations, browser CLI actions, and further specifically documented capabilities not tracked at sufficient granularity. +- P-011 completed: `ADR-0002-rust-workspace-architecture.md` records crate boundaries and supervisor/client process ownership; dependency decisions remain separate gates. +- P-017 completed: Cargo workspace, lockfile, and `rover-core` crate created; `.gitignore` excludes Cargo build output. The current core dependencies are reviewed in ADR-0003 and the dependency audit. +- P-018 partially completed: `rust-toolchain.toml`, `make rust-check`, and `make rust-sbom` pin and check the 1.88.0 MSRV workspace gates locally and in CI. Local fmt, Clippy, unit, and dependency-audit checks pass. Rust integration/fuzz campaigns, cross-platform build jobs, and hosted CI confirmation remain open. +- P-014 completed: `ADR-0004-rust-sqlite-adapter.md` selects exact rusqlite 0.40.2 with only its `bundled` feature; MIT crate notices and the bundled SQLite blessing are retained and audited. +- P-021 completed: `rover-core` now has typed validated IDs, typed SHA-256 digests, typed UTC timestamps, compatible raw legacy ID generation, and 13 unit tests. `ADR-0003-core-identifiers-digests-and-time.md` records the dependency contract; `RUST_DEPENDENCY_AUDIT.md` inventories all 37 locked packages and their license metadata, and `licenses/THIRD_PARTY_NOTICES.md` retains their exact license/author files plus the SQLite public-domain notice. +- P-022 completed: `rover-store` schema migration tests prove transactional initial schema creation, rollback after an injected SQL failure, migration-ledger checksum and timestamp, idempotent re-entry, data-preserving adoption of the current Go v1 schema, and fail-closed behavior on future, malformed, altered, or unversioned schemas. This does not implement safe state-root opening, record transactions, Go-state import, or backup. +- P-062 advanced: ADR-0027 implements the bounded Herdr matcher and bundles all 22 source-hashed manifests with Apache-2.0 attribution. All 15 region selectors and all gate operators have regression coverage. Live TUI screen/OSC wiring and process-group/wrapper recognition remain open; no upstream application code was copied. +- Verification on 2026-09-25: `make rust-check CARGO='cargo +1.88.0'`, `GOWORK=off make check`, `GOWORK=off make race`, and `GOWORK=off make demo` (12 smoke scenarios) passed. The Herdr-focused agent, execution progress-capture, TUI detection, and transcript-viewer skip tests each passed three consecutive locked offline runs; latest workspace totals include 45 agent, 70 execution, and 48 TUI tests. These checks do not verify cross-platform runtime behavior or complete competitor parity. +- P-166 advanced: `transcript_viewer_skip_rule_preserves_the_last_screen_state` proves same-agent skip rules retain prior state/evidence without repeated-evidence growth and changed identity does not inherit stale state. The focused test passed three consecutive runs and the full Rust workspace check passed. +- P-167 advanced: the owned PTY now samples bounded foreground process-group member executable basenames on Linux and macOS and sends them in an additive optional `processes` session-frame field. Leader identity retains priority; conflicting recognized non-leader identities resolve to unknown. The group sampler, additive frame round trip, and both TUI selection tests passed three consecutive focused runs; full `make rust-check CARGO='cargo +1.88.0'` passed on the current macOS host. Linux runtime verification, wrapper argv recognition, and Letta filtering remain open. `env GOWORK=off make check`, `env GOWORK=off make race`, and `env GOWORK=off make demo` (12 scenarios) also passed in this turn. +- P-019 remains open for a Rust vulnerability scanner and hosted CI confirmation. +- No upstream implementation source has been copied. The Rust core now covers ID validation/generation, typed SHA-256 digests, and typed UTC timestamps; it does not provide serialized Rover records, CLI, TUI, or product feature parity. Continue P0 source audit; do not cross the source-reuse gate while a file-level review is incomplete. diff --git a/docs/design/UPSTREAM_FEATURE_MATRIX.md b/docs/design/UPSTREAM_FEATURE_MATRIX.md new file mode 100644 index 0000000..ad759bf --- /dev/null +++ b/docs/design/UPSTREAM_FEATURE_MATRIX.md @@ -0,0 +1,227 @@ +# Upstream feature-to-Rover task matrix + +Status: source and claim inventory in progress; reviewed 2026-09-26. Repository +trees below are read from the immutable commits in +`UPSTREAM_SOURCE_AUDIT.md`; the documented live sites remain mutable. The +register at the end maps capability groups to paths present in those trees and +separates repository evidence from documentation or availability claims. A path +in a tree proves only that source exists at that commit; it does not prove that +the source builds, ships, is enabled, or is reachable as a service. P-003, +P-004, and P-006 remain open until each row has an observable acceptance case +and its source/claim class has been reviewed. Source reuse separately requires +the exact-file license/attribution gate. + +Evidence labels used below: + +- `SRC`: path exists in the pinned Git tree; source behavior/build is not + independently asserted by this label. +- `DOC`: behavior is stated in the linked documentation or pinned README. +- `OPT`: documentation says the behavior is optional, configuration-gated, or + integration-dependent. +- `BETA` / `EXPERIMENTAL`: documentation explicitly marks maturity or outcome + as such. +- `CLOUD SRC` / `MOBILE SRC`: corresponding Orca source subtree exists; this + does not prove a currently hosted service or a store-distributed app works. +- `CLAIM ONLY`: README, website, or release links state availability/scope that + this source-tree inventory does not independently verify. + +For current Herdr and Luvus documentation reviewed on 2026-09-26, see their +[quick start](https://herdr.dev/docs/quick-start/), +[agent state documentation](https://herdr.dev/docs/agents/), +[restore documentation](https://herdr.dev/docs/session-state/), +[Luvus orchestration guide](https://luvus.dev/docs/guides/orchestration/), and +[module guide](https://luvus.dev/docs/extend/using-modules/). Those URLs are +live documentation, not immutable commit evidence. + +## Herdr + +Evidence: [quick start](https://herdr.dev/docs/quick-start/), +[agents](https://herdr.dev/docs/agents/), +[integrations](https://herdr.dev/docs/integrations/), +[session state and restore](https://herdr.dev/docs/session-state/), +[persistence and remote access](https://herdr.dev/docs/persistence-remote/), +[connecting machines](https://herdr.dev/docs/connecting-machines/), +[configuration](https://herdr.dev/docs/configuration/), +[plugins](https://herdr.dev/docs/plugins/), +[CLI reference](https://herdr.dev/docs/cli/), +[socket API](https://herdr.dev/docs/socket-api/). + +| Capability evidenced in current docs | Rover coverage task(s) | Acceptance condition to write before implementation | +|---|---|---| +| Persistent server/client session; client detach leaves pane processes alive; server stop ends them | P-042, P-045, P-047 | Detach/reconnect and explicit stop tests prove process/session ownership and restore outcome. | +| Project workspaces containing tabs, panes, and agents | P-046, P-051 | Create/list/switch/close operations preserve workspace isolation and ordering after restart. | +| Split panes, resizing, focus, zoom, popup/scratch layouts, prefix-mode navigation | P-046, P-049, P-053 | Keyboard-only golden interaction tests cover supported layouts and focus ownership. | +| Terminal mouse selection, context menu, links, wheel and mouse reporting | P-049, P-050, P-146 | Supported terminal input events have deterministic behavior; passthrough/input ownership is explicit; unsupported events are reported. | +| Direct attach to one agent/terminal; takeover semantics | P-048, P-049 | Concurrent-input ownership and explicit takeover are enforced and tested. | +| Scrollback copy mode, search, selection, export and history replay | P-044, P-047, P-050 | Bounded replay/search and safe rendering tests include hostile terminal sequences. | +| Session snapshot/restart restore and native agent session identity/resume | P-047, P-065 | Rover now persists explicit Codex/Claude session IDs bound to project and pane and can construct opt-in resume argv. It does not yet launch, restore after restart, integrate provider hooks, or support provider store discovery; see ADR-0024. | +| Agent detection by foreground process and screen manifests | P-062, P-166, P-167 | Rust has owned-PTY foreground-group leader sampling, exact executable evidence, and a bounded Herdr-format TOML/regex evaluator with all 22 pinned manifests and selector/operator tests. The attached TUI evaluates the live viewport, OSC title, and bounded OSC 9;4 progress payloads for audited direct executable mappings. Lifecycle-authority integration, process-group enumeration, and wrapper/child identification remain open. | +| Codex/Claude structured JSONL session events and provider usage claims | P-061, P-066, P-070 | Bounded fixtures prove event parsing and failure handling; provider claims remain distinct from verification evidence, and fixture results are not live-provider certification. | +| Lifecycle-hook authority, screen fallback, no double authority | P-063, P-064 | Rust currently has a per-pane authority registry with source sequence ordering, explicit release, and screen suppression; transport, event persistence, lifecycle expiry, and TUI integration remain open (ADR-0023). | +| State rollups across pane/tab/workspace and priority/unread behavior | P-051, P-063, P-125 | Rust now has pure pane/tab/workspace aggregation with explicit freshness, done-unseen input, state precedence, and blocked-pane IDs. Lifecycle-event persistence, viewed/unread transitions, and TUI display remain open; see ADR-0022. | +| Agent integration install/status/update/uninstall; local and remote manifests | P-069 | Preview and explicit apply/undo; updates are pinned and invalid manifests fail safely. | +| Agent explain/debug evidence and custom labels/metadata | P-062, P-067 | Explain output names evidence/rule/source and keeps semantic state distinct from display metadata. | +| Agent automation/socket API and direct control | P-067, P-068, P-091 | Rover can explicitly bind, list, inspect, and clear project-bound native session identities; attached TUI sessions can receive owned-PTY foreground samples and display known identity with unknown state. Live inventory, wait, prompt, read, and direct attach require lifecycle evidence and explicit pane input authority; protocol operations still need shared schemas, authorization, and audit records. | +| Worktree commands/provenance in workspace responses | P-035, P-036, P-101 | Worktree ancestry and dirty state remain visible and removal is guarded. | +| SSH remote sessions, saved machine profiles, reconnect and multi-machine visibility | P-095, P-096, P-097 | Host keys, session identity, reconnect and non-replayed input are covered on a real SSH fixture. | +| Themes, keybindings, sidebar/status configuration | P-054, P-126 | Strict schema, conflict resolution, atomic save and terminal color snapshots. | +| Plugins, event hooks and marketplace-delivered extensions | P-121, P-122, P-123 | Manifest, digest, authority disclosure and removal; unsupported sandbox claims absent. | +| Notifications and sound customization | P-059, P-125, P-128 | Rust currently captures terminal BEL and relays it to the host only for an unfocused pane. Configurable sound/notification policy, quiet hours, persistent unread actions and opt-out remain required; suppress active-pane noise. | +| Platform behavior, including Windows-specific support | P-043, P-060, P-156 | Each advertised target passes native build and terminal behavior tests on an owned host. | + +## Luvus + +Pinned source: `RizRiyz/luvus` at `63cb48263f347093f952dc43bbe0c2d468609567` +(see [source and license audit](UPSTREAM_SOURCE_AUDIT.md)). Evidence: +[README](https://github.com/RizRiyz/luvus), +[orchestration guide](https://luvus.dev/docs/guides/orchestration/), +[installation](https://luvus.dev/docs/getting-started/installation/), +[modules](https://luvus.dev/docs/extend/using-modules/), +[module authoring](https://luvus.dev/docs/extend/writing-modules/). + +| Capability evidenced in current docs | Rover coverage task(s) | Acceptance condition to write before implementation | +|---|---|---| +| Persistent panes/tabs/layout and server-client model | P-045, P-046, P-047, P-051 | Restart and reconnect restore stable IDs, layout, cwd and explicit child-process outcomes. | +| Live agent status sidebar across projects; jump to blocked agent | P-051, P-062, P-063, P-067 | State shows evidence/freshness and navigation lands on correct pane. | +| Native conversation resume where adapter supports it; fork a session | P-065, P-071 | Resume/fork is opt-in, identity-bound, and fails visibly when unsupported. | +| Worktree-isolated workers and explicit shared-workspace workers | P-035, P-072 | Isolation mode is visible; shared mode warns; dirty worktree cleanup is safe. | +| Task board, drafts, dependencies, status, retries/history | P-052, P-055, P-058, P-071 | Acceptance covers repository-bound drafts with title, path globs, dependencies, quality-gate text, and multiline prompt; saving a draft never dispatches it. rover-tasks persists project-bound plans, derives transitive prerequisite readiness, and records bounded manual attempt history. The TUI confirms draft-to-plan creation and lists plans, while keeping them separate from runnable tasks. Full task forms still require Manual/Now start choice, validated worker selection, process-backed execution, and gate evaluation. The current Rust TUI slice adds repository-scoped search and deterministic sort preferences. | +| Glob path leases and conflict prevention | P-073 | Atomic admission, overlap rules, expiry, owner check and crash recovery tests. | +| Quality gates with retained output and retries | P-074 | Required check status is strict, bounded, retained and never replaced by task success. | +| Serialized merge/integration gate | P-075, P-076 | Concurrent branch completions serialize; conflicts abort without guessed merge. | +| Schedules, one-shot and recurring automation, misfire handling | P-078 | Timezone/DST, idempotency, missed-run policy and restart tests. | +| Explicit reviewed unattended profiles and read/workspace/full access | P-079 | Policy decision is visible, scoped and logged; default is not elevated. | +| Git branches/commits/diffs and PR/issue integrations | P-101, P-102, P-103, P-104 | Read/write capabilities are distinct; external writes require explicit confirmation and reconciliation. | +| File tree, Git coloring, viewer, editor handoff and diffs | P-056, P-057, P-107 | Path containment, size bounds, atomic writes and dirty-change confirmation. | +| SSH remote attach and compact changed-cell transport | P-095, P-096 | Host identity and framing are authenticated; disconnect never replays terminal input. | +| Local socket/CLI with UI actions scriptable | P-016, P-067, P-091 | Every exposed action has a versioned schema and identical CLI/TUI authorization. | +| Modules in multiple languages through manifest/actions/hooks/panes/settings | P-121, P-122, P-123 | Versioned manifest, argument-safe launch, hook bounds, state ownership and honest unsandboxed notice. | +| Themes, remappable keys, sidebar docks, localization | P-054, P-126, P-127 | Keyboard reachability, persistent settings and translation-key completeness. | +| Notifications/unread and phone-sized remote view | P-051, P-125, P-095 | TUI inbox actions work; responsive behavior is represented by narrow terminal tests. | +| Named/moved/swapped/reordered pane and tab operations | P-143 | Stable IDs and layouts survive reordering and restart without changing the wrong session. | +| Agent-to-agent messaging, key input, semantic waits and supported session forks | P-145 | Explicit target and adapter capability are required; every input has an audit event. | +| Persistent per-pane scrollback memory and cross-history search | P-144 | Retention limits, restart restore and search scope are visible and tested. | +| Multi-client remote sessions with independent viewports | P-140, P-141 | View state is per client; only the input owner can write to a terminal. | +| Versioned Universal Harness Protocol (UHP) 1.0 method registry, IPC, snapshots, events and semantic waits | P-139 | Version negotiation, owner-only local IPC, bounded stream/cancellation and exact-input fixtures. | +| Agent titles, token/cost/context-use summaries | P-142 | Values appear only from a documented adapter/provider signal and include freshness/unknown behavior. | +| Module startup hooks, sidebar docks and status-bar widgets | P-152 | Lifecycle, bounds, order, config ownership and unsandboxed authority are visible. | +| License statements at the pinned commit: README and root LICENSE both identify Apache-2.0; earlier indexed snapshots differed | P-005, P-007, P-008 | Use only the pinned revision for review; preserve exact notices and audit each reused file/dependency before import. | + +## Orca ADE + +Evidence: [repository README](https://github.com/stablyai/orca), +[CLI overview](https://github.com/stablyai/orca/blob/main/docs/site/content/docs/cli/overview.mdx), +[SSH docs](https://www.onorca.dev/docs/ssh), +[remote worktrees recipe](https://www.onorca.dev/docs/recipes/remote-worktrees), +[notifications](https://www.onorca.dev/docs/notifications), +[mobile readme](https://github.com/stablyai/orca/blob/main/mobile/README.md). +Current repository root LICENSE says MIT; each copied asset/dependency still +requires a separate audit. + +| Capability evidenced in current docs/repository | Rover coverage task(s) | CLI/TUI acceptance condition to write before implementation | +|---|---|---| +| Parallel agents in isolated Git worktrees; fan-out and result comparison | P-035, P-072, P-077 | N-way runs retain separate identities; comparison is inspectable and winner is explicitly selected. | +| Terminal splits, persistent terminals, scrollback | P-043, P-044, P-045, P-046, P-047 | Pane lifecycle and restore are deterministic across client/server restart tests. | +| Task/agent board, profiles, state and account/usage visibility | P-051, P-061, P-063, P-129, P-130 | Rust `rover-agents` now exposes the four declared Go profiles through read-only CLI listing/lookup and fails closed on unknown names. Process launch, TUI selection, state, provider account, and usage visibility remain open; only provider-documented values appear and account switching must be explicit and confirmed. | +| Editor/file browser, image/file handoff, diff comments | P-056, P-057, P-105, P-108 | File handling is bounded and private; annotations bind to exact candidate paths/hashes. | +| GitHub/Linear issues, PRs, boards and CI | P-103, P-104, P-106 | Provider fixtures prove contracts only; real writes are opt-in, confirmed and reconciled. | +| Conflict review, branch/PR status tracking | P-075, P-106 | No automatic merge; conflict evidence and final status bind to branch/base identities. | +| Markdown, image and PDF previews | P-107 | Parsing is size-bounded and hostile content cannot emit terminal control or execute. | +| Embedded browser and dev server | P-111, P-112 | Only owned processes are terminated; allowed origins and ports are explicit. | +| DOM/accessibility inspection, design mode, computer-use controls | P-113, P-114, P-115, P-116, P-117, P-118 | Snapshot provenance is captured; interaction requires explicit authority and audit; no pixel-mode claim from textual output. | +| Local, SSH, self-hosted, ephemeral remote runtime and reconnect | P-094, P-095, P-096, P-097, P-098, P-099 | Each adapter is functional, host-authenticated, capability-reported and tested under disconnect. | +| Port forwarding and local browser to remote dev server | P-098 | Endpoint is user-selected; bind target is visible; no automatic public exposure. | +| Mobile companion over paired desktop runtime | P-095, P-096, P-119 | CLI/TUI remote access provides equivalent supported actions; no mobile app/API is added to Rover. | +| Persistent notification inbox and unread state | P-125 | Inbox survives restart, supports unread/snooze/jump, and records source timestamps. | +| CLI scripting for worktrees, terminals, browser and UI | P-016, P-091, P-102, P-112, P-116, P-131 | Commands have stable schemas, dry-run/confirmation where needed, and TUI parity. | +| Schedules, artifacts and skills | P-078, P-088, P-124 | Schedules are idempotent; artifacts are content-addressed; skills retain provenance and safe rendering. | +| Desktop/mobile/cloud/Electron/React functionality in upstream source | P-006, P-009, P-111..P-120 | Include only behavior with a tested CLI/TUI equivalent; do not copy unsupported UI/runtime code into the Rust product. | +| Browser CLI `snapshot`, `click`, `fill`, design-mode HTML/CSS and cropped screenshot handoff | P-147, P-148 | Actions operate only on a bounded captured page; explicit element/source/artifact IDs are retained. | +| Quick open across files, worktrees, agents, commands and repository context | P-150 | Search is bounded, deterministic for identical input and respects project boundaries. | +| Account switcher and rate-limit reset/usage tracking | P-149 | Read only supported local provider state, require confirmation to switch, and never persist credentials. | +| Rich Markdown/image/PDF and repository document previews | P-151 | Size/type bounds, terminal-safe rendering, and explicit unavailable preview errors. | + +## Traceability rule + +Each table row is a scope statement, not evidence that Rover already implements +it. Tasks remain pending until the Rust behavior, a focused regression/contract +test, the plan ledger, and capability output agree. Exact per-file upstream +provenance and license records are separate gates from feature behavior. + +## Pinned source-path and claim register + +These anchors are exact paths at the commits in `UPSTREAM_SOURCE_AUDIT.md`. +Several anchors are directories because a capability spans multiple modules; +they are mapping evidence, not authorization to copy the directory. The +file-level reuse gate still requires each reused file's blob digest and notice +review. On 2026-09-26, every listed anchor was checked against `git ls-tree` +for its pinned commit; all listed paths were present. This check proves path +existence only, not implementation semantics, build status, release inclusion, +or live service availability. + +### Herdr + +| Capability rows above | Pinned source anchors | Evidence class and qualification | +|---|---|---| +| Persistent server/client sessions; workspaces, tabs, panes and layout | `src/server/`, `src/client/attach.rs`, `src/workspace.rs`, `src/workspace/aggregate.rs`, `src/workspace/tab.rs`, `src/persist/restore.rs`, `src/persist/snapshot.rs` | `SRC + DOC`. Live docs distinguish detach (processes continue) from server restart (processes stop, layout may restore). | +| Split, resize, focus, zoom, popup, keyboard/mouse and copy/search | `src/app/api/panes.rs`, `src/input/keybindings.rs`, `src/input/mouse.rs`, `src/selection.rs`, `src/copy_mode.rs`, `src/client/clipboard_forwarding.rs` | `SRC + DOC`. Mouse passthrough and platform-specific link gestures are terminal/config dependent. | +| Direct terminal/agent attach and input takeover | `src/client/attach.rs`, `src/server/terminal_attach.rs`, `src/input/lease.rs`, `src/cli/agent.rs` | `SRC + DOC`. Takeover is an explicit `--takeover` operation; single-writer/input ownership is part of the acceptance contract. | +| Session screen history and native-agent restore | `src/terminal/history_read.rs`, `src/persist/snapshot.rs`, `src/agent_resume.rs`, `src/app/agent_resume.rs` | `SRC + DOC + OPT`. Pane history is documented off by default; native resume requires integration-reported session identity and supported agent/integration versions. | +| Update handoff and process detection fallback | `src/handoff_runtime.rs`, `src/server/autodetect.rs`, `src/pane/agent_detection.rs`, `src/detect/manifest.rs` | `SRC + DOC + EXPERIMENTAL`. Handoff is documented best-effort; child-process-group detection is an opt-in fallback with possible foreground-process misclassification. | +| Agent lifecycle hooks, manifests, explain output and state rollups | `src/integration/registry.rs`, `src/integration/actions.rs`, `src/detect/manifest_update.rs`, `src/agent_view_eval.rs`, `src/workspace/aggregate.rs`, `src/ui/sidebar.rs` | `SRC + DOC`. Docs describe one lifecycle authority per pane, screen fallback, and a strict known-visible-prompt rule for `blocked`; unseen prompts may be shown `idle`. | +| Integration install/status/update and custom agent metadata | `src/cli/integration.rs`, `src/integration/`, `src/cli/agent.rs`, `src/metadata_tokens.rs` | `SRC + DOC`. Remote manifest updates, local overrides, and display-only labels have separate semantics; display metadata is not lifecycle state. | +| Agent automation, socket/API and wait/control operations | `src/api/client.rs`, `src/api/server.rs`, `src/api/schema/`, `src/api/wait.rs`, `src/cli/api.rs`, `src/cli/agent.rs` | `SRC + DOC`. Each protocol action still needs exact authorization and side-effect classification in Rover. | +| Worktree provenance and machine/SSH sessions | `src/worktree.rs`, `src/cli/worktree.rs`, `src/remote/attach.rs`, `src/remote/host.rs`, `src/remote/saved.rs`, `src/cli/machine.rs` | `SRC + DOC`. Host identity, reconnect, and input replay behavior require separate acceptance tests. | +| Themes, keybindings, sidebar, notifications and sound | `src/config/theme.rs`, `src/config/keybinds.rs`, `src/config/sidebar.rs`, `src/config/sound.rs`, `src/server/notifications.rs`, `src/client/notifications.rs`, `src/terminal_notify.rs` | `SRC + DOC + OPT`. User configuration and notification opt-outs must be represented explicitly. | +| Plugins, commands, platform PTYs and Windows support | `src/plugin_command.rs`, `src/plugin_paths.rs`, `src/persist/plugin_registry.rs`, `src/pty/backend.rs`, `src/platform/`, `src/cli/plugin.rs` | `SRC + DOC`. Repository presence does not establish a plugin's trust isolation or native behavior on every advertised OS. | + +### Luvus + +| Capability rows above | Pinned source anchors | Evidence class and qualification | +|---|---|---| +| Persistent terminal sessions, panes, tabs and workspace UI | `src/session.rs`, `src/terminal/`, `src/ui/panes.rs`, `src/layout.rs`, `src/persist.rs`, `src/app/persistence.rs` | `SRC + DOC`. Session restore and persistence behavior must be checked against the pinned source, not inferred from the live site alone. | +| Agent detection, state, usage, native session adapters and messaging | `src/agent/registry.rs`, `src/agent/types.rs`, `src/agent/usage.rs`, `src/agent/*/sessions.rs`, `src/app/dispatch/agents.rs`, `src/app/mission.rs` | `SRC + DOC`. Per-agent resume/state is adapter-dependent; generic process support does not prove native resume for every agent. | +| Task board, task drafts, dependencies, retries and worker start | `src/ui/board.rs`, `src/app/board.rs`, `src/app/mission.rs`, `src/orch/mod.rs`, `src/orch/worker.rs`, `src/app/dispatch/orchestration.rs` | `SRC + DOC`. The current orchestration guide specifies project binding, worker modes, task transitions and restart behavior. | +| Path leases, quality gates and serialized integration/merge | `src/app/dispatch/orchestration.rs`, `src/app/dispatch/tests/orchestration.rs`, `src/orch/worker.rs`, `src/worktree.rs`, `src/git/local.rs`, `src/git/github.rs` | `SRC + DOC`. Lease overlap, gate failure, merge conflict and interrupted integration are distinct states, not task success. | +| Scheduled/recurring agent automations and access profiles | `src/automation/model.rs`, `src/automation/persist.rs`, `src/automation/schedule.rs`, `src/automation/worker.rs`, `src/app/automation.rs`, `src/app/automation_persistence.rs` | `SRC + DOC + BETA`. The guide labels the board's Automations view beta; misfire/restart rules and access profiles require independent contract tests. | +| Git, PR review, diffs, files, previews and search | `src/git/`, `src/diff/`, `src/files/`, `src/search/`, `src/ui/git.rs`, `src/ui/diff.rs`, `src/ui/files.rs`, `src/ui/preview.rs`, `src/ui/search.rs` | `SRC + DOC`. Preview parser/security behavior is separate from the fact that a preview UI exists. | +| Remote sessions and multiple machines | `src/machine/ssh.rs`, `src/machine/catalog.rs`, `src/machine/recovery.rs`, `src/machine/api.rs`, `src/terminal/pty/` | `SRC + DOC`. Authentication, host-key policy, reconnect and remote input semantics remain separate acceptance areas. | +| Scriptable CLI, local API and Universal Harness Protocol | `src/cli.rs`, `src/api/`, `src/uhp/`, `protocol/` | `SRC + DOC`. Protocol method/version and authorization claims must be matched to exact schemas before Rover advertises parity. | +| Language-neutral modules, manifest, install, actions, hooks and settings | `src/module/`, `src/app/modules.rs`, `src/app/dispatch/extensions.rs`, `plugins/` | `SRC + DOC`. The current docs explicitly say modules are ordinary programs with no SDK and no plugin runtime; install confirmation can be skipped with `--yes`, so code execution is not sandboxed by that prompt. No marketplace registry is described. | +| Themes, localization, status bar and configuration | `src/theme/`, `src/i18n/`, `src/bar/`, `src/ui/settings.rs`, `src/config.rs` | `SRC + DOC`. Built-in/theme assets also have nested notices recorded in the source audit. | +| Mobile-sized, web and remote-view surfaces | `src/ui/mobile/`, `src/web/`, `src/machine/` | `SRC + DOC`. These are source/documentation indicators; Rover's deliverable maps supported behavior to CLI/TUI and does not add a web or mobile application. | + +### Orca ADE + +| Capability rows above | Pinned source anchors | Evidence class and qualification | +|---|---|---| +| CLI and local scripting contracts | `src/cli/`, `src/shared/rpc-contract/` | `SRC + DOC`. The pinned README advertises CLI commands including worktree creation and browser snapshot/click/fill; command contract and side effect must be reviewed per command. | +| Persistent terminals, daemon, PTY and terminal rendering | `src/main/daemon/`, `src/main/pty/`, `src/main/ghostty/`, `src/renderer/src/components/terminal/` | `SRC + DOC`. The README's “infinite splits” and restart-surviving scrollback are product claims, not verified here as unbounded capacity or a Rover guarantee. | +| Agent launch, account/usage and task orchestration | `src/main/agent-launch/`, `src/main/claude-accounts/`, `src/main/claude-usage/`, `src/main/codex-accounts/`, `src/main/codex-usage/`, `src/main/automations/` | `SRC + DOC`. Provider-specific account and usage data cannot be generalized to arbitrary agents. | +| Worktrees, GitHub/Linear/GitLab and review annotations | `src/main/git/`, `src/main/ipc/worktrees/`, `src/main/github/`, `src/main/linear/`, `src/main/gitlab/`, `src/renderer/src/components/editor/` | `SRC + DOC`. External provider writes and candidate-bound comments are independent operations requiring their own Rover contracts. | +| File tree, editor handoff, diffs and document previews | `src/main/ipc/filesystem-list-files.ts`, `src/main/ipc/filesystem-mutations.ts`, `src/main/file-transaction-lock.ts`, `src/renderer/src/components/editor/`, `src/main/artifacts/` | `SRC + DOC`. README drag/drop/autosave statements are GUI claims; Rover acceptance maps these to explicit CLI/TUI paths and safe edit/write behavior. | +| Browser, DOM/design inspection and computer-use controls | `src/main/browser/`, `src/main/computer/`, `src/shared/rpc-contract/browser-params.ts`, `src/shared/rpc-contract/computer-params.ts`, `src/cli/handlers/browser-*`, `src/cli/handlers/computer.ts` | `SRC + DOC`. Browser and computer actions require captured-target provenance, explicit authority and auditable effects. | +| SSH, self-hosted/ephemeral runtime, reconnect and port forwarding | `src/main/ssh/`, `src/main/ephemeral-vm-runtime-service.ts`, `src/main/host/`, `src/cli/runtime/` | `SRC + DOC`. These are code paths in an application repository; their presence does not establish a live remote service or provider certification. | +| Notifications, scheduling, artifacts, skills and plugins | `src/main/notifications/`, `src/main/automations/`, `src/main/artifacts/`, `src/main/plugins/`, `src/cli/handlers/automations.ts`, `src/cli/handlers/artifacts.ts`, `src/cli/handlers/skills.ts` | `SRC + DOC`. Inbox state, schedule durability, artifact provenance and extension authority are separate parity contracts. | +| Cloud relay/service and paired mobile companion | `cloud/`, `mobile/`, `src/main/ssh/`, `src/shared/rpc-contract/ssh-params.ts` | `CLOUD SRC + MOBILE SRC + CLAIM ONLY`. The pinned README links iOS/TestFlight/Android distribution and identifies `cloud/` as the pairing relay; source presence/README links do not prove current store availability or hosted-service uptime. Rover maps needed remote-access behavior to CLI/TUI only. | +| Desktop/mobile renderer and native computer-use areas | `src/renderer/`, `mobile/`, `native/`, `src/main/computer/` | `SRC + MOBILE SRC`. These are upstream implementation areas to study for behavior; they are not Rover product surfaces under the CLI/TUI-only decision. | + +### Claim disposition + +- `SRC` is repository-content evidence only. We have not built or run the three + upstream products from these snapshots as part of this inventory. +- `DOC` records a published claim and links its source. Live Herdr/Luvus web + documentation can change independently of the pinned commit; it must not be + treated as proof of the exact pinned implementation without a path-level + source check. +- `OPT`, `BETA`, and `EXPERIMENTAL` preserve the upstream qualification in the + parity requirements. Rover must report its own capability state and must not + turn these into unconditional guarantees. +- Orca's repository includes `cloud/`, `mobile/`, and `native/` source roots, + while its README also links external distribution artifacts and describes + broad compatibility claims. This inventory establishes those source roots + and README statements only; it does not verify current stores, services, + external integrations, or “any agent” compatibility. +- No `CLAIM ONLY` item becomes a Rover acceptance requirement until a CLI/TUI + observable behavior and testable contract are written. No source file is + authorized for reuse by this register. diff --git a/docs/design/UPSTREAM_SOURCE_AUDIT.md b/docs/design/UPSTREAM_SOURCE_AUDIT.md new file mode 100644 index 0000000..7f66579 --- /dev/null +++ b/docs/design/UPSTREAM_SOURCE_AUDIT.md @@ -0,0 +1,125 @@ +# Pinned upstream source and license audit + +Audit snapshot date: 2026-09-25. These are exact `origin/HEAD` commits fetched +from the public repositories. The working snapshots are kept outside Rover at +`/private/tmp/rover-upstream-audit-{herdr,luvus,orca}` with `--no-checkout`; +no upstream repository was modified. No Herdr, Luvus, or Orca application code +has been copied. The 22 Herdr TOML detection manifests listed below are copied +unchanged as data, with the exact root license and file-level hashes retained. + +## Immutable source revisions + +| Project | Repository | Commit | Snapshot root license | Same-commit README statement | +|---|---|---|---|---| +| Herdr | [herdrdev/herdr](https://github.com/herdrdev/herdr) | `c411883ec639c9893ed9c33021c890485e91727b` | Apache-2.0, `LICENSE`, SHA-256 `c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4` | Apache-2.0 | +| Luvus | [RizRiyz/luvus](https://github.com/RizRiyz/luvus) | `63cb48263f347093f952dc43bbe0c2d468609567` | Apache-2.0, `LICENSE`, SHA-256 `c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4` | Apache-2.0 | +| Orca ADE | [stablyai/orca](https://github.com/stablyai/orca) | `2c2414be5780ea6bf4cf058e8f4dda48d0d0d3b1` | MIT, `LICENSE`, SHA-256 `ff1b611f80580d49f4b97e93a97b24eb050b0671b26b8afe16341fab699112f3` | MIT | + +The earlier Luvus README/LICENSE conflict is **not present at the pinned +commit above**: both files at `63cb482…` state Apache-2.0. This conclusion is +revision-specific; it does not reinterpret older revisions or releases. +Herdr's older AGPL release boundary also does not change the license of this +current pinned commit. Only source from the recorded revisions can be considered +for reuse; changing a pin requires rerunning this audit. + +## Detected nested license and attribution files + +SHA-256 digests below identify the exact files in the pinned tree. A root +license does not replace a nested third-party license or notice. + +### Herdr + +| Path | SHA-256 | First-line identification | +|---|---|---| +| `packaging/windows/licenses/Microsoft.Windows.Console.ConPTY-LICENSE.txt` | `5d177f23ecfeb0ea8e050b6a5a16355e1ae9a0b286436ca8f83ed08b3795be6b` | Microsoft copyright | +| `packaging/windows/licenses/Microsoft.Windows.Console.ConPTY-NOTICE.md` | `e7fbaadee6ab20c28b87730a510ee5f5815d8fb4bd88d1d54d282dc2a74c0726` | Windows Console ConPTY notices | +| `vendor/libghostty-vt/LICENSE` | `386211873e5b7a02f663ae4d7adf96285999f91608f8f9f31fecfd0f4095e6f1` | MIT License | +| `vendor/libghostty-vt/pkg/afl++/LICENSE` | `337a40d58de145036691382b3aec220ee28f507fe4f0db8970b6844f6dd60eb3` | zig-afl-kit-derived AFL material | +| `vendor/portable-pty/LICENSE.md` | `191c46fcf52061382b1c51a70311eb9081381cc158e5899f3739473a9432185b` | MIT License | + +### Luvus + +| Path | SHA-256 | First-line identification | +|---|---|---| +| `src/theme/paper/NOTICE` | `652490143a19dc5c9b2290ea290717dd5a47caa0da1e91643bb19b5f21f32128` | Paper palette adaptation attribution | +| `src/theme/papercolor/LICENSE` | `e337357fa410de84aa3f35490af0b2d0541ac44774876f7056140158a15f924e` | MIT License | +| `src/theme/rose-pine/LICENSE` | `fb2535bcb42729f0547691eea7feee12d6870d184d704a7a929513973974d7bd` | MIT License | +| `src/theme/tokyo-night/LICENSE` | `358ef84dd5b689c6c6d96cb0630661e61cf20b88749694905457230d56f57b0b` | MIT License | +| `vendor/alacritty_terminal/LICENSE-APACHE` | `8884289e3969c089cd556c53fa1d3f51d86b29e52a8c1d660a55ce8292986910` | Apache-2.0 | +| `vendor/vte/LICENSE-APACHE` | `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a` | Apache-2.0 | +| `vendor/vte/LICENSE-MIT` | `e4c9b06fa850cb9b540a5e400e9f6394cf15efcf4098144de477d1d3dae10150` | MIT license | + +### Orca ADE + +| Path | SHA-256 | First-line identification | +|---|---|---| +| `docs/site/THIRD_PARTY_NOTICES.md` | `1e039dc5567ff96b755c80a384dd626a00a0c46c49e9994c86cb599efcc435fd` | Third-party notices | +| `mobile/packages/expo-two-way-audio/LICENSE` | `bc32465e1384fbea714ddf43e4849aae2e3662ab8a7c8c4a35a1bf41c7b5ad81` | MIT License | + +The vendored `pkg/afl++/LICENSE` text, every attribution entry, repository +assets/fonts/themes, and all lockfile-resolved dependency licenses still need a +file-level disposition before corresponding material is reused. This table is +a detected root/nested notice inventory, not a complete dependency SBOM. + +## Herdr manifest data bundled by Rover + +These TOML files are byte-identical to `src/detect/manifests/` at the pinned +Herdr commit in the immutable revision table. Rover includes them under +`crates/rover-agents/src/herdr_manifests/`; their behavior is evaluated by +Rover's separate Rust implementation. The source commit root declares +Apache-2.0. Rover retains that exact `LICENSE` at +`licenses/upstream/herdr/LICENSE` (SHA-256 +`c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4`). +No manifest contains a separate copyright or license header. + +| Pinned source path | Rover path | SHA-256 | +|---|---|---| +| `src/detect/manifests/amp.toml` | `crates/rover-agents/src/herdr_manifests/amp.toml` | `b5806b0dd21e2f5e752d0eac7f084d5ce2e3f275638cfd1234c42cc88152dc8c` | +| `src/detect/manifests/antigravity.toml` | `crates/rover-agents/src/herdr_manifests/antigravity.toml` | `11300b853130d037eb2c57d9c4b897893cc1f0a876e12eb54ff1b216177db9d7` | +| `src/detect/manifests/claude.toml` | `crates/rover-agents/src/herdr_manifests/claude.toml` | `038d0aa23fee3f9b39cb3c9ca117d0f95b0b3a5873cf0f38284ccbac279c9664` | +| `src/detect/manifests/cline.toml` | `crates/rover-agents/src/herdr_manifests/cline.toml` | `75fe33ec735c59638da8d62e16bddd257d9959e692edb83b1f11b7f28057866a` | +| `src/detect/manifests/codex.toml` | `crates/rover-agents/src/herdr_manifests/codex.toml` | `aba3d44a0f7177d5dd9fd60002a8efd11ef6e1a589bb3992c02563ed3e0de5e0` | +| `src/detect/manifests/cursor.toml` | `crates/rover-agents/src/herdr_manifests/cursor.toml` | `753b1f7f632d42fa21139c2767ecbb5e1078748aba2e59407ac4932d3ce36ad7` | +| `src/detect/manifests/devin.toml` | `crates/rover-agents/src/herdr_manifests/devin.toml` | `250c9cea1d60bdb965dc6056f3066b785e941d60242756fbaca73b57ca6b0f85` | +| `src/detect/manifests/droid.toml` | `crates/rover-agents/src/herdr_manifests/droid.toml` | `d37e7c464177c0e8f3edce8d4fabc4bcc7a1874edf2c4c87a2f927888cf69ce9` | +| `src/detect/manifests/gemini.toml` | `crates/rover-agents/src/herdr_manifests/gemini.toml` | `d7013b5e772852ecc595febf964f00b4f9edcbc6047a2a5421613a154d92520d` | +| `src/detect/manifests/github-copilot.toml` | `crates/rover-agents/src/herdr_manifests/github-copilot.toml` | `b70c652584326a1a98475a5fcef16207dee9f23b65f8e78300f4fc2bb578cb11` | +| `src/detect/manifests/grok.toml` | `crates/rover-agents/src/herdr_manifests/grok.toml` | `4298dd2ea7d124f3ba5d72b649bbb4fb6edc533d7e6e546d523a8214fa31456d` | +| `src/detect/manifests/hermes.toml` | `crates/rover-agents/src/herdr_manifests/hermes.toml` | `533d21b65dea3a0c60c25d0475c9c900d28a5712b6392669a7788f75de2b6e85` | +| `src/detect/manifests/kilo.toml` | `crates/rover-agents/src/herdr_manifests/kilo.toml` | `70f0ba4e58bc141fe69d7024013f973cd16e8616393079318914d70afeefef3b` | +| `src/detect/manifests/kimi.toml` | `crates/rover-agents/src/herdr_manifests/kimi.toml` | `ede08c0d2d5024f7606dc0a1b2f7a9c6a0ebb99f3b6c58ca6757049856d06e05` | +| `src/detect/manifests/kiro.toml` | `crates/rover-agents/src/herdr_manifests/kiro.toml` | `c8990f3c9d4810995be97e8df29836411e37d90f6ac9d9303f505787b504806b` | +| `src/detect/manifests/letta.toml` | `crates/rover-agents/src/herdr_manifests/letta.toml` | `205b8c135584c86f9f529aa2d061109b5129085cc5c3124c6dbfe0e78fcdba43` | +| `src/detect/manifests/maki.toml` | `crates/rover-agents/src/herdr_manifests/maki.toml` | `3b392170ee3082051266509f575a4640bde8d693b2f37ecf52157a641bc75b28` | +| `src/detect/manifests/muse.toml` | `crates/rover-agents/src/herdr_manifests/muse.toml` | `b69c4d87fa9c19e3e6453b706fbe39c98a8b33ffbaa48e8cd5ae6751e9615074` | +| `src/detect/manifests/opencode.toml` | `crates/rover-agents/src/herdr_manifests/opencode.toml` | `faa82aed2d76ad856528caae04232894594b74cc903aed3e522b3e725c5f2c95` | +| `src/detect/manifests/pi.toml` | `crates/rover-agents/src/herdr_manifests/pi.toml` | `57469b82e4239bf93559ed5d400c9d9f86a9e64d5def728b7d4bb398be7659ca` | +| `src/detect/manifests/qodercli.toml` | `crates/rover-agents/src/herdr_manifests/qodercli.toml` | `2089f70fc78c6576fd7128a00f4e7eedb85be81bde9ed73301b3b88000048961` | +| `src/detect/manifests/qwen.toml` | `crates/rover-agents/src/herdr_manifests/qwen.toml` | `b27aa456af228e8a4ceac74f0dd431c33b21473b69314fc672a7fba474e2a7fe` | + +## Pinned source-to-capability roots + +Paths are relative to the pinned commit above. These are the implementation +areas to inspect for behavior. They are not blanket approval to copy every file. + +| Upstream | CLI/protocol | Session/terminal | Agents/orchestration | Git/remote/files | Extensions/settings | +|---|---|---|---|---|---| +| Herdr | `src/cli/`, `src/api/`, `src/protocol/` | `src/server/`, `src/client/`, `src/workspace/`, `src/pane/`, `src/terminal/`, `src/pty/`, `src/persist/` | `src/detect/`, `src/integration/`, `src/agent_resume.rs`, `src/agent_view_eval.rs` | `src/remote/`, `src/workspace/` | `src/plugin_command.rs`, `src/plugin_paths.rs`, `src/config/`, `src/ui/`, `src/input/` | +| Luvus | `src/app/`, `src/ipc/`, `src/api/`, `src/uhp/` | `src/terminal/`, `src/platform/`, `src/ui/` | `src/agent/`, `src/mission/`, `src/orch/`, `src/automation/` | `src/git/`, `src/machine/`, `src/files/`, `src/search/`, `src/diff/` | `src/module/`, `src/theme/`, `src/i18n/`, `src/bar/` | +| Orca ADE | `src/cli/`, `src/shared/rpc-contract/` | `src/main/daemon/`, `src/main/pty/`, `src/main/ghostty/`, `src/renderer/src/components/terminal/` | `src/main/agent-launch/`, provider directories under `src/main/`, `src/main/automations/` | `src/main/git/`, `src/main/ssh/`, `src/main/browser/`, `src/main/ipc/worktrees/`, `src/renderer/src/components/editor/` | `src/main/plugins/`, `src/main/keybindings/`, `src/main/notifications/`, `src/shared/plugins/` | + +Orca also has separate `mobile/`, `cloud/`, `native/`, and Electron renderer +areas. Their behavior is in the parity audit because the user requested all +features, but Rover's deliverable remains Rust CLI/TUI: port the behavior into +those surfaces; do not add an Orca-style GUI/mobile/cloud application to Rover. + +## Reuse gate + +User clarified: port all behavior into Rust CLI/TUI and reuse source only when +its exact path, digest, license, copyright/NOTICE, and relevant acceptance test +have been audited. The manifest data above is the first audited reuse; no +upstream application code has been copied into Rover. Before further reuse, +record the exact commit/path/blob digest, transformation notice, license +obligations, nested dependency/asset notices, and the test that proves the +resulting Rust behavior. Implement the capability independently if that +evidence is incomplete. From 032d0e897ddfc4ae159bb80c0a161c7041895e1b Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:41:32 +0530 Subject: [PATCH 03/23] build(rust): add dependency license audit, notice bundle and SBOM tool scripts/rust_dependency_audit.py checks every locked crates.io package against its SPDX expression and the exact license/notice files bundled under licenses/third-party, and emits a CycloneDX SBOM of the packages active on the supported targets. licenses/upstream/herdr holds the Apache-2.0 license for the reused Herdr detection manifests, whose hashes scripts/test_herdr_manifest_audit.py checks against the upstream audit. .gitattributes marks the bundled notices and Herdr manifests -text. Nine upstream notices use CRLF and THIRD_PARTY_NOTICES.md pins their exact SHA-256, so any EOL normalisation (for example core.autocrlf=input) would store LF blobs and make the audit fail on every fresh clone. Co-Authored-By: Claude Opus 5.5 --- .gitattributes | 5 + licenses/THIRD_PARTY_NOTICES.md | 196 ++++++ .../third-party/aho-corasick-1.1.5/COPYING | 3 + .../aho-corasick-1.1.5/LICENSE-MIT | 21 + .../third-party/aho-corasick-1.1.5/UNLICENSE | 24 + .../alacritty_terminal-0.26.0/LICENSE-APACHE | 201 ++++++ .../allocator-api2-0.2.21/LICENSE-APACHE | 176 +++++ .../allocator-api2-0.2.21/LICENSE-MIT | 23 + .../third-party/anyhow-1.0.104/LICENSE-APACHE | 176 +++++ .../third-party/anyhow-1.0.104/LICENSE-MIT | 23 + licenses/third-party/approx-0.5.1/LICENSE | 202 ++++++ .../ar_archive_writer-0.5.3/LICENSE.txt | 219 ++++++ .../third-party/arrayvec-0.7.8/LICENSE-APACHE | 201 ++++++ .../third-party/arrayvec-0.7.8/LICENSE-MIT | 25 + .../atomic-waker-1.1.2/LICENSE-APACHE | 201 ++++++ .../atomic-waker-1.1.2/LICENSE-MIT | 23 + .../third-party/autocfg-1.5.1/LICENSE-APACHE | 201 ++++++ .../third-party/autocfg-1.5.1/LICENSE-MIT | 25 + .../third-party/base64-0.22.1/LICENSE-APACHE | 201 ++++++ .../third-party/base64-0.22.1/LICENSE-MIT | 21 + .../third-party/bitflags-1.3.2/LICENSE-APACHE | 201 ++++++ .../third-party/bitflags-1.3.2/LICENSE-MIT | 25 + .../bitflags-2.13.2/LICENSE-APACHE | 201 ++++++ .../third-party/bitflags-2.13.2/LICENSE-MIT | 25 + .../block-buffer-0.12.1/LICENSE-APACHE | 201 ++++++ .../block-buffer-0.12.1/LICENSE-MIT | 25 + .../by_address-1.2.1/LICENSE-APACHE | 201 ++++++ .../third-party/by_address-1.2.1/LICENSE-MIT | 25 + licenses/third-party/castaway-0.2.4/LICENSE | 21 + licenses/third-party/cc-1.4.7/LICENSE-APACHE | 201 ++++++ licenses/third-party/cc-1.4.7/LICENSE-MIT | 25 + .../third-party/cfg-if-1.0.5/LICENSE-APACHE | 201 ++++++ licenses/third-party/cfg-if-1.0.5/LICENSE-MIT | 25 + .../third-party/cfg_aliases-0.1.1/LICENSE | 9 + .../third-party/compact_str-0.9.1/LICENSE | 21 + .../concurrent-queue-2.5.0/LICENSE-APACHE | 201 ++++++ .../concurrent-queue-2.5.0/LICENSE-MIT | 23 + .../const-oid-0.10.2/LICENSE-APACHE | 201 ++++++ .../third-party/const-oid-0.10.2/LICENSE-MIT | 25 + .../third-party/convert_case-0.10.0/LICENSE | 21 + .../cpufeatures-0.3.1/LICENSE-APACHE | 201 ++++++ .../third-party/cpufeatures-0.3.1/LICENSE-MIT | 25 + .../crossbeam-utils-0.8.23/LICENSE-APACHE | 201 ++++++ .../crossbeam-utils-0.8.23/LICENSE-MIT | 27 + licenses/third-party/crossterm-0.29.0/LICENSE | 21 + .../crossterm_winapi-0.9.1/LICENSE | 21 + .../crypto-common-0.2.2/LICENSE-APACHE | 201 ++++++ .../crypto-common-0.2.2/LICENSE-MIT | 25 + .../cursor-icon-1.2.0/LICENSE-APACHE | 202 ++++++ .../third-party/cursor-icon-1.2.0/LICENSE-MIT | 19 + licenses/third-party/darling-0.24.1/LICENSE | 21 + .../third-party/darling_core-0.24.1/LICENSE | 21 + .../third-party/darling_macro-0.24.1/LICENSE | 21 + .../third-party/deranged-0.5.8/LICENSE-APACHE | 202 ++++++ .../third-party/deranged-0.5.8/LICENSE-MIT | 19 + .../third-party/derive_more-2.1.1/LICENSE | 21 + .../derive_more-impl-2.1.1/LICENSE | 21 + .../third-party/digest-0.11.3/LICENSE-APACHE | 201 ++++++ .../third-party/digest-0.11.3/LICENSE-MIT | 26 + .../document-features-0.2.12/LICENSE-APACHE | 73 ++ .../document-features-0.2.12/LICENSE-MIT | 19 + .../downcast-rs-1.2.1/LICENSE-APACHE | 201 ++++++ .../third-party/downcast-rs-1.2.1/LICENSE-MIT | 25 + .../third-party/either-1.18.0/LICENSE-APACHE | 201 ++++++ .../third-party/either-1.18.0/LICENSE-MIT | 25 + .../equivalent-1.0.2/LICENSE-APACHE | 201 ++++++ .../third-party/equivalent-1.0.2/LICENSE-MIT | 25 + .../third-party/errno-0.3.14/LICENSE-APACHE | 201 ++++++ licenses/third-party/errno-0.3.14/LICENSE-MIT | 25 + .../fallible-iterator-0.3.0/LICENSE-APACHE | 202 ++++++ .../fallible-iterator-0.3.0/LICENSE-MIT | 19 + .../LICENSE-APACHE | 202 ++++++ .../LICENSE-MIT | 19 + .../fast-srgb8-1.0.0/LICENSE-APACHE | 201 ++++++ .../third-party/fast-srgb8-1.0.0/LICENSE-CC0 | 121 ++++ .../third-party/fast-srgb8-1.0.0/LICENSE-MIT | 25 + .../third-party/fastrand-2.5.0/LICENSE-APACHE | 201 ++++++ .../third-party/fastrand-2.5.0/LICENSE-MIT | 23 + .../filedescriptor-0.8.3/LICENSE.md | 21 + .../find-msvc-tools-0.1.13/LICENSE-APACHE | 201 ++++++ .../find-msvc-tools-0.1.13/LICENSE-MIT | 25 + licenses/third-party/foldhash-0.2.0/LICENSE | 19 + .../futures-io-0.3.34/LICENSE-APACHE | 202 ++++++ .../third-party/futures-io-0.3.34/LICENSE-MIT | 26 + .../getrandom-0.4.3/LICENSE-APACHE | 201 ++++++ .../third-party/getrandom-0.4.3/LICENSE-MIT | 26 + .../hashbrown-0.16.1/LICENSE-APACHE | 201 ++++++ .../third-party/hashbrown-0.16.1/LICENSE-MIT | 25 + .../hashbrown-0.17.1/LICENSE-APACHE | 201 ++++++ .../third-party/hashbrown-0.17.1/LICENSE-MIT | 25 + .../third-party/heck-0.5.0/LICENSE-APACHE | 201 ++++++ licenses/third-party/heck-0.5.0/LICENSE-MIT | 25 + .../hermit-abi-0.5.3/LICENSE-APACHE | 201 ++++++ .../third-party/hermit-abi-0.5.3/LICENSE-MIT | 23 + .../third-party/home-0.5.12/LICENSE-APACHE | 201 ++++++ licenses/third-party/home-0.5.12/LICENSE-MIT | 23 + .../hybrid-array-0.4.15/LICENSE-APACHE | 201 ++++++ .../hybrid-array-0.4.15/LICENSE-MIT | 25 + licenses/third-party/ident_case-1.0.1/LICENSE | 19 + .../indexmap-2.14.2/LICENSE-APACHE | 201 ++++++ .../third-party/indexmap-2.14.2/LICENSE-MIT | 25 + .../third-party/indoc-2.0.7/LICENSE-APACHE | 176 +++++ licenses/third-party/indoc-2.0.7/LICENSE-MIT | 23 + .../third-party/instability-0.3.13/LICENSE.md | 22 + .../itertools-0.14.0/LICENSE-APACHE | 201 ++++++ .../third-party/itertools-0.14.0/LICENSE-MIT | 25 + .../third-party/itoa-1.0.18/LICENSE-APACHE | 176 +++++ licenses/third-party/itoa-1.0.18/LICENSE-MIT | 23 + .../third-party/kasuari-0.4.12/LICENSE-APACHE | 201 ++++++ .../third-party/kasuari-0.4.12/LICENSE-MIT | 22 + .../lazy_static-1.5.0/LICENSE-APACHE | 201 ++++++ .../third-party/lazy_static-1.5.0/LICENSE-MIT | 25 + .../third-party/libc-0.2.189/LICENSE-APACHE | 176 +++++ licenses/third-party/libc-0.2.189/LICENSE-MIT | 25 + licenses/third-party/libm-0.2.16/LICENSE.txt | 258 +++++++ .../third-party/libsqlite3-sys-0.38.2/LICENSE | 19 + .../libsqlite3-sys-0.38.2/SQLITE_BLESSING.txt | 7 + .../line-clipping-0.3.7/LICENSE-APACHE | 201 ++++++ .../line-clipping-0.3.7/LICENSE-MIT | 21 + .../linux-raw-sys-0.12.1/COPYRIGHT | 29 + .../linux-raw-sys-0.12.1/LICENSE-APACHE | 201 ++++++ .../LICENSE-APACHE-2.0_WITH_LLVM-exception | 220 ++++++ .../linux-raw-sys-0.12.1/LICENSE-MIT | 23 + .../third-party/litrs-1.0.0/LICENSE-APACHE | 176 +++++ licenses/third-party/litrs-1.0.0/LICENSE-MIT | 25 + .../lock_api-0.4.14/LICENSE-APACHE | 201 ++++++ .../third-party/lock_api-0.4.14/LICENSE-MIT | 25 + .../third-party/log-0.4.34/LICENSE-APACHE | 201 ++++++ licenses/third-party/log-0.4.34/LICENSE-MIT | 25 + licenses/third-party/lru-0.18.0/LICENSE | 21 + licenses/third-party/memchr-2.8.3/COPYING | 3 + licenses/third-party/memchr-2.8.3/LICENSE-MIT | 21 + licenses/third-party/memchr-2.8.3/UNLICENSE | 24 + licenses/third-party/mio-1.2.3/LICENSE | 19 + .../third-party/miow-0.6.1/LICENSE-APACHE | 201 ++++++ licenses/third-party/miow-0.6.1/LICENSE-MIT | 25 + licenses/third-party/nix-0.28.0/LICENSE | 21 + .../third-party/num-conv-0.2.2/LICENSE-APACHE | 177 +++++ .../third-party/num-conv-0.2.2/LICENSE-MIT | 19 + .../num-traits-0.2.19/LICENSE-APACHE | 201 ++++++ .../third-party/num-traits-0.2.19/LICENSE-MIT | 25 + .../num_threads-0.1.7/LICENSE-APACHE | 202 ++++++ .../third-party/num_threads-0.1.7/LICENSE-MIT | 19 + .../third-party/object-0.39.1/LICENSE-APACHE | 201 ++++++ .../third-party/object-0.39.1/LICENSE-MIT | 25 + .../third-party/palette-0.7.6/LICENSE-APACHE | 201 ++++++ .../third-party/palette-0.7.6/LICENSE-MIT | 22 + .../palette_derive-0.7.6/LICENSE-APACHE | 201 ++++++ .../palette_derive-0.7.6/LICENSE-MIT | 22 + .../parking_lot-0.12.5/LICENSE-APACHE | 201 ++++++ .../parking_lot-0.12.5/LICENSE-MIT | 25 + .../parking_lot_core-0.9.12/LICENSE-APACHE | 201 ++++++ .../parking_lot_core-0.9.12/LICENSE-MIT | 25 + .../pin-project-lite-0.2.17/LICENSE-APACHE | 177 +++++ .../pin-project-lite-0.2.17/LICENSE-MIT | 23 + .../third-party/piper-0.2.5/LICENSE-APACHE | 201 ++++++ licenses/third-party/piper-0.2.5/LICENSE-MIT | 23 + .../pkg-config-0.3.34/LICENSE-APACHE | 201 ++++++ .../third-party/pkg-config-0.3.34/LICENSE-MIT | 25 + .../third-party/polling-3.11.0/LICENSE-APACHE | 201 ++++++ .../third-party/polling-3.11.0/LICENSE-MIT | 23 + .../portable-atomic-1.13.1/LICENSE-APACHE | 177 +++++ .../portable-atomic-1.13.1/LICENSE-MIT | 23 + .../third-party/portable-pty-0.9.0/LICENSE.md | 21 + .../third-party/powerfmt-0.2.0/LICENSE-APACHE | 202 ++++++ .../third-party/powerfmt-0.2.0/LICENSE-MIT | 19 + .../proc-macro2-1.0.107/LICENSE-APACHE | 176 +++++ .../proc-macro2-1.0.107/LICENSE-MIT | 23 + .../third-party/psm-0.1.32/LICENSE-APACHE | 201 ++++++ licenses/third-party/psm-0.1.32/LICENSE-MIT | 16 + .../third-party/quote-1.0.47/LICENSE-APACHE | 176 +++++ licenses/third-party/quote-1.0.47/LICENSE-MIT | 23 + licenses/third-party/r-efi-6.0.0/AUTHORS | 75 +++ licenses/third-party/ratatui-0.30.2/LICENSE | 22 + .../third-party/ratatui-core-0.1.2/LICENSE | 22 + .../ratatui-crossterm-0.1.2/LICENSE | 22 + .../third-party/ratatui-widgets-0.3.2/LICENSE | 22 + .../third-party/redox_syscall-0.5.18/LICENSE | 22 + .../third-party/regex-1.12.4/LICENSE-APACHE | 201 ++++++ licenses/third-party/regex-1.12.4/LICENSE-MIT | 25 + .../regex-automata-0.4.18/LICENSE-APACHE | 201 ++++++ .../regex-automata-0.4.18/LICENSE-MIT | 25 + .../regex-syntax-0.8.11/LICENSE-APACHE | 201 ++++++ .../regex-syntax-0.8.11/LICENSE-MIT | 25 + licenses/third-party/rusqlite-0.40.2/LICENSE | 19 + .../rustc_version-0.4.1/LICENSE-APACHE | 201 ++++++ .../rustc_version-0.4.1/LICENSE-MIT | 25 + licenses/third-party/rustix-1.1.4/COPYRIGHT | 29 + .../third-party/rustix-1.1.4/LICENSE-APACHE | 201 ++++++ .../LICENSE-APACHE-2.0_WITH_LLVM-exception | 220 ++++++ licenses/third-party/rustix-1.1.4/LICENSE-MIT | 23 + .../rustix-openpty-0.2.0/COPYRIGHT | 29 + .../rustix-openpty-0.2.0/LICENSE-APACHE | 201 ++++++ .../LICENSE-APACHE-2.0_WITH_LLVM-exception | 220 ++++++ .../rustix-openpty-0.2.0/LICENSE-MIT | 23 + .../rustversion-1.0.23/LICENSE-APACHE | 176 +++++ .../rustversion-1.0.23/LICENSE-MIT | 23 + .../third-party/ryu-1.0.23/LICENSE-APACHE | 176 +++++ licenses/third-party/ryu-1.0.23/LICENSE-BOOST | 23 + .../scopeguard-1.2.0/LICENSE-APACHE | 201 ++++++ .../third-party/scopeguard-1.2.0/LICENSE-MIT | 25 + .../third-party/semver-1.0.28/LICENSE-APACHE | 176 +++++ .../third-party/semver-1.0.28/LICENSE-MIT | 23 + .../third-party/serde-1.0.229/LICENSE-APACHE | 176 +++++ .../third-party/serde-1.0.229/LICENSE-MIT | 23 + .../serde_core-1.0.229/LICENSE-APACHE | 176 +++++ .../serde_core-1.0.229/LICENSE-MIT | 23 + .../serde_derive-1.0.229/LICENSE-APACHE | 176 +++++ .../serde_derive-1.0.229/LICENSE-MIT | 23 + .../serde_json-1.0.151/LICENSE-APACHE | 176 +++++ .../serde_json-1.0.151/LICENSE-MIT | 23 + .../serde_spanned-0.6.9/LICENSE-APACHE | 202 ++++++ .../serde_spanned-0.6.9/LICENSE-MIT | 19 + .../serde_stacker-0.1.14/LICENSE-APACHE | 176 +++++ .../serde_stacker-0.1.14/LICENSE-MIT | 23 + .../third-party/serial2-0.2.38/LICENSE-APACHE | 13 + .../third-party/sha2-0.11.0/LICENSE-APACHE | 201 ++++++ licenses/third-party/sha2-0.11.0/LICENSE-MIT | 28 + .../shared_library-0.1.9/LICENSE-APACHE | 201 ++++++ .../shared_library-0.1.9/LICENSE-MIT | 25 + .../shell-words-1.1.1/LICENSE-APACHE | 201 ++++++ .../third-party/shell-words-1.1.1/LICENSE-MIT | 25 + .../third-party/shlex-2.0.1/LICENSE-APACHE | 13 + licenses/third-party/shlex-2.0.1/LICENSE-MIT | 21 + .../signal-hook-0.3.18/LICENSE-APACHE | 201 ++++++ .../signal-hook-0.3.18/LICENSE-MIT | 25 + .../signal-hook-0.4.4/LICENSE-APACHE | 201 ++++++ .../third-party/signal-hook-0.4.4/LICENSE-MIT | 25 + .../signal-hook-mio-0.2.5/LICENSE-APACHE | 201 ++++++ .../signal-hook-mio-0.2.5/LICENSE-MIT | 25 + .../signal-hook-registry-1.4.8/LICENSE-APACHE | 201 ++++++ .../signal-hook-registry-1.4.8/LICENSE-MIT | 25 + .../smallvec-1.16.1/LICENSE-APACHE | 201 ++++++ .../third-party/smallvec-1.16.1/LICENSE-MIT | 25 + .../third-party/stacker-0.1.25/LICENSE-APACHE | 201 ++++++ .../third-party/stacker-0.1.25/LICENSE-MIT | 25 + .../static_assertions-1.1.0/LICENSE-APACHE | 202 ++++++ .../static_assertions-1.1.0/LICENSE-MIT | 21 + licenses/third-party/strsim-0.11.1/LICENSE | 23 + licenses/third-party/strum-0.28.0/LICENSE | 21 + .../third-party/strum_macros-0.28.0/LICENSE | 21 + .../third-party/syn-2.0.119/LICENSE-APACHE | 176 +++++ licenses/third-party/syn-2.0.119/LICENSE-MIT | 23 + licenses/third-party/syn-3.0.6/LICENSE-APACHE | 176 +++++ licenses/third-party/syn-3.0.6/LICENSE-MIT | 23 + .../thiserror-1.0.69/LICENSE-APACHE | 176 +++++ .../third-party/thiserror-1.0.69/LICENSE-MIT | 23 + .../thiserror-2.0.20/LICENSE-APACHE | 176 +++++ .../third-party/thiserror-2.0.20/LICENSE-MIT | 23 + .../thiserror-impl-1.0.69/LICENSE-APACHE | 176 +++++ .../thiserror-impl-1.0.69/LICENSE-MIT | 23 + .../thiserror-impl-2.0.20/LICENSE-APACHE | 176 +++++ .../thiserror-impl-2.0.20/LICENSE-MIT | 23 + .../third-party/time-0.3.55/LICENSE-APACHE | 177 +++++ licenses/third-party/time-0.3.55/LICENSE-MIT | 19 + .../time-core-0.1.9/LICENSE-APACHE | 177 +++++ .../third-party/time-core-0.1.9/LICENSE-MIT | 19 + .../time-macros-0.2.32/LICENSE-APACHE | 177 +++++ .../time-macros-0.2.32/LICENSE-MIT | 19 + .../third-party/toml-0.8.23/LICENSE-APACHE | 202 ++++++ licenses/third-party/toml-0.8.23/LICENSE-MIT | 19 + .../toml_datetime-0.6.11/LICENSE-APACHE | 202 ++++++ .../toml_datetime-0.6.11/LICENSE-MIT | 19 + .../toml_edit-0.22.27/LICENSE-APACHE | 202 ++++++ .../third-party/toml_edit-0.22.27/LICENSE-MIT | 19 + licenses/third-party/typenum-1.20.1/LICENSE | 1 + .../third-party/typenum-1.20.1/LICENSE-APACHE | 201 ++++++ .../third-party/typenum-1.20.1/LICENSE-MIT | 21 + .../unicode-ident-1.0.26/LICENSE-APACHE | 176 +++++ .../unicode-ident-1.0.26/LICENSE-MIT | 23 + .../unicode-ident-1.0.26/LICENSE-UNICODE | 39 ++ .../unicode-segmentation-1.13.3/COPYRIGHT | 7 + .../LICENSE-APACHE | 201 ++++++ .../unicode-segmentation-1.13.3/LICENSE-MIT | 25 + .../unicode-truncate-2.0.1/COPYRIGHT | 7 + .../unicode-truncate-2.0.1/LICENSE-APACHE | 201 ++++++ .../unicode-truncate-2.0.1/LICENSE-MIT | 25 + .../third-party/unicode-width-0.2.2/COPYRIGHT | 7 + .../unicode-width-0.2.2/LICENSE-APACHE | 201 ++++++ .../unicode-width-0.2.2/LICENSE-MIT | 25 + .../third-party/vcpkg-0.2.15/LICENSE-APACHE | 202 ++++++ licenses/third-party/vcpkg-0.2.15/LICENSE-MIT | 26 + .../third-party/vte-0.15.0/LICENSE-APACHE | 176 +++++ licenses/third-party/vte-0.15.0/LICENSE-MIT | 25 + .../LICENSE-APACHE | 201 ++++++ .../LICENSE-APACHE-2.0_WITH_LLVM-exception | 220 ++++++ .../LICENSE-MIT | 23 + .../third-party/winapi-0.3.9/LICENSE-APACHE | 201 ++++++ licenses/third-party/winapi-0.3.9/LICENSE-MIT | 19 + .../LICENSE-APACHE | 201 ++++++ .../LICENSE-MIT | 19 + .../LICENSE-APACHE | 201 ++++++ .../LICENSE-MIT | 19 + .../windows-link-0.2.1/LICENSE-APACHE-2.0 | 201 ++++++ .../windows-link-0.2.1/LICENSE-MIT | 21 + .../windows-sys-0.59.0/LICENSE-APACHE-2.0 | 201 ++++++ .../windows-sys-0.59.0/LICENSE-MIT | 21 + .../windows-sys-0.61.2/LICENSE-APACHE-2.0 | 201 ++++++ .../windows-sys-0.61.2/LICENSE-MIT | 21 + .../windows-targets-0.52.6/LICENSE-APACHE-2.0 | 201 ++++++ .../windows-targets-0.52.6/LICENSE-MIT | 21 + .../LICENSE-APACHE-2.0 | 201 ++++++ .../LICENSE-MIT | 21 + .../LICENSE-APACHE-2.0 | 201 ++++++ .../windows_aarch64_msvc-0.52.6/LICENSE-MIT | 21 + .../LICENSE-APACHE-2.0 | 201 ++++++ .../windows_i686_gnu-0.52.6/LICENSE-MIT | 21 + .../LICENSE-APACHE-2.0 | 201 ++++++ .../windows_i686_gnullvm-0.52.6/LICENSE-MIT | 21 + .../LICENSE-APACHE-2.0 | 201 ++++++ .../windows_i686_msvc-0.52.6/LICENSE-MIT | 21 + .../LICENSE-APACHE-2.0 | 201 ++++++ .../windows_x86_64_gnu-0.52.6/LICENSE-MIT | 21 + .../LICENSE-APACHE-2.0 | 201 ++++++ .../windows_x86_64_gnullvm-0.52.6/LICENSE-MIT | 21 + .../LICENSE-APACHE-2.0 | 201 ++++++ .../windows_x86_64_msvc-0.52.6/LICENSE-MIT | 21 + .../third-party/winnow-0.7.15/LICENSE-MIT | 18 + licenses/third-party/winreg-0.10.1/LICENSE | 19 + licenses/third-party/zmij-1.0.23/LICENSE-MIT | 23 + licenses/upstream/herdr/LICENSE | 201 ++++++ scripts/rust_dependency_audit.py | 634 ++++++++++++++++++ scripts/test_herdr_manifest_audit.py | 43 ++ scripts/test_rust_dependency_audit.py | 192 ++++++ 324 files changed, 32226 insertions(+) create mode 100644 .gitattributes create mode 100644 licenses/THIRD_PARTY_NOTICES.md create mode 100644 licenses/third-party/aho-corasick-1.1.5/COPYING create mode 100644 licenses/third-party/aho-corasick-1.1.5/LICENSE-MIT create mode 100644 licenses/third-party/aho-corasick-1.1.5/UNLICENSE create mode 100644 licenses/third-party/alacritty_terminal-0.26.0/LICENSE-APACHE create mode 100644 licenses/third-party/allocator-api2-0.2.21/LICENSE-APACHE create mode 100644 licenses/third-party/allocator-api2-0.2.21/LICENSE-MIT create mode 100644 licenses/third-party/anyhow-1.0.104/LICENSE-APACHE create mode 100644 licenses/third-party/anyhow-1.0.104/LICENSE-MIT create mode 100644 licenses/third-party/approx-0.5.1/LICENSE create mode 100644 licenses/third-party/ar_archive_writer-0.5.3/LICENSE.txt create mode 100644 licenses/third-party/arrayvec-0.7.8/LICENSE-APACHE create mode 100644 licenses/third-party/arrayvec-0.7.8/LICENSE-MIT create mode 100644 licenses/third-party/atomic-waker-1.1.2/LICENSE-APACHE create mode 100644 licenses/third-party/atomic-waker-1.1.2/LICENSE-MIT create mode 100644 licenses/third-party/autocfg-1.5.1/LICENSE-APACHE create mode 100644 licenses/third-party/autocfg-1.5.1/LICENSE-MIT create mode 100644 licenses/third-party/base64-0.22.1/LICENSE-APACHE create mode 100644 licenses/third-party/base64-0.22.1/LICENSE-MIT create mode 100644 licenses/third-party/bitflags-1.3.2/LICENSE-APACHE create mode 100644 licenses/third-party/bitflags-1.3.2/LICENSE-MIT create mode 100644 licenses/third-party/bitflags-2.13.2/LICENSE-APACHE create mode 100644 licenses/third-party/bitflags-2.13.2/LICENSE-MIT create mode 100644 licenses/third-party/block-buffer-0.12.1/LICENSE-APACHE create mode 100644 licenses/third-party/block-buffer-0.12.1/LICENSE-MIT create mode 100644 licenses/third-party/by_address-1.2.1/LICENSE-APACHE create mode 100644 licenses/third-party/by_address-1.2.1/LICENSE-MIT create mode 100644 licenses/third-party/castaway-0.2.4/LICENSE create mode 100644 licenses/third-party/cc-1.4.7/LICENSE-APACHE create mode 100644 licenses/third-party/cc-1.4.7/LICENSE-MIT create mode 100644 licenses/third-party/cfg-if-1.0.5/LICENSE-APACHE create mode 100644 licenses/third-party/cfg-if-1.0.5/LICENSE-MIT create mode 100644 licenses/third-party/cfg_aliases-0.1.1/LICENSE create mode 100644 licenses/third-party/compact_str-0.9.1/LICENSE create mode 100644 licenses/third-party/concurrent-queue-2.5.0/LICENSE-APACHE create mode 100644 licenses/third-party/concurrent-queue-2.5.0/LICENSE-MIT create mode 100644 licenses/third-party/const-oid-0.10.2/LICENSE-APACHE create mode 100644 licenses/third-party/const-oid-0.10.2/LICENSE-MIT create mode 100644 licenses/third-party/convert_case-0.10.0/LICENSE create mode 100644 licenses/third-party/cpufeatures-0.3.1/LICENSE-APACHE create mode 100644 licenses/third-party/cpufeatures-0.3.1/LICENSE-MIT create mode 100644 licenses/third-party/crossbeam-utils-0.8.23/LICENSE-APACHE create mode 100644 licenses/third-party/crossbeam-utils-0.8.23/LICENSE-MIT create mode 100644 licenses/third-party/crossterm-0.29.0/LICENSE create mode 100644 licenses/third-party/crossterm_winapi-0.9.1/LICENSE create mode 100644 licenses/third-party/crypto-common-0.2.2/LICENSE-APACHE create mode 100644 licenses/third-party/crypto-common-0.2.2/LICENSE-MIT create mode 100644 licenses/third-party/cursor-icon-1.2.0/LICENSE-APACHE create mode 100644 licenses/third-party/cursor-icon-1.2.0/LICENSE-MIT create mode 100644 licenses/third-party/darling-0.24.1/LICENSE create mode 100644 licenses/third-party/darling_core-0.24.1/LICENSE create mode 100644 licenses/third-party/darling_macro-0.24.1/LICENSE create mode 100644 licenses/third-party/deranged-0.5.8/LICENSE-APACHE create mode 100644 licenses/third-party/deranged-0.5.8/LICENSE-MIT create mode 100644 licenses/third-party/derive_more-2.1.1/LICENSE create mode 100644 licenses/third-party/derive_more-impl-2.1.1/LICENSE create mode 100644 licenses/third-party/digest-0.11.3/LICENSE-APACHE create mode 100644 licenses/third-party/digest-0.11.3/LICENSE-MIT create mode 100644 licenses/third-party/document-features-0.2.12/LICENSE-APACHE create mode 100644 licenses/third-party/document-features-0.2.12/LICENSE-MIT create mode 100644 licenses/third-party/downcast-rs-1.2.1/LICENSE-APACHE create mode 100644 licenses/third-party/downcast-rs-1.2.1/LICENSE-MIT create mode 100644 licenses/third-party/either-1.18.0/LICENSE-APACHE create mode 100644 licenses/third-party/either-1.18.0/LICENSE-MIT create mode 100644 licenses/third-party/equivalent-1.0.2/LICENSE-APACHE create mode 100644 licenses/third-party/equivalent-1.0.2/LICENSE-MIT create mode 100644 licenses/third-party/errno-0.3.14/LICENSE-APACHE create mode 100644 licenses/third-party/errno-0.3.14/LICENSE-MIT create mode 100644 licenses/third-party/fallible-iterator-0.3.0/LICENSE-APACHE create mode 100644 licenses/third-party/fallible-iterator-0.3.0/LICENSE-MIT create mode 100644 licenses/third-party/fallible-streaming-iterator-0.1.9/LICENSE-APACHE create mode 100644 licenses/third-party/fallible-streaming-iterator-0.1.9/LICENSE-MIT create mode 100644 licenses/third-party/fast-srgb8-1.0.0/LICENSE-APACHE create mode 100644 licenses/third-party/fast-srgb8-1.0.0/LICENSE-CC0 create mode 100644 licenses/third-party/fast-srgb8-1.0.0/LICENSE-MIT create mode 100644 licenses/third-party/fastrand-2.5.0/LICENSE-APACHE create mode 100644 licenses/third-party/fastrand-2.5.0/LICENSE-MIT create mode 100644 licenses/third-party/filedescriptor-0.8.3/LICENSE.md create mode 100644 licenses/third-party/find-msvc-tools-0.1.13/LICENSE-APACHE create mode 100644 licenses/third-party/find-msvc-tools-0.1.13/LICENSE-MIT create mode 100644 licenses/third-party/foldhash-0.2.0/LICENSE create mode 100644 licenses/third-party/futures-io-0.3.34/LICENSE-APACHE create mode 100644 licenses/third-party/futures-io-0.3.34/LICENSE-MIT create mode 100644 licenses/third-party/getrandom-0.4.3/LICENSE-APACHE create mode 100644 licenses/third-party/getrandom-0.4.3/LICENSE-MIT create mode 100644 licenses/third-party/hashbrown-0.16.1/LICENSE-APACHE create mode 100644 licenses/third-party/hashbrown-0.16.1/LICENSE-MIT create mode 100644 licenses/third-party/hashbrown-0.17.1/LICENSE-APACHE create mode 100644 licenses/third-party/hashbrown-0.17.1/LICENSE-MIT create mode 100644 licenses/third-party/heck-0.5.0/LICENSE-APACHE create mode 100644 licenses/third-party/heck-0.5.0/LICENSE-MIT create mode 100644 licenses/third-party/hermit-abi-0.5.3/LICENSE-APACHE create mode 100644 licenses/third-party/hermit-abi-0.5.3/LICENSE-MIT create mode 100644 licenses/third-party/home-0.5.12/LICENSE-APACHE create mode 100644 licenses/third-party/home-0.5.12/LICENSE-MIT create mode 100644 licenses/third-party/hybrid-array-0.4.15/LICENSE-APACHE create mode 100644 licenses/third-party/hybrid-array-0.4.15/LICENSE-MIT create mode 100644 licenses/third-party/ident_case-1.0.1/LICENSE create mode 100644 licenses/third-party/indexmap-2.14.2/LICENSE-APACHE create mode 100644 licenses/third-party/indexmap-2.14.2/LICENSE-MIT create mode 100644 licenses/third-party/indoc-2.0.7/LICENSE-APACHE create mode 100644 licenses/third-party/indoc-2.0.7/LICENSE-MIT create mode 100644 licenses/third-party/instability-0.3.13/LICENSE.md create mode 100644 licenses/third-party/itertools-0.14.0/LICENSE-APACHE create mode 100644 licenses/third-party/itertools-0.14.0/LICENSE-MIT create mode 100644 licenses/third-party/itoa-1.0.18/LICENSE-APACHE create mode 100644 licenses/third-party/itoa-1.0.18/LICENSE-MIT create mode 100644 licenses/third-party/kasuari-0.4.12/LICENSE-APACHE create mode 100644 licenses/third-party/kasuari-0.4.12/LICENSE-MIT create mode 100644 licenses/third-party/lazy_static-1.5.0/LICENSE-APACHE create mode 100644 licenses/third-party/lazy_static-1.5.0/LICENSE-MIT create mode 100644 licenses/third-party/libc-0.2.189/LICENSE-APACHE create mode 100644 licenses/third-party/libc-0.2.189/LICENSE-MIT create mode 100644 licenses/third-party/libm-0.2.16/LICENSE.txt create mode 100644 licenses/third-party/libsqlite3-sys-0.38.2/LICENSE create mode 100644 licenses/third-party/libsqlite3-sys-0.38.2/SQLITE_BLESSING.txt create mode 100644 licenses/third-party/line-clipping-0.3.7/LICENSE-APACHE create mode 100644 licenses/third-party/line-clipping-0.3.7/LICENSE-MIT create mode 100644 licenses/third-party/linux-raw-sys-0.12.1/COPYRIGHT create mode 100644 licenses/third-party/linux-raw-sys-0.12.1/LICENSE-APACHE create mode 100644 licenses/third-party/linux-raw-sys-0.12.1/LICENSE-APACHE-2.0_WITH_LLVM-exception create mode 100644 licenses/third-party/linux-raw-sys-0.12.1/LICENSE-MIT create mode 100644 licenses/third-party/litrs-1.0.0/LICENSE-APACHE create mode 100644 licenses/third-party/litrs-1.0.0/LICENSE-MIT create mode 100644 licenses/third-party/lock_api-0.4.14/LICENSE-APACHE create mode 100644 licenses/third-party/lock_api-0.4.14/LICENSE-MIT create mode 100644 licenses/third-party/log-0.4.34/LICENSE-APACHE create mode 100644 licenses/third-party/log-0.4.34/LICENSE-MIT create mode 100644 licenses/third-party/lru-0.18.0/LICENSE create mode 100644 licenses/third-party/memchr-2.8.3/COPYING create mode 100644 licenses/third-party/memchr-2.8.3/LICENSE-MIT create mode 100644 licenses/third-party/memchr-2.8.3/UNLICENSE create mode 100644 licenses/third-party/mio-1.2.3/LICENSE create mode 100644 licenses/third-party/miow-0.6.1/LICENSE-APACHE create mode 100644 licenses/third-party/miow-0.6.1/LICENSE-MIT create mode 100644 licenses/third-party/nix-0.28.0/LICENSE create mode 100644 licenses/third-party/num-conv-0.2.2/LICENSE-APACHE create mode 100644 licenses/third-party/num-conv-0.2.2/LICENSE-MIT create mode 100644 licenses/third-party/num-traits-0.2.19/LICENSE-APACHE create mode 100644 licenses/third-party/num-traits-0.2.19/LICENSE-MIT create mode 100644 licenses/third-party/num_threads-0.1.7/LICENSE-APACHE create mode 100644 licenses/third-party/num_threads-0.1.7/LICENSE-MIT create mode 100644 licenses/third-party/object-0.39.1/LICENSE-APACHE create mode 100644 licenses/third-party/object-0.39.1/LICENSE-MIT create mode 100644 licenses/third-party/palette-0.7.6/LICENSE-APACHE create mode 100644 licenses/third-party/palette-0.7.6/LICENSE-MIT create mode 100644 licenses/third-party/palette_derive-0.7.6/LICENSE-APACHE create mode 100644 licenses/third-party/palette_derive-0.7.6/LICENSE-MIT create mode 100644 licenses/third-party/parking_lot-0.12.5/LICENSE-APACHE create mode 100644 licenses/third-party/parking_lot-0.12.5/LICENSE-MIT create mode 100644 licenses/third-party/parking_lot_core-0.9.12/LICENSE-APACHE create mode 100644 licenses/third-party/parking_lot_core-0.9.12/LICENSE-MIT create mode 100644 licenses/third-party/pin-project-lite-0.2.17/LICENSE-APACHE create mode 100644 licenses/third-party/pin-project-lite-0.2.17/LICENSE-MIT create mode 100644 licenses/third-party/piper-0.2.5/LICENSE-APACHE create mode 100644 licenses/third-party/piper-0.2.5/LICENSE-MIT create mode 100644 licenses/third-party/pkg-config-0.3.34/LICENSE-APACHE create mode 100644 licenses/third-party/pkg-config-0.3.34/LICENSE-MIT create mode 100644 licenses/third-party/polling-3.11.0/LICENSE-APACHE create mode 100644 licenses/third-party/polling-3.11.0/LICENSE-MIT create mode 100644 licenses/third-party/portable-atomic-1.13.1/LICENSE-APACHE create mode 100644 licenses/third-party/portable-atomic-1.13.1/LICENSE-MIT create mode 100644 licenses/third-party/portable-pty-0.9.0/LICENSE.md create mode 100644 licenses/third-party/powerfmt-0.2.0/LICENSE-APACHE create mode 100644 licenses/third-party/powerfmt-0.2.0/LICENSE-MIT create mode 100644 licenses/third-party/proc-macro2-1.0.107/LICENSE-APACHE create mode 100644 licenses/third-party/proc-macro2-1.0.107/LICENSE-MIT create mode 100644 licenses/third-party/psm-0.1.32/LICENSE-APACHE create mode 100644 licenses/third-party/psm-0.1.32/LICENSE-MIT create mode 100644 licenses/third-party/quote-1.0.47/LICENSE-APACHE create mode 100644 licenses/third-party/quote-1.0.47/LICENSE-MIT create mode 100644 licenses/third-party/r-efi-6.0.0/AUTHORS create mode 100644 licenses/third-party/ratatui-0.30.2/LICENSE create mode 100644 licenses/third-party/ratatui-core-0.1.2/LICENSE create mode 100644 licenses/third-party/ratatui-crossterm-0.1.2/LICENSE create mode 100644 licenses/third-party/ratatui-widgets-0.3.2/LICENSE create mode 100644 licenses/third-party/redox_syscall-0.5.18/LICENSE create mode 100644 licenses/third-party/regex-1.12.4/LICENSE-APACHE create mode 100644 licenses/third-party/regex-1.12.4/LICENSE-MIT create mode 100644 licenses/third-party/regex-automata-0.4.18/LICENSE-APACHE create mode 100644 licenses/third-party/regex-automata-0.4.18/LICENSE-MIT create mode 100644 licenses/third-party/regex-syntax-0.8.11/LICENSE-APACHE create mode 100644 licenses/third-party/regex-syntax-0.8.11/LICENSE-MIT create mode 100644 licenses/third-party/rusqlite-0.40.2/LICENSE create mode 100644 licenses/third-party/rustc_version-0.4.1/LICENSE-APACHE create mode 100644 licenses/third-party/rustc_version-0.4.1/LICENSE-MIT create mode 100644 licenses/third-party/rustix-1.1.4/COPYRIGHT create mode 100644 licenses/third-party/rustix-1.1.4/LICENSE-APACHE create mode 100644 licenses/third-party/rustix-1.1.4/LICENSE-APACHE-2.0_WITH_LLVM-exception create mode 100644 licenses/third-party/rustix-1.1.4/LICENSE-MIT create mode 100644 licenses/third-party/rustix-openpty-0.2.0/COPYRIGHT create mode 100644 licenses/third-party/rustix-openpty-0.2.0/LICENSE-APACHE create mode 100644 licenses/third-party/rustix-openpty-0.2.0/LICENSE-APACHE-2.0_WITH_LLVM-exception create mode 100644 licenses/third-party/rustix-openpty-0.2.0/LICENSE-MIT create mode 100644 licenses/third-party/rustversion-1.0.23/LICENSE-APACHE create mode 100644 licenses/third-party/rustversion-1.0.23/LICENSE-MIT create mode 100644 licenses/third-party/ryu-1.0.23/LICENSE-APACHE create mode 100644 licenses/third-party/ryu-1.0.23/LICENSE-BOOST create mode 100644 licenses/third-party/scopeguard-1.2.0/LICENSE-APACHE create mode 100644 licenses/third-party/scopeguard-1.2.0/LICENSE-MIT create mode 100644 licenses/third-party/semver-1.0.28/LICENSE-APACHE create mode 100644 licenses/third-party/semver-1.0.28/LICENSE-MIT create mode 100644 licenses/third-party/serde-1.0.229/LICENSE-APACHE create mode 100644 licenses/third-party/serde-1.0.229/LICENSE-MIT create mode 100644 licenses/third-party/serde_core-1.0.229/LICENSE-APACHE create mode 100644 licenses/third-party/serde_core-1.0.229/LICENSE-MIT create mode 100644 licenses/third-party/serde_derive-1.0.229/LICENSE-APACHE create mode 100644 licenses/third-party/serde_derive-1.0.229/LICENSE-MIT create mode 100644 licenses/third-party/serde_json-1.0.151/LICENSE-APACHE create mode 100644 licenses/third-party/serde_json-1.0.151/LICENSE-MIT create mode 100644 licenses/third-party/serde_spanned-0.6.9/LICENSE-APACHE create mode 100644 licenses/third-party/serde_spanned-0.6.9/LICENSE-MIT create mode 100644 licenses/third-party/serde_stacker-0.1.14/LICENSE-APACHE create mode 100644 licenses/third-party/serde_stacker-0.1.14/LICENSE-MIT create mode 100644 licenses/third-party/serial2-0.2.38/LICENSE-APACHE create mode 100644 licenses/third-party/sha2-0.11.0/LICENSE-APACHE create mode 100644 licenses/third-party/sha2-0.11.0/LICENSE-MIT create mode 100644 licenses/third-party/shared_library-0.1.9/LICENSE-APACHE create mode 100644 licenses/third-party/shared_library-0.1.9/LICENSE-MIT create mode 100644 licenses/third-party/shell-words-1.1.1/LICENSE-APACHE create mode 100644 licenses/third-party/shell-words-1.1.1/LICENSE-MIT create mode 100644 licenses/third-party/shlex-2.0.1/LICENSE-APACHE create mode 100644 licenses/third-party/shlex-2.0.1/LICENSE-MIT create mode 100644 licenses/third-party/signal-hook-0.3.18/LICENSE-APACHE create mode 100644 licenses/third-party/signal-hook-0.3.18/LICENSE-MIT create mode 100644 licenses/third-party/signal-hook-0.4.4/LICENSE-APACHE create mode 100644 licenses/third-party/signal-hook-0.4.4/LICENSE-MIT create mode 100644 licenses/third-party/signal-hook-mio-0.2.5/LICENSE-APACHE create mode 100644 licenses/third-party/signal-hook-mio-0.2.5/LICENSE-MIT create mode 100644 licenses/third-party/signal-hook-registry-1.4.8/LICENSE-APACHE create mode 100644 licenses/third-party/signal-hook-registry-1.4.8/LICENSE-MIT create mode 100644 licenses/third-party/smallvec-1.16.1/LICENSE-APACHE create mode 100644 licenses/third-party/smallvec-1.16.1/LICENSE-MIT create mode 100644 licenses/third-party/stacker-0.1.25/LICENSE-APACHE create mode 100644 licenses/third-party/stacker-0.1.25/LICENSE-MIT create mode 100644 licenses/third-party/static_assertions-1.1.0/LICENSE-APACHE create mode 100644 licenses/third-party/static_assertions-1.1.0/LICENSE-MIT create mode 100644 licenses/third-party/strsim-0.11.1/LICENSE create mode 100644 licenses/third-party/strum-0.28.0/LICENSE create mode 100644 licenses/third-party/strum_macros-0.28.0/LICENSE create mode 100644 licenses/third-party/syn-2.0.119/LICENSE-APACHE create mode 100644 licenses/third-party/syn-2.0.119/LICENSE-MIT create mode 100644 licenses/third-party/syn-3.0.6/LICENSE-APACHE create mode 100644 licenses/third-party/syn-3.0.6/LICENSE-MIT create mode 100644 licenses/third-party/thiserror-1.0.69/LICENSE-APACHE create mode 100644 licenses/third-party/thiserror-1.0.69/LICENSE-MIT create mode 100644 licenses/third-party/thiserror-2.0.20/LICENSE-APACHE create mode 100644 licenses/third-party/thiserror-2.0.20/LICENSE-MIT create mode 100644 licenses/third-party/thiserror-impl-1.0.69/LICENSE-APACHE create mode 100644 licenses/third-party/thiserror-impl-1.0.69/LICENSE-MIT create mode 100644 licenses/third-party/thiserror-impl-2.0.20/LICENSE-APACHE create mode 100644 licenses/third-party/thiserror-impl-2.0.20/LICENSE-MIT create mode 100644 licenses/third-party/time-0.3.55/LICENSE-APACHE create mode 100644 licenses/third-party/time-0.3.55/LICENSE-MIT create mode 100644 licenses/third-party/time-core-0.1.9/LICENSE-APACHE create mode 100644 licenses/third-party/time-core-0.1.9/LICENSE-MIT create mode 100644 licenses/third-party/time-macros-0.2.32/LICENSE-APACHE create mode 100644 licenses/third-party/time-macros-0.2.32/LICENSE-MIT create mode 100644 licenses/third-party/toml-0.8.23/LICENSE-APACHE create mode 100644 licenses/third-party/toml-0.8.23/LICENSE-MIT create mode 100644 licenses/third-party/toml_datetime-0.6.11/LICENSE-APACHE create mode 100644 licenses/third-party/toml_datetime-0.6.11/LICENSE-MIT create mode 100644 licenses/third-party/toml_edit-0.22.27/LICENSE-APACHE create mode 100644 licenses/third-party/toml_edit-0.22.27/LICENSE-MIT create mode 100644 licenses/third-party/typenum-1.20.1/LICENSE create mode 100644 licenses/third-party/typenum-1.20.1/LICENSE-APACHE create mode 100644 licenses/third-party/typenum-1.20.1/LICENSE-MIT create mode 100644 licenses/third-party/unicode-ident-1.0.26/LICENSE-APACHE create mode 100644 licenses/third-party/unicode-ident-1.0.26/LICENSE-MIT create mode 100644 licenses/third-party/unicode-ident-1.0.26/LICENSE-UNICODE create mode 100644 licenses/third-party/unicode-segmentation-1.13.3/COPYRIGHT create mode 100644 licenses/third-party/unicode-segmentation-1.13.3/LICENSE-APACHE create mode 100644 licenses/third-party/unicode-segmentation-1.13.3/LICENSE-MIT create mode 100644 licenses/third-party/unicode-truncate-2.0.1/COPYRIGHT create mode 100644 licenses/third-party/unicode-truncate-2.0.1/LICENSE-APACHE create mode 100644 licenses/third-party/unicode-truncate-2.0.1/LICENSE-MIT create mode 100644 licenses/third-party/unicode-width-0.2.2/COPYRIGHT create mode 100644 licenses/third-party/unicode-width-0.2.2/LICENSE-APACHE create mode 100644 licenses/third-party/unicode-width-0.2.2/LICENSE-MIT create mode 100644 licenses/third-party/vcpkg-0.2.15/LICENSE-APACHE create mode 100644 licenses/third-party/vcpkg-0.2.15/LICENSE-MIT create mode 100644 licenses/third-party/vte-0.15.0/LICENSE-APACHE create mode 100644 licenses/third-party/vte-0.15.0/LICENSE-MIT create mode 100644 licenses/third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-APACHE create mode 100644 licenses/third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-APACHE-2.0_WITH_LLVM-exception create mode 100644 licenses/third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-MIT create mode 100644 licenses/third-party/winapi-0.3.9/LICENSE-APACHE create mode 100644 licenses/third-party/winapi-0.3.9/LICENSE-MIT create mode 100644 licenses/third-party/winapi-i686-pc-windows-gnu-0.4.0/LICENSE-APACHE create mode 100644 licenses/third-party/winapi-i686-pc-windows-gnu-0.4.0/LICENSE-MIT create mode 100644 licenses/third-party/winapi-x86_64-pc-windows-gnu-0.4.0/LICENSE-APACHE create mode 100644 licenses/third-party/winapi-x86_64-pc-windows-gnu-0.4.0/LICENSE-MIT create mode 100644 licenses/third-party/windows-link-0.2.1/LICENSE-APACHE-2.0 create mode 100644 licenses/third-party/windows-link-0.2.1/LICENSE-MIT create mode 100644 licenses/third-party/windows-sys-0.59.0/LICENSE-APACHE-2.0 create mode 100644 licenses/third-party/windows-sys-0.59.0/LICENSE-MIT create mode 100644 licenses/third-party/windows-sys-0.61.2/LICENSE-APACHE-2.0 create mode 100644 licenses/third-party/windows-sys-0.61.2/LICENSE-MIT create mode 100644 licenses/third-party/windows-targets-0.52.6/LICENSE-APACHE-2.0 create mode 100644 licenses/third-party/windows-targets-0.52.6/LICENSE-MIT create mode 100644 licenses/third-party/windows_aarch64_gnullvm-0.52.6/LICENSE-APACHE-2.0 create mode 100644 licenses/third-party/windows_aarch64_gnullvm-0.52.6/LICENSE-MIT create mode 100644 licenses/third-party/windows_aarch64_msvc-0.52.6/LICENSE-APACHE-2.0 create mode 100644 licenses/third-party/windows_aarch64_msvc-0.52.6/LICENSE-MIT create mode 100644 licenses/third-party/windows_i686_gnu-0.52.6/LICENSE-APACHE-2.0 create mode 100644 licenses/third-party/windows_i686_gnu-0.52.6/LICENSE-MIT create mode 100644 licenses/third-party/windows_i686_gnullvm-0.52.6/LICENSE-APACHE-2.0 create mode 100644 licenses/third-party/windows_i686_gnullvm-0.52.6/LICENSE-MIT create mode 100644 licenses/third-party/windows_i686_msvc-0.52.6/LICENSE-APACHE-2.0 create mode 100644 licenses/third-party/windows_i686_msvc-0.52.6/LICENSE-MIT create mode 100644 licenses/third-party/windows_x86_64_gnu-0.52.6/LICENSE-APACHE-2.0 create mode 100644 licenses/third-party/windows_x86_64_gnu-0.52.6/LICENSE-MIT create mode 100644 licenses/third-party/windows_x86_64_gnullvm-0.52.6/LICENSE-APACHE-2.0 create mode 100644 licenses/third-party/windows_x86_64_gnullvm-0.52.6/LICENSE-MIT create mode 100644 licenses/third-party/windows_x86_64_msvc-0.52.6/LICENSE-APACHE-2.0 create mode 100644 licenses/third-party/windows_x86_64_msvc-0.52.6/LICENSE-MIT create mode 100644 licenses/third-party/winnow-0.7.15/LICENSE-MIT create mode 100644 licenses/third-party/winreg-0.10.1/LICENSE create mode 100644 licenses/third-party/zmij-1.0.23/LICENSE-MIT create mode 100644 licenses/upstream/herdr/LICENSE create mode 100644 scripts/rust_dependency_audit.py create mode 100644 scripts/test_herdr_manifest_audit.py create mode 100644 scripts/test_rust_dependency_audit.py diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d7bb20b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# Byte-exact files. scripts/rust_dependency_audit.py and +# scripts/test_herdr_manifest_audit.py verify these by SHA-256, so Git must +# never convert their line endings (some upstream notices use CRLF). +/licenses/** -text +/crates/rover-agents/src/herdr_manifests/** -text diff --git a/licenses/THIRD_PARTY_NOTICES.md b/licenses/THIRD_PARTY_NOTICES.md new file mode 100644 index 0000000..a5168cf --- /dev/null +++ b/licenses/THIRD_PARTY_NOTICES.md @@ -0,0 +1,196 @@ +# Third party notices + +This inventory covers all registry packages in `Cargo.lock`, including optional and target-specific packages. +Legacy slash-separated license alternatives are normalized to SPDX `OR` expressions. +License and author files are copied from the resolved crate sources and checked byte-for-byte; when a target-specific package omits them, an identical SPDX declaration from the same upstream repository supplies the files. + +| Package | Version | SPDX license expression | Included license/author files (SHA-256) | +|---|---:|---|---| +| `aho-corasick` | 1.1.5 | Unlicense OR MIT | [`COPYING`](third-party/aho-corasick-1.1.5/COPYING) `01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f`
[`LICENSE-MIT`](third-party/aho-corasick-1.1.5/LICENSE-MIT) `0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f`
[`UNLICENSE`](third-party/aho-corasick-1.1.5/UNLICENSE) `7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c` | +| `alacritty_terminal` | 0.26.0 | Apache-2.0 | [`LICENSE-APACHE`](third-party/alacritty_terminal-0.26.0/LICENSE-APACHE) `8884289e3969c089cd556c53fa1d3f51d86b29e52a8c1d660a55ce8292986910` | +| `allocator-api2` | 0.2.21 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/allocator-api2-0.2.21/LICENSE-APACHE) `20fe7b00e904ed690e3b9fd6073784d3fc428141dbd10b81c01fd143d0797f58`
[`LICENSE-MIT`](third-party/allocator-api2-0.2.21/LICENSE-MIT) `36516aefdc84c5d5a1e7485425913a22dbda69eb1930c5e84d6ae4972b5194b9` | +| `anyhow` | 1.0.104 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/anyhow-1.0.104/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/anyhow-1.0.104/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `approx` | 0.5.1 | Apache-2.0 | [`LICENSE`](third-party/approx-0.5.1/LICENSE) `3ddf9be5c28fe27dad143a5dc76eea25222ad1dd68934a047064e56ed2fa40c5` | +| `ar_archive_writer` | 0.5.3 | Apache-2.0 WITH LLVM-exception | [`LICENSE.txt`](third-party/ar_archive_writer-0.5.3/LICENSE.txt) `f72b120d1385408e9e380acc020756eb6ba1b461d66c328ea67327ba08d7dcfd` | +| `arrayvec` | 0.7.8 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/arrayvec-0.7.8/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/arrayvec-0.7.8/LICENSE-MIT) `4da95ec4ecb65b738d470b7d762894ad9c97da93e6cbfb18b570fc2c96f4b871` | +| `atomic-waker` | 1.1.2 | Apache-2.0 OR MIT | [`LICENSE-APACHE`](third-party/atomic-waker-1.1.2/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/atomic-waker-1.1.2/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `autocfg` | 1.5.1 | Apache-2.0 OR MIT | [`LICENSE-APACHE`](third-party/autocfg-1.5.1/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/autocfg-1.5.1/LICENSE-MIT) `27995d58ad5c1145c1a8cd86244ce844886958a35eb2b78c6b772748669999ac` | +| `base64` | 0.22.1 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/base64-0.22.1/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/base64-0.22.1/LICENSE-MIT) `0dd882e53de11566d50f8e8e2d5a651bcf3fabee4987d70f306233cf39094ba7` | +| `bitflags` | 1.3.2 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/bitflags-1.3.2/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/bitflags-1.3.2/LICENSE-MIT) `6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb` | +| `bitflags` | 2.13.2 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/bitflags-2.13.2/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/bitflags-2.13.2/LICENSE-MIT) `6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb` | +| `block-buffer` | 0.12.1 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/block-buffer-0.12.1/LICENSE-APACHE) `a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5`
[`LICENSE-MIT`](third-party/block-buffer-0.12.1/LICENSE-MIT) `98181e7249d0c01737645ec982499ce99a0f07eb8f7d625b8840d799d10dbc01` | +| `by_address` | 1.2.1 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/by_address-1.2.1/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/by_address-1.2.1/LICENSE-MIT) `daa94322de7eab889e055932396160395bd8e3af82f56ae8c419d3049111da72` | +| `castaway` | 0.2.4 | MIT | [`LICENSE`](third-party/castaway-0.2.4/LICENSE) `f98e09091d5ae02b2f2ec1ead4f7f28c4c44d1cb98b078739ba67091637e170c` | +| `cc` | 1.4.7 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/cc-1.4.7/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/cc-1.4.7/LICENSE-MIT) `378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397` | +| `cfg-if` | 1.0.5 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/cfg-if-1.0.5/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/cfg-if-1.0.5/LICENSE-MIT) `378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397` | +| `cfg_aliases` | 0.1.1 | MIT | [`LICENSE`](third-party/cfg_aliases-0.1.1/LICENSE) `31b94860253d8ec7b4529f51901044d3b459d6292d996504a36b1bae3a36a812` | +| `compact_str` | 0.9.1 | MIT | [`LICENSE`](third-party/compact_str-0.9.1/LICENSE) `0d52feec79589df30138dad3800323fe5686b764b73347330a2dfef8ac83efe8` | +| `concurrent-queue` | 2.5.0 | Apache-2.0 OR MIT | [`LICENSE-APACHE`](third-party/concurrent-queue-2.5.0/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/concurrent-queue-2.5.0/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `const-oid` | 0.10.2 | Apache-2.0 OR MIT | [`LICENSE-APACHE`](third-party/const-oid-0.10.2/LICENSE-APACHE) `a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5`
[`LICENSE-MIT`](third-party/const-oid-0.10.2/LICENSE-MIT) `73b9dc2e79c7308998dd30296e073aefaefb944a68fb89aa412c23c0edcabcaa` | +| `convert_case` | 0.10.0 | MIT | [`LICENSE`](third-party/convert_case-0.10.0/LICENSE) `aed7b1758e35afa0cd0fde059d61950747ca11cd0e5e169cb21c11608daed772` | +| `cpufeatures` | 0.3.1 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/cpufeatures-0.3.1/LICENSE-APACHE) `a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5`
[`LICENSE-MIT`](third-party/cpufeatures-0.3.1/LICENSE-MIT) `73b9dc2e79c7308998dd30296e073aefaefb944a68fb89aa412c23c0edcabcaa` | +| `crossbeam-utils` | 0.8.23 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/crossbeam-utils-0.8.23/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/crossbeam-utils-0.8.23/LICENSE-MIT) `5734ed989dfca1f625b40281ee9f4530f91b2411ec01cb748223e7eb87e201ab` | +| `crossterm` | 0.29.0 | MIT | [`LICENSE`](third-party/crossterm-0.29.0/LICENSE) `aca4760f2a5eba9ce9d4448fe7cd3fcf98245a8315ede59ebd0c6085dd542e61` | +| `crossterm_winapi` | 0.9.1 | MIT | [`LICENSE`](third-party/crossterm_winapi-0.9.1/LICENSE) `aca4760f2a5eba9ce9d4448fe7cd3fcf98245a8315ede59ebd0c6085dd542e61` | +| `crypto-common` | 0.2.2 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/crypto-common-0.2.2/LICENSE-APACHE) `a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5`
[`LICENSE-MIT`](third-party/crypto-common-0.2.2/LICENSE-MIT) `d2e7ec5355c96eeade56b09187ceb48a6a30299da3ce7531a66d3d11405ab963` | +| `cursor-icon` | 1.2.0 | MIT OR Apache-2.0 OR Zlib | [`LICENSE-APACHE`](third-party/cursor-icon-1.2.0/LICENSE-APACHE) `b59affd015e517ed998dd735757f9a3693643cd389609947ed54074234a10784`
[`LICENSE-MIT`](third-party/cursor-icon-1.2.0/LICENSE-MIT) `c138726a726ac37bae09d0f8a48e74e8e3aa8a8952fcd10fae0fb224cc1c05f1` | +| `darling` | 0.24.1 | MIT | [`LICENSE`](third-party/darling-0.24.1/LICENSE) `8ea93490d74a5a1b1af3ff71d786271b3f1e5f0bea79ac16e02ec533cef040d6` | +| `darling_core` | 0.24.1 | MIT | [`LICENSE`](third-party/darling_core-0.24.1/LICENSE) `8ea93490d74a5a1b1af3ff71d786271b3f1e5f0bea79ac16e02ec533cef040d6` | +| `darling_macro` | 0.24.1 | MIT | [`LICENSE`](third-party/darling_macro-0.24.1/LICENSE) `8ea93490d74a5a1b1af3ff71d786271b3f1e5f0bea79ac16e02ec533cef040d6` | +| `deranged` | 0.5.8 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/deranged-0.5.8/LICENSE-APACHE) `edd65bdd88957a205c47d53fa499eed8865a70320f0f03f6391668cb304ea376`
[`LICENSE-MIT`](third-party/deranged-0.5.8/LICENSE-MIT) `231c837c45eb53f108fb48929e488965bc4fcc14e9ea21d35f50e6b99d98685b` | +| `derive_more` | 2.1.1 | MIT | [`LICENSE`](third-party/derive_more-2.1.1/LICENSE) `8a35369f3ca263b3c62fbb5032947e53b6bfebc6c8a4d1bb982de1c069f6fba5` | +| `derive_more-impl` | 2.1.1 | MIT | [`LICENSE`](third-party/derive_more-impl-2.1.1/LICENSE) `8a35369f3ca263b3c62fbb5032947e53b6bfebc6c8a4d1bb982de1c069f6fba5` | +| `digest` | 0.11.3 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/digest-0.11.3/LICENSE-APACHE) `a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5`
[`LICENSE-MIT`](third-party/digest-0.11.3/LICENSE-MIT) `af59cea35d7f5e2777a713b8d155d65efa2c339eb43f3c14e868c6ac8506edad` | +| `document-features` | 0.2.12 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/document-features-0.2.12/LICENSE-APACHE) `074e6e32c86a4c0ef8b3ed25b721ca23aca83df277cd88106ef7177c354615ff`
[`LICENSE-MIT`](third-party/document-features-0.2.12/LICENSE-MIT) `aa893340d14b9844625be6a50ac644169a01b52f0211cbf81b09e1874c8cd81d` | +| `downcast-rs` | 1.2.1 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/downcast-rs-1.2.1/LICENSE-APACHE) `8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90`
[`LICENSE-MIT`](third-party/downcast-rs-1.2.1/LICENSE-MIT) `fb252fecf98d9af8f1d9201c5860319a514f5a8a24700dc84fc6ea287a7a4958` | +| `either` | 1.18.0 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/either-1.18.0/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/either-1.18.0/LICENSE-MIT) `7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545` | +| `equivalent` | 1.0.2 | Apache-2.0 OR MIT | [`LICENSE-APACHE`](third-party/equivalent-1.0.2/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/equivalent-1.0.2/LICENSE-MIT) `7365cc8878a1d7ce155a58c4ca09c3d7a6be413efa5334a80ea842912b669349` | +| `errno` | 0.3.14 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/errno-0.3.14/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/errno-0.3.14/LICENSE-MIT) `8764a597675778ddfd4e25f81b08a05dbcf089ac05662df7613fe67f150e3aa2` | +| `fallible-iterator` | 0.3.0 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/fallible-iterator-0.3.0/LICENSE-APACHE) `c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08`
[`LICENSE-MIT`](third-party/fallible-iterator-0.3.0/LICENSE-MIT) `0816e154b159ba255c563f7c8c7df5bbb8cc5fc96f5ab8cf9f4743b4f41fe7eb` | +| `fallible-streaming-iterator` | 0.1.9 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/fallible-streaming-iterator-0.1.9/LICENSE-APACHE) `c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08`
[`LICENSE-MIT`](third-party/fallible-streaming-iterator-0.1.9/LICENSE-MIT) `8dcec5569a9be5b0e086c80faed6f1aefa670af0ec29cecc2f714303096887e0` | +| `fast-srgb8` | 1.0.0 | MIT OR Apache-2.0 OR CC0-1.0 | [`LICENSE-APACHE`](third-party/fast-srgb8-1.0.0/LICENSE-APACHE) `a8a76b5fd1f1e53246d8d12dac4e6bed981f93b1f94d63f4edbf9a367b83cfc4`
[`LICENSE-CC0`](third-party/fast-srgb8-1.0.0/LICENSE-CC0) `a2010f343487d3f7618affe54f789f5487602331c0a8d03f49e9a7c547cf0499`
[`LICENSE-MIT`](third-party/fast-srgb8-1.0.0/LICENSE-MIT) `0b32b8546bef7ce9338df62d68b8f1b1a2de678df10e97ee108218ae921d4845` | +| `fastrand` | 2.5.0 | Apache-2.0 OR MIT | [`LICENSE-APACHE`](third-party/fastrand-2.5.0/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/fastrand-2.5.0/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `filedescriptor` | 0.8.3 | MIT | [`LICENSE.md`](third-party/filedescriptor-0.8.3/LICENSE.md) `191c46fcf52061382b1c51a70311eb9081381cc158e5899f3739473a9432185b` | +| `find-msvc-tools` | 0.1.13 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/find-msvc-tools-0.1.13/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/find-msvc-tools-0.1.13/LICENSE-MIT) `378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397` | +| `foldhash` | 0.2.0 | Zlib | [`LICENSE`](third-party/foldhash-0.2.0/LICENSE) `b1181a40b2a7b25cf66fd01481713bc1005df082c53ef73e851e55071b102744` | +| `futures-io` | 0.3.34 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/futures-io-0.3.34/LICENSE-APACHE) `275c491d6d1160553c32fd6127061d7f9606c3ea25abfad6ca3f6ed088785427`
[`LICENSE-MIT`](third-party/futures-io-0.3.34/LICENSE-MIT) `6652c868f35dfe5e8ef636810a4e576b9d663f3a17fb0f5613ad73583e1b88fd` | +| `getrandom` | 0.4.3 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/getrandom-0.4.3/LICENSE-APACHE) `aaff376532ea30a0cd5330b9502ad4a4c8bf769c539c87ffe78819d188a18ebf`
[`LICENSE-MIT`](third-party/getrandom-0.4.3/LICENSE-MIT) `523a42c25d245dde9c015f882cec7f4555aad883382a6cf19b4b7d9b2cd5419b` | +| `hashbrown` | 0.16.1 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/hashbrown-0.16.1/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/hashbrown-0.16.1/LICENSE-MIT) `ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2` | +| `hashbrown` | 0.17.1 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/hashbrown-0.17.1/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/hashbrown-0.17.1/LICENSE-MIT) `ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2` | +| `heck` | 0.5.0 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/heck-0.5.0/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/heck-0.5.0/LICENSE-MIT) `7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0` | +| `hermit-abi` | 0.5.3 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/hermit-abi-0.5.3/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/hermit-abi-0.5.3/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `home` | 0.5.12 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/home-0.5.12/LICENSE-APACHE) `8ada45cd9f843acf64e4722ae262c622a2b3b3007c7310ef36ac1061a30f6adb`
[`LICENSE-MIT`](third-party/home-0.5.12/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `hybrid-array` | 0.4.15 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/hybrid-array-0.4.15/LICENSE-APACHE) `a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5`
[`LICENSE-MIT`](third-party/hybrid-array-0.4.15/LICENSE-MIT) `70c9d40f1f9545c3f133b8a67206e89da850f6468eed072281bb3701514114a9` | +| `ident_case` | 1.0.1 | MIT OR Apache-2.0 | [`LICENSE`](third-party/ident_case-1.0.1/LICENSE) `508a77d2e7b51d98adeed32648ad124b7b30241a8e70b2e72c99f92d8e5874d1` | +| `indexmap` | 2.14.2 | Apache-2.0 OR MIT | [`LICENSE-APACHE`](third-party/indexmap-2.14.2/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/indexmap-2.14.2/LICENSE-MIT) `ecc269ef87fd38a1d98e30bfac9ba964a9dbd9315c3770fed98d4d7cb5882055` | +| `indoc` | 2.0.7 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/indoc-2.0.7/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/indoc-2.0.7/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `instability` | 0.3.13 | MIT | [`LICENSE.md`](third-party/instability-0.3.13/LICENSE.md) `0352320d9929e862c1f07b59caacc3cd1bbacfec0ad4136bf4c89d4ca8939781` | +| `itertools` | 0.14.0 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/itertools-0.14.0/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/itertools-0.14.0/LICENSE-MIT) `7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545` | +| `itoa` | 1.0.18 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/itoa-1.0.18/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/itoa-1.0.18/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `kasuari` | 0.4.12 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/kasuari-0.4.12/LICENSE-APACHE) `000b4962e6b27176a0ff89cce4be555b16472cafb5671eb2804a8fdac6854793`
[`LICENSE-MIT`](third-party/kasuari-0.4.12/LICENSE-MIT) `74a7056189235b49336669da4e67ad1b315de4ed17e93df751f48c3fab403812` | +| `lazy_static` | 1.5.0 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/lazy_static-1.5.0/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/lazy_static-1.5.0/LICENSE-MIT) `0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720` | +| `libc` | 0.2.189 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/libc-0.2.189/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/libc-0.2.189/LICENSE-MIT) `123a331b5dbf04c30097fa43b8f858bc85df671fe776de498d01f3d6b7c1f69e` | +| `libm` | 0.2.16 | MIT | [`LICENSE.txt`](third-party/libm-0.2.16/LICENSE.txt) `3823dda7cf046602f4b4e77ec8e227863dc4736037cc85bb33d9f19febe16bb7` | +| `libsqlite3-sys` | 0.38.2 | MIT | [`LICENSE`](third-party/libsqlite3-sys-0.38.2/LICENSE) `c10c1f27337546471e5f7e4e97fdd398b35b9d4e126115dcd22de8d8e65abf6f` | +| `line-clipping` | 0.3.7 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/line-clipping-0.3.7/LICENSE-APACHE) `86f2767527a034d6b9cb8bd98676925e58ed75bf805d234dc2a71a54150cbad1`
[`LICENSE-MIT`](third-party/line-clipping-0.3.7/LICENSE-MIT) `520a93f9b172b4c8d2a7ab6f0da1f5f987eba7270ab64a02972095cd87df7fc4` | +| `linux-raw-sys` | 0.12.1 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | [`COPYRIGHT`](third-party/linux-raw-sys-0.12.1/COPYRIGHT) `3290ae0fbc9ddb77d2239121d710f0bb9d31b3b4744e6d97fe01e652b4c1870b`
[`LICENSE-APACHE`](third-party/linux-raw-sys-0.12.1/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-APACHE-2.0_WITH_LLVM-exception`](third-party/linux-raw-sys-0.12.1/LICENSE-APACHE-2.0_WITH_LLVM-exception) `268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5`
[`LICENSE-MIT`](third-party/linux-raw-sys-0.12.1/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `litrs` | 1.0.0 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/litrs-1.0.0/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/litrs-1.0.0/LICENSE-MIT) `7dc1552e88f49132cb358b1b962fc5e79fa42d70bcbb88c526d33e45b8e98036` | +| `lock_api` | 0.4.14 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/lock_api-0.4.14/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/lock_api-0.4.14/LICENSE-MIT) `c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5` | +| `log` | 0.4.34 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/log-0.4.34/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/log-0.4.34/LICENSE-MIT) `6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb` | +| `lru` | 0.18.0 | MIT | [`LICENSE`](third-party/lru-0.18.0/LICENSE) `061dc50af2cd9340703daf61978af3200cf681b12ea67a323c33ba109a23a45e` | +| `memchr` | 2.8.3 | Unlicense OR MIT | [`COPYING`](third-party/memchr-2.8.3/COPYING) `01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f`
[`LICENSE-MIT`](third-party/memchr-2.8.3/LICENSE-MIT) `0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f`
[`UNLICENSE`](third-party/memchr-2.8.3/UNLICENSE) `7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c` | +| `mio` | 1.2.3 | MIT | [`LICENSE`](third-party/mio-1.2.3/LICENSE) `07919255c7e04793d8ea760d6c2ce32d19f9ff02bdbdde3ce90b1e1880929a9b` | +| `miow` | 0.6.1 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/miow-0.6.1/LICENSE-APACHE) `7cfd738c53d61c79f07e348f622bf7707c9084237054d37fbe07788a75f5881c`
[`LICENSE-MIT`](third-party/miow-0.6.1/LICENSE-MIT) `29c9533fc9a32ee529e928bcb12308f0745d237fdea80d83c0258f23243d65d1` | +| `nix` | 0.28.0 | MIT | [`LICENSE`](third-party/nix-0.28.0/LICENSE) `66e3ee1fa7f909ad3c612d556f2a0cdabcd809ad6e66f3b0605015ac64841b70` | +| `num-conv` | 0.2.2 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/num-conv-0.2.2/LICENSE-APACHE) `0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594`
[`LICENSE-MIT`](third-party/num-conv-0.2.2/LICENSE-MIT) `e2e245f2b566d0bfafb0f6a04c16b82d710c4e6e7d799e39bb4ef6e541b85b98` | +| `num-traits` | 0.2.19 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/num-traits-0.2.19/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/num-traits-0.2.19/LICENSE-MIT) `6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb` | +| `num_threads` | 0.1.7 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/num_threads-0.1.7/LICENSE-APACHE) `c69b72788ec261765e460532016b4bb78372f238e449b5e70e3268ec1e18fd15`
[`LICENSE-MIT`](third-party/num_threads-0.1.7/LICENSE-MIT) `b4bf94a9fceb8846320fda938ee53fc16b506572609da5cf1d2d289a5597a8f8` | +| `object` | 0.39.1 | Apache-2.0 OR MIT | [`LICENSE-APACHE`](third-party/object-0.39.1/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/object-0.39.1/LICENSE-MIT) `0b74dfa0bcee5c420c6b7f67b4b2658f9ab8388c97b8e733975f2cecbdd668a6` | +| `palette` | 0.7.6 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/palette-0.7.6/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/palette-0.7.6/LICENSE-MIT) `be1313a4b07ab509c80083966cdd209a7d81d75cd57fcea24441fc9041ccf184` | +| `palette_derive` | 0.7.6 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/palette_derive-0.7.6/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/palette_derive-0.7.6/LICENSE-MIT) `be1313a4b07ab509c80083966cdd209a7d81d75cd57fcea24441fc9041ccf184` | +| `parking_lot` | 0.12.5 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/parking_lot-0.12.5/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/parking_lot-0.12.5/LICENSE-MIT) `c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5` | +| `parking_lot_core` | 0.9.12 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/parking_lot_core-0.9.12/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/parking_lot_core-0.9.12/LICENSE-MIT) `c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5` | +| `pin-project-lite` | 0.2.17 | Apache-2.0 OR MIT | [`LICENSE-APACHE`](third-party/pin-project-lite-0.2.17/LICENSE-APACHE) `0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594`
[`LICENSE-MIT`](third-party/pin-project-lite-0.2.17/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `piper` | 0.2.5 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/piper-0.2.5/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/piper-0.2.5/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `pkg-config` | 0.3.34 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/pkg-config-0.3.34/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/pkg-config-0.3.34/LICENSE-MIT) `378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397` | +| `polling` | 3.11.0 | Apache-2.0 OR MIT | [`LICENSE-APACHE`](third-party/polling-3.11.0/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/polling-3.11.0/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `portable-atomic` | 1.13.1 | Apache-2.0 OR MIT | [`LICENSE-APACHE`](third-party/portable-atomic-1.13.1/LICENSE-APACHE) `0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594`
[`LICENSE-MIT`](third-party/portable-atomic-1.13.1/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `portable-pty` | 0.9.0 | MIT | [`LICENSE.md`](third-party/portable-pty-0.9.0/LICENSE.md) `191c46fcf52061382b1c51a70311eb9081381cc158e5899f3739473a9432185b` | +| `powerfmt` | 0.2.0 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/powerfmt-0.2.0/LICENSE-APACHE) `155420c6403d4e0fca34105e3c03fdd6939b64c393c7ec6f95f5b72c5474eab0`
[`LICENSE-MIT`](third-party/powerfmt-0.2.0/LICENSE-MIT) `070dbc7dda03a29296f2d58bdb9b7331af90f2abc9f31df22875d1eabaf29852` | +| `proc-macro2` | 1.0.107 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/proc-macro2-1.0.107/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/proc-macro2-1.0.107/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `psm` | 0.1.32 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/psm-0.1.32/LICENSE-APACHE) `965a63a81d9a2fbeb5f9096954dabb49690f9dffcdac9825f675b25c807252a2`
[`LICENSE-MIT`](third-party/psm-0.1.32/LICENSE-MIT) `3e3714aa69bd874601741fd7d7ad5298740cece37778e279fc1ab4451c5a11af` | +| `quote` | 1.0.47 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/quote-1.0.47/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/quote-1.0.47/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `r-efi` | 6.0.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | [`AUTHORS`](third-party/r-efi-6.0.0/AUTHORS) `d027e91dbc9cdbb2f1190068e498bd6b61cff022b6a032b191021ba658d96111` | +| `ratatui` | 0.30.2 | MIT | [`LICENSE`](third-party/ratatui-0.30.2/LICENSE) `50eb43e8d742c9c61a9391e42b2184fce54dbd1893a1bb1c85b8c9ee217ab1f5` | +| `ratatui-core` | 0.1.2 | MIT | [`LICENSE`](third-party/ratatui-core-0.1.2/LICENSE) `50eb43e8d742c9c61a9391e42b2184fce54dbd1893a1bb1c85b8c9ee217ab1f5` | +| `ratatui-crossterm` | 0.1.2 | MIT | [`LICENSE`](third-party/ratatui-crossterm-0.1.2/LICENSE) `50eb43e8d742c9c61a9391e42b2184fce54dbd1893a1bb1c85b8c9ee217ab1f5` | +| `ratatui-widgets` | 0.3.2 | MIT | [`LICENSE`](third-party/ratatui-widgets-0.3.2/LICENSE) `50eb43e8d742c9c61a9391e42b2184fce54dbd1893a1bb1c85b8c9ee217ab1f5` | +| `redox_syscall` | 0.5.18 | MIT | [`LICENSE`](third-party/redox_syscall-0.5.18/LICENSE) `efcfee7981ff72431fffb06925cad00a23dce079ed4354f61030ad5abdb78829` | +| `regex` | 1.12.4 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/regex-1.12.4/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/regex-1.12.4/LICENSE-MIT) `6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb` | +| `regex-automata` | 0.4.18 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/regex-automata-0.4.18/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/regex-automata-0.4.18/LICENSE-MIT) `6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb` | +| `regex-syntax` | 0.8.11 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/regex-syntax-0.8.11/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/regex-syntax-0.8.11/LICENSE-MIT) `6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb` | +| `rusqlite` | 0.40.2 | MIT | [`LICENSE`](third-party/rusqlite-0.40.2/LICENSE) `c10c1f27337546471e5f7e4e97fdd398b35b9d4e126115dcd22de8d8e65abf6f` | +| `rustc_version` | 0.4.1 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/rustc_version-0.4.1/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/rustc_version-0.4.1/LICENSE-MIT) `c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5` | +| `rustix` | 1.1.4 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | [`COPYRIGHT`](third-party/rustix-1.1.4/COPYRIGHT) `377c2e7c53250cc5905c0b0532d35973392af16ffb9596a41d99d202cf3617c9`
[`LICENSE-APACHE`](third-party/rustix-1.1.4/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-APACHE-2.0_WITH_LLVM-exception`](third-party/rustix-1.1.4/LICENSE-APACHE-2.0_WITH_LLVM-exception) `268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5`
[`LICENSE-MIT`](third-party/rustix-1.1.4/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `rustix-openpty` | 0.2.0 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | [`COPYRIGHT`](third-party/rustix-openpty-0.2.0/COPYRIGHT) `27292f8969032a60051bbb7501e6114428b8f4a566f2e9b6bb7dc771afc9352b`
[`LICENSE-APACHE`](third-party/rustix-openpty-0.2.0/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-APACHE-2.0_WITH_LLVM-exception`](third-party/rustix-openpty-0.2.0/LICENSE-APACHE-2.0_WITH_LLVM-exception) `268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5`
[`LICENSE-MIT`](third-party/rustix-openpty-0.2.0/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `rustversion` | 1.0.23 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/rustversion-1.0.23/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/rustversion-1.0.23/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `ryu` | 1.0.23 | Apache-2.0 OR BSL-1.0 | [`LICENSE-APACHE`](third-party/ryu-1.0.23/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-BOOST`](third-party/ryu-1.0.23/LICENSE-BOOST) `c9bff75738922193e67fa726fa225535870d2aa1059f91452c411736284ad566` | +| `scopeguard` | 1.2.0 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/scopeguard-1.2.0/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/scopeguard-1.2.0/LICENSE-MIT) `fb77f0a9c53e473abe5103c8632ef9f0f2874d4fb3f17cb2d8c661aab9cee9d7` | +| `semver` | 1.0.28 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/semver-1.0.28/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/semver-1.0.28/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `serde` | 1.0.229 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/serde-1.0.229/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/serde-1.0.229/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `serde_core` | 1.0.229 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/serde_core-1.0.229/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/serde_core-1.0.229/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `serde_derive` | 1.0.229 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/serde_derive-1.0.229/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/serde_derive-1.0.229/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `serde_json` | 1.0.151 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/serde_json-1.0.151/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/serde_json-1.0.151/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `serde_spanned` | 0.6.9 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/serde_spanned-0.6.9/LICENSE-APACHE) `c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08`
[`LICENSE-MIT`](third-party/serde_spanned-0.6.9/LICENSE-MIT) `6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6` | +| `serde_stacker` | 0.1.14 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/serde_stacker-0.1.14/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/serde_stacker-0.1.14/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `serial2` | 0.2.38 | BSD-2-Clause OR Apache-2.0 | [`LICENSE-APACHE`](third-party/serial2-0.2.38/LICENSE-APACHE) `da4d60bfcfd15870aa69e0d0891796d7075a860c54e7b38ec5ccd353c66049ed` | +| `sha2` | 0.11.0 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/sha2-0.11.0/LICENSE-APACHE) `a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5`
[`LICENSE-MIT`](third-party/sha2-0.11.0/LICENSE-MIT) `831e0f43ad0bf014c1c4fec5767aae470434c1d66d6e671be2d823e729063e25` | +| `shared_library` | 0.1.9 | Apache-2.0 OR MIT | [`LICENSE-APACHE`](third-party/shared_library-0.1.9/LICENSE-APACHE) `c144680885b29e4719e2a51f0aab5439a1e02d980692b5aaf086cae12727f28b`
[`LICENSE-MIT`](third-party/shared_library-0.1.9/LICENSE-MIT) `1c07d19ccbe2578665ab7d8c63f71559f890eb8d2a82fa39d0206b7a3414064f` | +| `shell-words` | 1.1.1 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/shell-words-1.1.1/LICENSE-APACHE) `848d7bf14c618c39f1e29fcac2318a2fcc4254045ba7e2f6f5fa799e057225cb`
[`LICENSE-MIT`](third-party/shell-words-1.1.1/LICENSE-MIT) `f76b7831ee8f42d3ac38919101e4ceb9f56aeb7796cdfe8a5ef52e26acedcbbf` | +| `shlex` | 2.0.1 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/shlex-2.0.1/LICENSE-APACHE) `553fffcd9b1cb158bc3e9edc35da85ca5c3b3d7d2e61c883ebcfa8a65814b583`
[`LICENSE-MIT`](third-party/shlex-2.0.1/LICENSE-MIT) `4455bf75a91154108304cb283e0fea9948c14f13e20d60887cf2552449dea3b1` | +| `signal-hook` | 0.3.18 | Apache-2.0 OR MIT | [`LICENSE-APACHE`](third-party/signal-hook-0.3.18/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/signal-hook-0.3.18/LICENSE-MIT) `503558bfefe66ca15e4e3f7955b3cb0ec87fd52f29bf24b336af7bd00e946d5c` | +| `signal-hook` | 0.4.4 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/signal-hook-0.4.4/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/signal-hook-0.4.4/LICENSE-MIT) `503558bfefe66ca15e4e3f7955b3cb0ec87fd52f29bf24b336af7bd00e946d5c` | +| `signal-hook-mio` | 0.2.5 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/signal-hook-mio-0.2.5/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/signal-hook-mio-0.2.5/LICENSE-MIT) `503558bfefe66ca15e4e3f7955b3cb0ec87fd52f29bf24b336af7bd00e946d5c` | +| `signal-hook-registry` | 1.4.8 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/signal-hook-registry-1.4.8/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/signal-hook-registry-1.4.8/LICENSE-MIT) `503558bfefe66ca15e4e3f7955b3cb0ec87fd52f29bf24b336af7bd00e946d5c` | +| `smallvec` | 1.16.1 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/smallvec-1.16.1/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/smallvec-1.16.1/LICENSE-MIT) `0b28172679e0009b655da42797c03fd163a3379d5cfa67ba1f1655e974a2a1a9` | +| `stacker` | 0.1.25 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/stacker-0.1.25/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/stacker-0.1.25/LICENSE-MIT) `378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397` | +| `static_assertions` | 1.1.0 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/static_assertions-1.1.0/LICENSE-APACHE) `cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30`
[`LICENSE-MIT`](third-party/static_assertions-1.1.0/LICENSE-MIT) `ea084a2373ebc1f0902c09266e7bf25a05ab3814c1805bb017ffa7308f90c061` | +| `strsim` | 0.11.1 | MIT | [`LICENSE`](third-party/strsim-0.11.1/LICENSE) `1e697ce8d21401fbf1bddd9b5c3fd4c4c79ae1e3bdf51f81761c85e11d5a89cd` | +| `strum` | 0.28.0 | MIT | [`LICENSE`](third-party/strum-0.28.0/LICENSE) `8bce3b45e49ecd1461f223b46de133d8f62cd39f745cfdaf81bee554b908bd42` | +| `strum_macros` | 0.28.0 | MIT | [`LICENSE`](third-party/strum_macros-0.28.0/LICENSE) `8bce3b45e49ecd1461f223b46de133d8f62cd39f745cfdaf81bee554b908bd42` | +| `syn` | 2.0.119 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/syn-2.0.119/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/syn-2.0.119/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `syn` | 3.0.6 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/syn-3.0.6/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/syn-3.0.6/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `thiserror` | 1.0.69 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/thiserror-1.0.69/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/thiserror-1.0.69/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `thiserror` | 2.0.20 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/thiserror-2.0.20/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/thiserror-2.0.20/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `thiserror-impl` | 1.0.69 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/thiserror-impl-1.0.69/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/thiserror-impl-1.0.69/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `thiserror-impl` | 2.0.20 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/thiserror-impl-2.0.20/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/thiserror-impl-2.0.20/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `time` | 0.3.55 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/time-0.3.55/LICENSE-APACHE) `0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594`
[`LICENSE-MIT`](third-party/time-0.3.55/LICENSE-MIT) `2537228d9a1b44a5dc595241349cae7090b326c8de165aaf89bfddef4a00d0fc` | +| `time-core` | 0.1.9 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/time-core-0.1.9/LICENSE-APACHE) `0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594`
[`LICENSE-MIT`](third-party/time-core-0.1.9/LICENSE-MIT) `2537228d9a1b44a5dc595241349cae7090b326c8de165aaf89bfddef4a00d0fc` | +| `time-macros` | 0.2.32 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/time-macros-0.2.32/LICENSE-APACHE) `0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594`
[`LICENSE-MIT`](third-party/time-macros-0.2.32/LICENSE-MIT) `2537228d9a1b44a5dc595241349cae7090b326c8de165aaf89bfddef4a00d0fc` | +| `toml` | 0.8.23 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/toml-0.8.23/LICENSE-APACHE) `c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08`
[`LICENSE-MIT`](third-party/toml-0.8.23/LICENSE-MIT) `6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6` | +| `toml_datetime` | 0.6.11 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/toml_datetime-0.6.11/LICENSE-APACHE) `c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08`
[`LICENSE-MIT`](third-party/toml_datetime-0.6.11/LICENSE-MIT) `6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6` | +| `toml_edit` | 0.22.27 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/toml_edit-0.22.27/LICENSE-APACHE) `c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08`
[`LICENSE-MIT`](third-party/toml_edit-0.22.27/LICENSE-MIT) `6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6` | +| `typenum` | 1.20.1 | MIT OR Apache-2.0 | [`LICENSE`](third-party/typenum-1.20.1/LICENSE) `db11fec9946737df39ca3898d9cd8c10ec6f6c3a884a6802b0ad0b81b4e8f23a`
[`LICENSE-APACHE`](third-party/typenum-1.20.1/LICENSE-APACHE) `516b24e051bf5630880ebbd55c40a25ce9552ebaf8970a53e8976eb70e522406`
[`LICENSE-MIT`](third-party/typenum-1.20.1/LICENSE-MIT) `a825bd853ab71619a4923d7b4311221427848070ff44d990da39b0b274c1683f` | +| `unicode-ident` | 1.0.26 | (MIT OR Apache-2.0) AND Unicode-3.0 | [`LICENSE-APACHE`](third-party/unicode-ident-1.0.26/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/unicode-ident-1.0.26/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3`
[`LICENSE-UNICODE`](third-party/unicode-ident-1.0.26/LICENSE-UNICODE) `f7db81051789b729fea528a63ec4c938fdcb93d9d61d97dc8cc2e9df6d47f2a1` | +| `unicode-segmentation` | 1.13.3 | MIT OR Apache-2.0 | [`COPYRIGHT`](third-party/unicode-segmentation-1.13.3/COPYRIGHT) `23860c2a7b5d96b21569afedf033469bab9fe14a1b24a35068b8641c578ce24d`
[`LICENSE-APACHE`](third-party/unicode-segmentation-1.13.3/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/unicode-segmentation-1.13.3/LICENSE-MIT) `7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0` | +| `unicode-truncate` | 2.0.1 | MIT OR Apache-2.0 | [`COPYRIGHT`](third-party/unicode-truncate-2.0.1/COPYRIGHT) `23860c2a7b5d96b21569afedf033469bab9fe14a1b24a35068b8641c578ce24d`
[`LICENSE-APACHE`](third-party/unicode-truncate-2.0.1/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/unicode-truncate-2.0.1/LICENSE-MIT) `fa226a5c235dafc28fc9303b03f6bf5f7483c31d8355a031ebd6963ffeb65b95` | +| `unicode-width` | 0.2.2 | MIT OR Apache-2.0 | [`COPYRIGHT`](third-party/unicode-width-0.2.2/COPYRIGHT) `23860c2a7b5d96b21569afedf033469bab9fe14a1b24a35068b8641c578ce24d`
[`LICENSE-APACHE`](third-party/unicode-width-0.2.2/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-MIT`](third-party/unicode-width-0.2.2/LICENSE-MIT) `7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0` | +| `vcpkg` | 0.2.15 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/vcpkg-0.2.15/LICENSE-APACHE) `60c93a31f490375aadf64098b75f10715010379541021e00261045d7800611d3`
[`LICENSE-MIT`](third-party/vcpkg-0.2.15/LICENSE-MIT) `016d20f335060a70e79d9fcf8dfaa6201114d65d592211f4bdfb8ae9ca2bc1dc` | +| `vte` | 0.15.0 | Apache-2.0 OR MIT | [`LICENSE-APACHE`](third-party/vte-0.15.0/LICENSE-APACHE) `62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a`
[`LICENSE-MIT`](third-party/vte-0.15.0/LICENSE-MIT) `e4c9b06fa850cb9b540a5e400e9f6394cf15efcf4098144de477d1d3dae10150` | +| `wasi` | 0.11.1+wasi-snapshot-preview1 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | [`LICENSE-APACHE`](third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-APACHE) `a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2`
[`LICENSE-APACHE-2.0_WITH_LLVM-exception`](third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-APACHE-2.0_WITH_LLVM-exception) `268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5`
[`LICENSE-MIT`](third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | +| `winapi` | 0.3.9 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/winapi-0.3.9/LICENSE-APACHE) `b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1`
[`LICENSE-MIT`](third-party/winapi-0.3.9/LICENSE-MIT) `ce7bc3499fee93d5022ef430d5e4201e79a6d9154f3974e42f41349f0569e09b` | +| `winapi-i686-pc-windows-gnu` | 0.4.0 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/winapi-i686-pc-windows-gnu-0.4.0/LICENSE-APACHE) `b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1`
[`LICENSE-MIT`](third-party/winapi-i686-pc-windows-gnu-0.4.0/LICENSE-MIT) `ce7bc3499fee93d5022ef430d5e4201e79a6d9154f3974e42f41349f0569e09b` | +| `winapi-x86_64-pc-windows-gnu` | 0.4.0 | MIT OR Apache-2.0 | [`LICENSE-APACHE`](third-party/winapi-x86_64-pc-windows-gnu-0.4.0/LICENSE-APACHE) `b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1`
[`LICENSE-MIT`](third-party/winapi-x86_64-pc-windows-gnu-0.4.0/LICENSE-MIT) `ce7bc3499fee93d5022ef430d5e4201e79a6d9154f3974e42f41349f0569e09b` | +| `windows-link` | 0.2.1 | MIT OR Apache-2.0 | [`LICENSE-APACHE-2.0`](third-party/windows-link-0.2.1/LICENSE-APACHE-2.0) `c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b`
[`LICENSE-MIT`](third-party/windows-link-0.2.1/LICENSE-MIT) `c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383` | +| `windows-sys` | 0.59.0 | MIT OR Apache-2.0 | [`LICENSE-APACHE-2.0`](third-party/windows-sys-0.59.0/LICENSE-APACHE-2.0) `c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b`
[`LICENSE-MIT`](third-party/windows-sys-0.59.0/LICENSE-MIT) `c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383` | +| `windows-sys` | 0.61.2 | MIT OR Apache-2.0 | [`LICENSE-APACHE-2.0`](third-party/windows-sys-0.61.2/LICENSE-APACHE-2.0) `c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b`
[`LICENSE-MIT`](third-party/windows-sys-0.61.2/LICENSE-MIT) `c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383` | +| `windows-targets` | 0.52.6 | MIT OR Apache-2.0 | [`LICENSE-APACHE-2.0`](third-party/windows-targets-0.52.6/LICENSE-APACHE-2.0) `c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b`
[`LICENSE-MIT`](third-party/windows-targets-0.52.6/LICENSE-MIT) `c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383` | +| `windows_aarch64_gnullvm` | 0.52.6 | MIT OR Apache-2.0 | [`LICENSE-APACHE-2.0`](third-party/windows_aarch64_gnullvm-0.52.6/LICENSE-APACHE-2.0) `c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b`
[`LICENSE-MIT`](third-party/windows_aarch64_gnullvm-0.52.6/LICENSE-MIT) `c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383` | +| `windows_aarch64_msvc` | 0.52.6 | MIT OR Apache-2.0 | [`LICENSE-APACHE-2.0`](third-party/windows_aarch64_msvc-0.52.6/LICENSE-APACHE-2.0) `c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b`
[`LICENSE-MIT`](third-party/windows_aarch64_msvc-0.52.6/LICENSE-MIT) `c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383` | +| `windows_i686_gnu` | 0.52.6 | MIT OR Apache-2.0 | [`LICENSE-APACHE-2.0`](third-party/windows_i686_gnu-0.52.6/LICENSE-APACHE-2.0) `c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b`
[`LICENSE-MIT`](third-party/windows_i686_gnu-0.52.6/LICENSE-MIT) `c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383` | +| `windows_i686_gnullvm` | 0.52.6 | MIT OR Apache-2.0 | [`LICENSE-APACHE-2.0`](third-party/windows_i686_gnullvm-0.52.6/LICENSE-APACHE-2.0) `c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b`
[`LICENSE-MIT`](third-party/windows_i686_gnullvm-0.52.6/LICENSE-MIT) `c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383` | +| `windows_i686_msvc` | 0.52.6 | MIT OR Apache-2.0 | [`LICENSE-APACHE-2.0`](third-party/windows_i686_msvc-0.52.6/LICENSE-APACHE-2.0) `c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b`
[`LICENSE-MIT`](third-party/windows_i686_msvc-0.52.6/LICENSE-MIT) `c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383` | +| `windows_x86_64_gnu` | 0.52.6 | MIT OR Apache-2.0 | [`LICENSE-APACHE-2.0`](third-party/windows_x86_64_gnu-0.52.6/LICENSE-APACHE-2.0) `c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b`
[`LICENSE-MIT`](third-party/windows_x86_64_gnu-0.52.6/LICENSE-MIT) `c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383` | +| `windows_x86_64_gnullvm` | 0.52.6 | MIT OR Apache-2.0 | [`LICENSE-APACHE-2.0`](third-party/windows_x86_64_gnullvm-0.52.6/LICENSE-APACHE-2.0) `c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b`
[`LICENSE-MIT`](third-party/windows_x86_64_gnullvm-0.52.6/LICENSE-MIT) `c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383` | +| `windows_x86_64_msvc` | 0.52.6 | MIT OR Apache-2.0 | [`LICENSE-APACHE-2.0`](third-party/windows_x86_64_msvc-0.52.6/LICENSE-APACHE-2.0) `c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b`
[`LICENSE-MIT`](third-party/windows_x86_64_msvc-0.52.6/LICENSE-MIT) `c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383` | +| `winnow` | 0.7.15 | MIT | [`LICENSE-MIT`](third-party/winnow-0.7.15/LICENSE-MIT) `cb5aedb296c5246d1f22e9099f925a65146f9f0d6b4eebba97fd27a6cdbbab2d` | +| `winreg` | 0.10.1 | MIT | [`LICENSE`](third-party/winreg-0.10.1/LICENSE) `ea021d8c6383c84d19c15ce90d3b1602cf448fdcc0bf3772c98e43c7bf1bf1d1` | +| `zmij` | 1.0.23 | MIT | [`LICENSE-MIT`](third-party/zmij-1.0.23/LICENSE-MIT) `23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3` | + +## Bundled third-party source notice + +The `bundled` rusqlite feature compiles this SQLite amalgamation into Rover. The exact public-domain blessing block is retained separately from the libsqlite3-sys MIT license. + +| Source component | Bundling crate | License notice | Included notice (SHA-256) | +|---|---|---|---| +| `SQLite 3.53.2` | `libsqlite3-sys 0.38.2` | public-domain blessing | [`SQLITE_BLESSING.txt`](third-party/libsqlite3-sys-0.38.2/SQLITE_BLESSING.txt) `7ba39c32af28155248da75313e83f17a429d9a4cf2ef691b092527d6b2d37b77` | + +## Bundled upstream detection data + +Rover includes the 22 Herdr agent-detection TOML manifests from commit +`c411883ec639c9893ed9c33021c890485e91727b` unchanged. The pinned repository +root is Apache-2.0; its exact license text is retained at +[`upstream/herdr/LICENSE`](upstream/herdr/LICENSE) (SHA-256 +`c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4`). The +per-file path and SHA-256 mapping is recorded in +[`UPSTREAM_SOURCE_AUDIT.md`](../docs/design/UPSTREAM_SOURCE_AUDIT.md). diff --git a/licenses/third-party/aho-corasick-1.1.5/COPYING b/licenses/third-party/aho-corasick-1.1.5/COPYING new file mode 100644 index 0000000..bb9c20a --- /dev/null +++ b/licenses/third-party/aho-corasick-1.1.5/COPYING @@ -0,0 +1,3 @@ +This project is dual-licensed under the Unlicense and MIT licenses. + +You may use this code under the terms of either license. diff --git a/licenses/third-party/aho-corasick-1.1.5/LICENSE-MIT b/licenses/third-party/aho-corasick-1.1.5/LICENSE-MIT new file mode 100644 index 0000000..3b0a5dc --- /dev/null +++ b/licenses/third-party/aho-corasick-1.1.5/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Andrew Gallant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/third-party/aho-corasick-1.1.5/UNLICENSE b/licenses/third-party/aho-corasick-1.1.5/UNLICENSE new file mode 100644 index 0000000..68a49da --- /dev/null +++ b/licenses/third-party/aho-corasick-1.1.5/UNLICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/licenses/third-party/alacritty_terminal-0.26.0/LICENSE-APACHE b/licenses/third-party/alacritty_terminal-0.26.0/LICENSE-APACHE new file mode 100644 index 0000000..9c3147f --- /dev/null +++ b/licenses/third-party/alacritty_terminal-0.26.0/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2020 The Alacritty Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/allocator-api2-0.2.21/LICENSE-APACHE b/licenses/third-party/allocator-api2-0.2.21/LICENSE-APACHE new file mode 100644 index 0000000..a2ce4d9 --- /dev/null +++ b/licenses/third-party/allocator-api2-0.2.21/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/allocator-api2-0.2.21/LICENSE-MIT b/licenses/third-party/allocator-api2-0.2.21/LICENSE-MIT new file mode 100644 index 0000000..458723b --- /dev/null +++ b/licenses/third-party/allocator-api2-0.2.21/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/anyhow-1.0.104/LICENSE-APACHE b/licenses/third-party/anyhow-1.0.104/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/anyhow-1.0.104/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/anyhow-1.0.104/LICENSE-MIT b/licenses/third-party/anyhow-1.0.104/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/anyhow-1.0.104/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/approx-0.5.1/LICENSE b/licenses/third-party/approx-0.5.1/LICENSE new file mode 100644 index 0000000..75b5248 --- /dev/null +++ b/licenses/third-party/approx-0.5.1/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/ar_archive_writer-0.5.3/LICENSE.txt b/licenses/third-party/ar_archive_writer-0.5.3/LICENSE.txt new file mode 100644 index 0000000..f9dc506 --- /dev/null +++ b/licenses/third-party/ar_archive_writer-0.5.3/LICENSE.txt @@ -0,0 +1,219 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + diff --git a/licenses/third-party/arrayvec-0.7.8/LICENSE-APACHE b/licenses/third-party/arrayvec-0.7.8/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/arrayvec-0.7.8/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/arrayvec-0.7.8/LICENSE-MIT b/licenses/third-party/arrayvec-0.7.8/LICENSE-MIT new file mode 100644 index 0000000..db4c067 --- /dev/null +++ b/licenses/third-party/arrayvec-0.7.8/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) Ulrik Sverdrup "bluss" 2015-2023 + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/atomic-waker-1.1.2/LICENSE-APACHE b/licenses/third-party/atomic-waker-1.1.2/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/atomic-waker-1.1.2/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/atomic-waker-1.1.2/LICENSE-MIT b/licenses/third-party/atomic-waker-1.1.2/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/atomic-waker-1.1.2/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/autocfg-1.5.1/LICENSE-APACHE b/licenses/third-party/autocfg-1.5.1/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/autocfg-1.5.1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/autocfg-1.5.1/LICENSE-MIT b/licenses/third-party/autocfg-1.5.1/LICENSE-MIT new file mode 100644 index 0000000..44fbc4d --- /dev/null +++ b/licenses/third-party/autocfg-1.5.1/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2018 Josh Stone + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/base64-0.22.1/LICENSE-APACHE b/licenses/third-party/base64-0.22.1/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/base64-0.22.1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/base64-0.22.1/LICENSE-MIT b/licenses/third-party/base64-0.22.1/LICENSE-MIT new file mode 100644 index 0000000..7bc10f8 --- /dev/null +++ b/licenses/third-party/base64-0.22.1/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Alice Maz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/third-party/bitflags-1.3.2/LICENSE-APACHE b/licenses/third-party/bitflags-1.3.2/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/bitflags-1.3.2/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/bitflags-1.3.2/LICENSE-MIT b/licenses/third-party/bitflags-1.3.2/LICENSE-MIT new file mode 100644 index 0000000..39d4bdb --- /dev/null +++ b/licenses/third-party/bitflags-1.3.2/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/bitflags-2.13.2/LICENSE-APACHE b/licenses/third-party/bitflags-2.13.2/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/bitflags-2.13.2/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/bitflags-2.13.2/LICENSE-MIT b/licenses/third-party/bitflags-2.13.2/LICENSE-MIT new file mode 100644 index 0000000..39d4bdb --- /dev/null +++ b/licenses/third-party/bitflags-2.13.2/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/block-buffer-0.12.1/LICENSE-APACHE b/licenses/third-party/block-buffer-0.12.1/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/licenses/third-party/block-buffer-0.12.1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/block-buffer-0.12.1/LICENSE-MIT b/licenses/third-party/block-buffer-0.12.1/LICENSE-MIT new file mode 100644 index 0000000..ecb2ee5 --- /dev/null +++ b/licenses/third-party/block-buffer-0.12.1/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2018-2025 The RustCrypto Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/by_address-1.2.1/LICENSE-APACHE b/licenses/third-party/by_address-1.2.1/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/by_address-1.2.1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/by_address-1.2.1/LICENSE-MIT b/licenses/third-party/by_address-1.2.1/LICENSE-MIT new file mode 100644 index 0000000..a9d6166 --- /dev/null +++ b/licenses/third-party/by_address-1.2.1/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2017 Matt Brubeck + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/castaway-0.2.4/LICENSE b/licenses/third-party/castaway-0.2.4/LICENSE new file mode 100644 index 0000000..efda8e4 --- /dev/null +++ b/licenses/third-party/castaway-0.2.4/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Stephen M. Coakley + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/cc-1.4.7/LICENSE-APACHE b/licenses/third-party/cc-1.4.7/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/cc-1.4.7/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/cc-1.4.7/LICENSE-MIT b/licenses/third-party/cc-1.4.7/LICENSE-MIT new file mode 100644 index 0000000..39e0ed6 --- /dev/null +++ b/licenses/third-party/cc-1.4.7/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/cfg-if-1.0.5/LICENSE-APACHE b/licenses/third-party/cfg-if-1.0.5/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/cfg-if-1.0.5/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/cfg-if-1.0.5/LICENSE-MIT b/licenses/third-party/cfg-if-1.0.5/LICENSE-MIT new file mode 100644 index 0000000..39e0ed6 --- /dev/null +++ b/licenses/third-party/cfg-if-1.0.5/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/cfg_aliases-0.1.1/LICENSE b/licenses/third-party/cfg_aliases-0.1.1/LICENSE new file mode 100644 index 0000000..f1dcd62 --- /dev/null +++ b/licenses/third-party/cfg_aliases-0.1.1/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2020 Katharos Technology + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/compact_str-0.9.1/LICENSE b/licenses/third-party/compact_str-0.9.1/LICENSE new file mode 100644 index 0000000..12f8fdb --- /dev/null +++ b/licenses/third-party/compact_str-0.9.1/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Parker Timmerman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/concurrent-queue-2.5.0/LICENSE-APACHE b/licenses/third-party/concurrent-queue-2.5.0/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/concurrent-queue-2.5.0/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/concurrent-queue-2.5.0/LICENSE-MIT b/licenses/third-party/concurrent-queue-2.5.0/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/concurrent-queue-2.5.0/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/const-oid-0.10.2/LICENSE-APACHE b/licenses/third-party/const-oid-0.10.2/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/licenses/third-party/const-oid-0.10.2/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/const-oid-0.10.2/LICENSE-MIT b/licenses/third-party/const-oid-0.10.2/LICENSE-MIT new file mode 100644 index 0000000..b135e18 --- /dev/null +++ b/licenses/third-party/const-oid-0.10.2/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2020-2026 The RustCrypto Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/convert_case-0.10.0/LICENSE b/licenses/third-party/convert_case-0.10.0/LICENSE new file mode 100644 index 0000000..c0ce8f0 --- /dev/null +++ b/licenses/third-party/convert_case-0.10.0/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 rutrum + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/licenses/third-party/cpufeatures-0.3.1/LICENSE-APACHE b/licenses/third-party/cpufeatures-0.3.1/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/licenses/third-party/cpufeatures-0.3.1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/cpufeatures-0.3.1/LICENSE-MIT b/licenses/third-party/cpufeatures-0.3.1/LICENSE-MIT new file mode 100644 index 0000000..b135e18 --- /dev/null +++ b/licenses/third-party/cpufeatures-0.3.1/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2020-2026 The RustCrypto Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/crossbeam-utils-0.8.23/LICENSE-APACHE b/licenses/third-party/crossbeam-utils-0.8.23/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/crossbeam-utils-0.8.23/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/crossbeam-utils-0.8.23/LICENSE-MIT b/licenses/third-party/crossbeam-utils-0.8.23/LICENSE-MIT new file mode 100644 index 0000000..068d491 --- /dev/null +++ b/licenses/third-party/crossbeam-utils-0.8.23/LICENSE-MIT @@ -0,0 +1,27 @@ +The MIT License (MIT) + +Copyright (c) 2019 The Crossbeam Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/crossterm-0.29.0/LICENSE b/licenses/third-party/crossterm-0.29.0/LICENSE new file mode 100644 index 0000000..5618e2a --- /dev/null +++ b/licenses/third-party/crossterm-0.29.0/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Timon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/crossterm_winapi-0.9.1/LICENSE b/licenses/third-party/crossterm_winapi-0.9.1/LICENSE new file mode 100644 index 0000000..5618e2a --- /dev/null +++ b/licenses/third-party/crossterm_winapi-0.9.1/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Timon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/crypto-common-0.2.2/LICENSE-APACHE b/licenses/third-party/crypto-common-0.2.2/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/licenses/third-party/crypto-common-0.2.2/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/crypto-common-0.2.2/LICENSE-MIT b/licenses/third-party/crypto-common-0.2.2/LICENSE-MIT new file mode 100644 index 0000000..4296756 --- /dev/null +++ b/licenses/third-party/crypto-common-0.2.2/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2021-2026 RustCrypto Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/cursor-icon-1.2.0/LICENSE-APACHE b/licenses/third-party/cursor-icon-1.2.0/LICENSE-APACHE new file mode 100644 index 0000000..64739dd --- /dev/null +++ b/licenses/third-party/cursor-icon-1.2.0/LICENSE-APACHE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2023 Kirill Chibisov + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/cursor-icon-1.2.0/LICENSE-MIT b/licenses/third-party/cursor-icon-1.2.0/LICENSE-MIT new file mode 100644 index 0000000..f14a2e3 --- /dev/null +++ b/licenses/third-party/cursor-icon-1.2.0/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) 2023 Kirill Chibisov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/darling-0.24.1/LICENSE b/licenses/third-party/darling-0.24.1/LICENSE new file mode 100644 index 0000000..0b48ead --- /dev/null +++ b/licenses/third-party/darling-0.24.1/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Ted Driggs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/darling_core-0.24.1/LICENSE b/licenses/third-party/darling_core-0.24.1/LICENSE new file mode 100644 index 0000000..0b48ead --- /dev/null +++ b/licenses/third-party/darling_core-0.24.1/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Ted Driggs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/darling_macro-0.24.1/LICENSE b/licenses/third-party/darling_macro-0.24.1/LICENSE new file mode 100644 index 0000000..0b48ead --- /dev/null +++ b/licenses/third-party/darling_macro-0.24.1/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Ted Driggs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/deranged-0.5.8/LICENSE-APACHE b/licenses/third-party/deranged-0.5.8/LICENSE-APACHE new file mode 100644 index 0000000..c763a0c --- /dev/null +++ b/licenses/third-party/deranged-0.5.8/LICENSE-APACHE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 Jacob Pratt et al. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/deranged-0.5.8/LICENSE-MIT b/licenses/third-party/deranged-0.5.8/LICENSE-MIT new file mode 100644 index 0000000..5cc097f --- /dev/null +++ b/licenses/third-party/deranged-0.5.8/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) 2024 Jacob Pratt et al. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/derive_more-2.1.1/LICENSE b/licenses/third-party/derive_more-2.1.1/LICENSE new file mode 100644 index 0000000..602cf05 --- /dev/null +++ b/licenses/third-party/derive_more-2.1.1/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Jelte Fennema + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/derive_more-impl-2.1.1/LICENSE b/licenses/third-party/derive_more-impl-2.1.1/LICENSE new file mode 100644 index 0000000..602cf05 --- /dev/null +++ b/licenses/third-party/derive_more-impl-2.1.1/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Jelte Fennema + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/digest-0.11.3/LICENSE-APACHE b/licenses/third-party/digest-0.11.3/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/licenses/third-party/digest-0.11.3/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/digest-0.11.3/LICENSE-MIT b/licenses/third-party/digest-0.11.3/LICENSE-MIT new file mode 100644 index 0000000..37f14fe --- /dev/null +++ b/licenses/third-party/digest-0.11.3/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (c) 2017-2025 RustCrypto Developers +Copyright (c) 2017 Artyom Pavlov + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/document-features-0.2.12/LICENSE-APACHE b/licenses/third-party/document-features-0.2.12/LICENSE-APACHE new file mode 100644 index 0000000..137069b --- /dev/null +++ b/licenses/third-party/document-features-0.2.12/LICENSE-APACHE @@ -0,0 +1,73 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/document-features-0.2.12/LICENSE-MIT b/licenses/third-party/document-features-0.2.12/LICENSE-MIT new file mode 100644 index 0000000..13a82b2 --- /dev/null +++ b/licenses/third-party/document-features-0.2.12/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) 2020 Olivier Goffart + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/downcast-rs-1.2.1/LICENSE-APACHE b/licenses/third-party/downcast-rs-1.2.1/LICENSE-APACHE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/licenses/third-party/downcast-rs-1.2.1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/downcast-rs-1.2.1/LICENSE-MIT b/licenses/third-party/downcast-rs-1.2.1/LICENSE-MIT new file mode 100644 index 0000000..7046862 --- /dev/null +++ b/licenses/third-party/downcast-rs-1.2.1/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2020 Ashish Myles and contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/either-1.18.0/LICENSE-APACHE b/licenses/third-party/either-1.18.0/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/either-1.18.0/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/either-1.18.0/LICENSE-MIT b/licenses/third-party/either-1.18.0/LICENSE-MIT new file mode 100644 index 0000000..9203baa --- /dev/null +++ b/licenses/third-party/either-1.18.0/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2015 + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/equivalent-1.0.2/LICENSE-APACHE b/licenses/third-party/equivalent-1.0.2/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/equivalent-1.0.2/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/equivalent-1.0.2/LICENSE-MIT b/licenses/third-party/equivalent-1.0.2/LICENSE-MIT new file mode 100644 index 0000000..5ac40da --- /dev/null +++ b/licenses/third-party/equivalent-1.0.2/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016--2023 + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/errno-0.3.14/LICENSE-APACHE b/licenses/third-party/errno-0.3.14/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/errno-0.3.14/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/errno-0.3.14/LICENSE-MIT b/licenses/third-party/errno-0.3.14/LICENSE-MIT new file mode 100644 index 0000000..66b6578 --- /dev/null +++ b/licenses/third-party/errno-0.3.14/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 Chris Wong + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/fallible-iterator-0.3.0/LICENSE-APACHE b/licenses/third-party/fallible-iterator-0.3.0/LICENSE-APACHE new file mode 100644 index 0000000..8f71f43 --- /dev/null +++ b/licenses/third-party/fallible-iterator-0.3.0/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/licenses/third-party/fallible-iterator-0.3.0/LICENSE-MIT b/licenses/third-party/fallible-iterator-0.3.0/LICENSE-MIT new file mode 100644 index 0000000..a7cfbe0 --- /dev/null +++ b/licenses/third-party/fallible-iterator-0.3.0/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) 2015 The rust-openssl-verify Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/fallible-streaming-iterator-0.1.9/LICENSE-APACHE b/licenses/third-party/fallible-streaming-iterator-0.1.9/LICENSE-APACHE new file mode 100644 index 0000000..8f71f43 --- /dev/null +++ b/licenses/third-party/fallible-streaming-iterator-0.1.9/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/licenses/third-party/fallible-streaming-iterator-0.1.9/LICENSE-MIT b/licenses/third-party/fallible-streaming-iterator-0.1.9/LICENSE-MIT new file mode 100644 index 0000000..41aaea3 --- /dev/null +++ b/licenses/third-party/fallible-streaming-iterator-0.1.9/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) 2016 The fallible-streaming-iterator Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/fast-srgb8-1.0.0/LICENSE-APACHE b/licenses/third-party/fast-srgb8-1.0.0/LICENSE-APACHE new file mode 100644 index 0000000..a32595f --- /dev/null +++ b/licenses/third-party/fast-srgb8-1.0.0/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2016 The Miri Developers + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/fast-srgb8-1.0.0/LICENSE-CC0 b/licenses/third-party/fast-srgb8-1.0.0/LICENSE-CC0 new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/licenses/third-party/fast-srgb8-1.0.0/LICENSE-CC0 @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/licenses/third-party/fast-srgb8-1.0.0/LICENSE-MIT b/licenses/third-party/fast-srgb8-1.0.0/LICENSE-MIT new file mode 100644 index 0000000..6223e8d --- /dev/null +++ b/licenses/third-party/fast-srgb8-1.0.0/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2021 Thom Chiovoloni + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/licenses/third-party/fastrand-2.5.0/LICENSE-APACHE b/licenses/third-party/fastrand-2.5.0/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/fastrand-2.5.0/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/fastrand-2.5.0/LICENSE-MIT b/licenses/third-party/fastrand-2.5.0/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/fastrand-2.5.0/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/filedescriptor-0.8.3/LICENSE.md b/licenses/third-party/filedescriptor-0.8.3/LICENSE.md new file mode 100644 index 0000000..7a93d12 --- /dev/null +++ b/licenses/third-party/filedescriptor-0.8.3/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Wez Furlong + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/find-msvc-tools-0.1.13/LICENSE-APACHE b/licenses/third-party/find-msvc-tools-0.1.13/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/find-msvc-tools-0.1.13/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/find-msvc-tools-0.1.13/LICENSE-MIT b/licenses/third-party/find-msvc-tools-0.1.13/LICENSE-MIT new file mode 100644 index 0000000..39e0ed6 --- /dev/null +++ b/licenses/third-party/find-msvc-tools-0.1.13/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/foldhash-0.2.0/LICENSE b/licenses/third-party/foldhash-0.2.0/LICENSE new file mode 100644 index 0000000..2f65b0a --- /dev/null +++ b/licenses/third-party/foldhash-0.2.0/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2024 Orson Peters + +This software is provided 'as-is', without any express or implied warranty. In +no event will the authors be held liable for any damages arising from the use of +this software. + +Permission is granted to anyone to use this software for any purpose, including +commercial applications, and to alter it and redistribute it freely, subject to +the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. \ No newline at end of file diff --git a/licenses/third-party/futures-io-0.3.34/LICENSE-APACHE b/licenses/third-party/futures-io-0.3.34/LICENSE-APACHE new file mode 100644 index 0000000..9eb0b09 --- /dev/null +++ b/licenses/third-party/futures-io-0.3.34/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright (c) 2016 Alex Crichton +Copyright (c) 2017 The Tokio Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/futures-io-0.3.34/LICENSE-MIT b/licenses/third-party/futures-io-0.3.34/LICENSE-MIT new file mode 100644 index 0000000..8ad082e --- /dev/null +++ b/licenses/third-party/futures-io-0.3.34/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (c) 2016 Alex Crichton +Copyright (c) 2017 The Tokio Authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/getrandom-0.4.3/LICENSE-APACHE b/licenses/third-party/getrandom-0.4.3/LICENSE-APACHE new file mode 100644 index 0000000..17d7468 --- /dev/null +++ b/licenses/third-party/getrandom-0.4.3/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/getrandom-0.4.3/LICENSE-MIT b/licenses/third-party/getrandom-0.4.3/LICENSE-MIT new file mode 100644 index 0000000..cd3f7cb --- /dev/null +++ b/licenses/third-party/getrandom-0.4.3/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (c) 2018-2026 The rust-random Project Developers +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/hashbrown-0.16.1/LICENSE-APACHE b/licenses/third-party/hashbrown-0.16.1/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/hashbrown-0.16.1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/hashbrown-0.16.1/LICENSE-MIT b/licenses/third-party/hashbrown-0.16.1/LICENSE-MIT new file mode 100644 index 0000000..5afc2a7 --- /dev/null +++ b/licenses/third-party/hashbrown-0.16.1/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016 Amanieu d'Antras + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/hashbrown-0.17.1/LICENSE-APACHE b/licenses/third-party/hashbrown-0.17.1/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/hashbrown-0.17.1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/hashbrown-0.17.1/LICENSE-MIT b/licenses/third-party/hashbrown-0.17.1/LICENSE-MIT new file mode 100644 index 0000000..5afc2a7 --- /dev/null +++ b/licenses/third-party/hashbrown-0.17.1/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016 Amanieu d'Antras + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/heck-0.5.0/LICENSE-APACHE b/licenses/third-party/heck-0.5.0/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/heck-0.5.0/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/heck-0.5.0/LICENSE-MIT b/licenses/third-party/heck-0.5.0/LICENSE-MIT new file mode 100644 index 0000000..e69282e --- /dev/null +++ b/licenses/third-party/heck-0.5.0/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2015 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/hermit-abi-0.5.3/LICENSE-APACHE b/licenses/third-party/hermit-abi-0.5.3/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/hermit-abi-0.5.3/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/hermit-abi-0.5.3/LICENSE-MIT b/licenses/third-party/hermit-abi-0.5.3/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/hermit-abi-0.5.3/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/home-0.5.12/LICENSE-APACHE b/licenses/third-party/home-0.5.12/LICENSE-APACHE new file mode 100644 index 0000000..c98d27d --- /dev/null +++ b/licenses/third-party/home-0.5.12/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/LICENSE-2.0 + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/home-0.5.12/LICENSE-MIT b/licenses/third-party/home-0.5.12/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/home-0.5.12/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/hybrid-array-0.4.15/LICENSE-APACHE b/licenses/third-party/hybrid-array-0.4.15/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/licenses/third-party/hybrid-array-0.4.15/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/hybrid-array-0.4.15/LICENSE-MIT b/licenses/third-party/hybrid-array-0.4.15/LICENSE-MIT new file mode 100644 index 0000000..5b37ba5 --- /dev/null +++ b/licenses/third-party/hybrid-array-0.4.15/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2022-2026 The RustCrypto Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/ident_case-1.0.1/LICENSE b/licenses/third-party/ident_case-1.0.1/LICENSE new file mode 100644 index 0000000..9cf1062 --- /dev/null +++ b/licenses/third-party/ident_case-1.0.1/LICENSE @@ -0,0 +1,19 @@ +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/indexmap-2.14.2/LICENSE-APACHE b/licenses/third-party/indexmap-2.14.2/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/indexmap-2.14.2/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/indexmap-2.14.2/LICENSE-MIT b/licenses/third-party/indexmap-2.14.2/LICENSE-MIT new file mode 100644 index 0000000..8b81810 --- /dev/null +++ b/licenses/third-party/indexmap-2.14.2/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016--2017 + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/indoc-2.0.7/LICENSE-APACHE b/licenses/third-party/indoc-2.0.7/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/indoc-2.0.7/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/indoc-2.0.7/LICENSE-MIT b/licenses/third-party/indoc-2.0.7/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/indoc-2.0.7/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/instability-0.3.13/LICENSE.md b/licenses/third-party/instability-0.3.13/LICENSE.md new file mode 100644 index 0000000..6144b39 --- /dev/null +++ b/licenses/third-party/instability-0.3.13/LICENSE.md @@ -0,0 +1,22 @@ +# MIT License + +Copyright (c) 2020 Stephen M. Coakley +Copyright (c) The Ratatui Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/itertools-0.14.0/LICENSE-APACHE b/licenses/third-party/itertools-0.14.0/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/itertools-0.14.0/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/itertools-0.14.0/LICENSE-MIT b/licenses/third-party/itertools-0.14.0/LICENSE-MIT new file mode 100644 index 0000000..9203baa --- /dev/null +++ b/licenses/third-party/itertools-0.14.0/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2015 + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/itoa-1.0.18/LICENSE-APACHE b/licenses/third-party/itoa-1.0.18/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/itoa-1.0.18/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/itoa-1.0.18/LICENSE-MIT b/licenses/third-party/itoa-1.0.18/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/itoa-1.0.18/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/kasuari-0.4.12/LICENSE-APACHE b/licenses/third-party/kasuari-0.4.12/LICENSE-APACHE new file mode 100644 index 0000000..6f75635 --- /dev/null +++ b/licenses/third-party/kasuari-0.4.12/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/kasuari-0.4.12/LICENSE-MIT b/licenses/third-party/kasuari-0.4.12/LICENSE-MIT new file mode 100644 index 0000000..7226a09 --- /dev/null +++ b/licenses/third-party/kasuari-0.4.12/LICENSE-MIT @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016 Dylan Ede +Copyright (c) 2024 Josh McKinney + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/lazy_static-1.5.0/LICENSE-APACHE b/licenses/third-party/lazy_static-1.5.0/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/lazy_static-1.5.0/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/lazy_static-1.5.0/LICENSE-MIT b/licenses/third-party/lazy_static-1.5.0/LICENSE-MIT new file mode 100644 index 0000000..25597d5 --- /dev/null +++ b/licenses/third-party/lazy_static-1.5.0/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2010 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/libc-0.2.189/LICENSE-APACHE b/licenses/third-party/libc-0.2.189/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/libc-0.2.189/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/libc-0.2.189/LICENSE-MIT b/licenses/third-party/libc-0.2.189/LICENSE-MIT new file mode 100644 index 0000000..f4095b2 --- /dev/null +++ b/licenses/third-party/libc-0.2.189/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/libm-0.2.16/LICENSE.txt b/licenses/third-party/libm-0.2.16/LICENSE.txt new file mode 100644 index 0000000..2f8e41f --- /dev/null +++ b/licenses/third-party/libm-0.2.16/LICENSE.txt @@ -0,0 +1,258 @@ +rust-lang/libm as a whole is available for use under the MIT license: + +------------------------------------------------------------------------------ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ + +As a contributor, you agree that your code can be used under either the MIT +license or the Apache-2.0 license: + +------------------------------------------------------------------------------ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +------------------------------------------------------------------------------ + +This Rust library contains the following copyrights: + + Copyright (c) 2018 Jorge Aparicio + +Portions of this software are derived from third-party works licensed under +terms compatible with the above MIT license: + +* musl libc https://www.musl-libc.org/. This library contains the following + copyright: + + Copyright © 2005-2020 Rich Felker, et al. + +* The CORE-MATH project https://core-math.gitlabpages.inria.fr/. CORE-MATH + routines are available under the MIT license on a per-file basis. + +The musl libc COPYRIGHT file also includes the following notice relevant to +math portions of the library: + +------------------------------------------------------------------------------ +Much of the math library code (src/math/* and src/complex/*) is +Copyright © 1993,2004 Sun Microsystems or +Copyright © 2003-2011 David Schultz or +Copyright © 2003-2009 Steven G. Kargl or +Copyright © 2003-2009 Bruce D. Evans or +Copyright © 2008 Stephen L. Moshier or +Copyright © 2017-2018 Arm Limited +and labelled as such in comments in the individual source files. All +have been licensed under extremely permissive terms. +------------------------------------------------------------------------------ + +Copyright notices are retained in src/* files where relevant. diff --git a/licenses/third-party/libsqlite3-sys-0.38.2/LICENSE b/licenses/third-party/libsqlite3-sys-0.38.2/LICENSE new file mode 100644 index 0000000..08c660f --- /dev/null +++ b/licenses/third-party/libsqlite3-sys-0.38.2/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 The rusqlite developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/third-party/libsqlite3-sys-0.38.2/SQLITE_BLESSING.txt b/licenses/third-party/libsqlite3-sys-0.38.2/SQLITE_BLESSING.txt new file mode 100644 index 0000000..602ee43 --- /dev/null +++ b/licenses/third-party/libsqlite3-sys-0.38.2/SQLITE_BLESSING.txt @@ -0,0 +1,7 @@ +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** diff --git a/licenses/third-party/line-clipping-0.3.7/LICENSE-APACHE b/licenses/third-party/line-clipping-0.3.7/LICENSE-APACHE new file mode 100644 index 0000000..e4c0503 --- /dev/null +++ b/licenses/third-party/line-clipping-0.3.7/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 Josh McKinney + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/line-clipping-0.3.7/LICENSE-MIT b/licenses/third-party/line-clipping-0.3.7/LICENSE-MIT new file mode 100644 index 0000000..94a0c78 --- /dev/null +++ b/licenses/third-party/line-clipping-0.3.7/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Josh McKinney + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/linux-raw-sys-0.12.1/COPYRIGHT b/licenses/third-party/linux-raw-sys-0.12.1/COPYRIGHT new file mode 100644 index 0000000..719e3db --- /dev/null +++ b/licenses/third-party/linux-raw-sys-0.12.1/COPYRIGHT @@ -0,0 +1,29 @@ +Short version for non-lawyers: + +`linux-raw-sys` is triple-licensed under Apache 2.0 with the LLVM Exception, +Apache 2.0, and MIT terms. + + +Longer version: + +Copyrights in the `linux-raw-sys` project are retained by their contributors. +No copyright assignment is required to contribute to the `linux-raw-sys` +project. + +Some files include code derived from Rust's `libstd`; see the comments in +the code for details. + +Except as otherwise noted (below and/or in individual files), `linux-raw-sys` +is licensed under: + + - the Apache License, Version 2.0, with the LLVM Exception + or + + - the Apache License, Version 2.0 + or + , + - or the MIT license + or + , + +at your option. diff --git a/licenses/third-party/linux-raw-sys-0.12.1/LICENSE-APACHE b/licenses/third-party/linux-raw-sys-0.12.1/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/linux-raw-sys-0.12.1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/linux-raw-sys-0.12.1/LICENSE-APACHE-2.0_WITH_LLVM-exception b/licenses/third-party/linux-raw-sys-0.12.1/LICENSE-APACHE-2.0_WITH_LLVM-exception new file mode 100644 index 0000000..f9d8195 --- /dev/null +++ b/licenses/third-party/linux-raw-sys-0.12.1/LICENSE-APACHE-2.0_WITH_LLVM-exception @@ -0,0 +1,220 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + diff --git a/licenses/third-party/linux-raw-sys-0.12.1/LICENSE-MIT b/licenses/third-party/linux-raw-sys-0.12.1/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/linux-raw-sys-0.12.1/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/litrs-1.0.0/LICENSE-APACHE b/licenses/third-party/litrs-1.0.0/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/litrs-1.0.0/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/litrs-1.0.0/LICENSE-MIT b/licenses/third-party/litrs-1.0.0/LICENSE-MIT new file mode 100644 index 0000000..4fa8658 --- /dev/null +++ b/licenses/third-party/litrs-1.0.0/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2020 Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/lock_api-0.4.14/LICENSE-APACHE b/licenses/third-party/lock_api-0.4.14/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/lock_api-0.4.14/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/lock_api-0.4.14/LICENSE-MIT b/licenses/third-party/lock_api-0.4.14/LICENSE-MIT new file mode 100644 index 0000000..40b8817 --- /dev/null +++ b/licenses/third-party/lock_api-0.4.14/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/log-0.4.34/LICENSE-APACHE b/licenses/third-party/log-0.4.34/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/log-0.4.34/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/log-0.4.34/LICENSE-MIT b/licenses/third-party/log-0.4.34/LICENSE-MIT new file mode 100644 index 0000000..39d4bdb --- /dev/null +++ b/licenses/third-party/log-0.4.34/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/lru-0.18.0/LICENSE b/licenses/third-party/lru-0.18.0/LICENSE new file mode 100644 index 0000000..0b3d7a8 --- /dev/null +++ b/licenses/third-party/lru-0.18.0/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Jerome Froelich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/licenses/third-party/memchr-2.8.3/COPYING b/licenses/third-party/memchr-2.8.3/COPYING new file mode 100644 index 0000000..bb9c20a --- /dev/null +++ b/licenses/third-party/memchr-2.8.3/COPYING @@ -0,0 +1,3 @@ +This project is dual-licensed under the Unlicense and MIT licenses. + +You may use this code under the terms of either license. diff --git a/licenses/third-party/memchr-2.8.3/LICENSE-MIT b/licenses/third-party/memchr-2.8.3/LICENSE-MIT new file mode 100644 index 0000000..3b0a5dc --- /dev/null +++ b/licenses/third-party/memchr-2.8.3/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Andrew Gallant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/third-party/memchr-2.8.3/UNLICENSE b/licenses/third-party/memchr-2.8.3/UNLICENSE new file mode 100644 index 0000000..68a49da --- /dev/null +++ b/licenses/third-party/memchr-2.8.3/UNLICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/licenses/third-party/mio-1.2.3/LICENSE b/licenses/third-party/mio-1.2.3/LICENSE new file mode 100644 index 0000000..3516413 --- /dev/null +++ b/licenses/third-party/mio-1.2.3/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 Carl Lerche and other MIO contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/third-party/miow-0.6.1/LICENSE-APACHE b/licenses/third-party/miow-0.6.1/LICENSE-APACHE new file mode 100644 index 0000000..f47c941 --- /dev/null +++ b/licenses/third-party/miow-0.6.1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/miow-0.6.1/LICENSE-MIT b/licenses/third-party/miow-0.6.1/LICENSE-MIT new file mode 100644 index 0000000..a22128a --- /dev/null +++ b/licenses/third-party/miow-0.6.1/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/nix-0.28.0/LICENSE b/licenses/third-party/nix-0.28.0/LICENSE new file mode 100644 index 0000000..aff9096 --- /dev/null +++ b/licenses/third-party/nix-0.28.0/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Carl Lerche + nix-rust Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/third-party/num-conv-0.2.2/LICENSE-APACHE b/licenses/third-party/num-conv-0.2.2/LICENSE-APACHE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/licenses/third-party/num-conv-0.2.2/LICENSE-APACHE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/num-conv-0.2.2/LICENSE-MIT b/licenses/third-party/num-conv-0.2.2/LICENSE-MIT new file mode 100644 index 0000000..3738d28 --- /dev/null +++ b/licenses/third-party/num-conv-0.2.2/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) Jacob Pratt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/num-traits-0.2.19/LICENSE-APACHE b/licenses/third-party/num-traits-0.2.19/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/num-traits-0.2.19/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/num-traits-0.2.19/LICENSE-MIT b/licenses/third-party/num-traits-0.2.19/LICENSE-MIT new file mode 100644 index 0000000..39d4bdb --- /dev/null +++ b/licenses/third-party/num-traits-0.2.19/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/num_threads-0.1.7/LICENSE-APACHE b/licenses/third-party/num_threads-0.1.7/LICENSE-APACHE new file mode 100644 index 0000000..8119b40 --- /dev/null +++ b/licenses/third-party/num_threads-0.1.7/LICENSE-APACHE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 Jacob Pratt + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/num_threads-0.1.7/LICENSE-MIT b/licenses/third-party/num_threads-0.1.7/LICENSE-MIT new file mode 100644 index 0000000..8daf823 --- /dev/null +++ b/licenses/third-party/num_threads-0.1.7/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) 2021 Jacob Pratt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/object-0.39.1/LICENSE-APACHE b/licenses/third-party/object-0.39.1/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/object-0.39.1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/object-0.39.1/LICENSE-MIT b/licenses/third-party/object-0.39.1/LICENSE-MIT new file mode 100644 index 0000000..8440c7e --- /dev/null +++ b/licenses/third-party/object-0.39.1/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2015 The Gimli Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/palette-0.7.6/LICENSE-APACHE b/licenses/third-party/palette-0.7.6/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/palette-0.7.6/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/palette-0.7.6/LICENSE-MIT b/licenses/third-party/palette-0.7.6/LICENSE-MIT new file mode 100644 index 0000000..53bf729 --- /dev/null +++ b/licenses/third-party/palette-0.7.6/LICENSE-MIT @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Erik Hedvall + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/licenses/third-party/palette_derive-0.7.6/LICENSE-APACHE b/licenses/third-party/palette_derive-0.7.6/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/palette_derive-0.7.6/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/palette_derive-0.7.6/LICENSE-MIT b/licenses/third-party/palette_derive-0.7.6/LICENSE-MIT new file mode 100644 index 0000000..53bf729 --- /dev/null +++ b/licenses/third-party/palette_derive-0.7.6/LICENSE-MIT @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Erik Hedvall + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/licenses/third-party/parking_lot-0.12.5/LICENSE-APACHE b/licenses/third-party/parking_lot-0.12.5/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/parking_lot-0.12.5/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/parking_lot-0.12.5/LICENSE-MIT b/licenses/third-party/parking_lot-0.12.5/LICENSE-MIT new file mode 100644 index 0000000..40b8817 --- /dev/null +++ b/licenses/third-party/parking_lot-0.12.5/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/parking_lot_core-0.9.12/LICENSE-APACHE b/licenses/third-party/parking_lot_core-0.9.12/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/parking_lot_core-0.9.12/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/parking_lot_core-0.9.12/LICENSE-MIT b/licenses/third-party/parking_lot_core-0.9.12/LICENSE-MIT new file mode 100644 index 0000000..40b8817 --- /dev/null +++ b/licenses/third-party/parking_lot_core-0.9.12/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/pin-project-lite-0.2.17/LICENSE-APACHE b/licenses/third-party/pin-project-lite-0.2.17/LICENSE-APACHE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/licenses/third-party/pin-project-lite-0.2.17/LICENSE-APACHE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/pin-project-lite-0.2.17/LICENSE-MIT b/licenses/third-party/pin-project-lite-0.2.17/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/pin-project-lite-0.2.17/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/piper-0.2.5/LICENSE-APACHE b/licenses/third-party/piper-0.2.5/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/piper-0.2.5/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/piper-0.2.5/LICENSE-MIT b/licenses/third-party/piper-0.2.5/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/piper-0.2.5/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/pkg-config-0.3.34/LICENSE-APACHE b/licenses/third-party/pkg-config-0.3.34/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/pkg-config-0.3.34/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/pkg-config-0.3.34/LICENSE-MIT b/licenses/third-party/pkg-config-0.3.34/LICENSE-MIT new file mode 100644 index 0000000..39e0ed6 --- /dev/null +++ b/licenses/third-party/pkg-config-0.3.34/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/polling-3.11.0/LICENSE-APACHE b/licenses/third-party/polling-3.11.0/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/polling-3.11.0/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/polling-3.11.0/LICENSE-MIT b/licenses/third-party/polling-3.11.0/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/polling-3.11.0/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/portable-atomic-1.13.1/LICENSE-APACHE b/licenses/third-party/portable-atomic-1.13.1/LICENSE-APACHE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/licenses/third-party/portable-atomic-1.13.1/LICENSE-APACHE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/portable-atomic-1.13.1/LICENSE-MIT b/licenses/third-party/portable-atomic-1.13.1/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/portable-atomic-1.13.1/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/portable-pty-0.9.0/LICENSE.md b/licenses/third-party/portable-pty-0.9.0/LICENSE.md new file mode 100644 index 0000000..7a93d12 --- /dev/null +++ b/licenses/third-party/portable-pty-0.9.0/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Wez Furlong + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/powerfmt-0.2.0/LICENSE-APACHE b/licenses/third-party/powerfmt-0.2.0/LICENSE-APACHE new file mode 100644 index 0000000..ddde1f9 --- /dev/null +++ b/licenses/third-party/powerfmt-0.2.0/LICENSE-APACHE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2023 Jacob Pratt et al. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/powerfmt-0.2.0/LICENSE-MIT b/licenses/third-party/powerfmt-0.2.0/LICENSE-MIT new file mode 100644 index 0000000..89c1f78 --- /dev/null +++ b/licenses/third-party/powerfmt-0.2.0/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) 2023 Jacob Pratt et al. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/proc-macro2-1.0.107/LICENSE-APACHE b/licenses/third-party/proc-macro2-1.0.107/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/proc-macro2-1.0.107/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/proc-macro2-1.0.107/LICENSE-MIT b/licenses/third-party/proc-macro2-1.0.107/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/proc-macro2-1.0.107/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/psm-0.1.32/LICENSE-APACHE b/licenses/third-party/psm-0.1.32/LICENSE-APACHE new file mode 100644 index 0000000..a3711ba --- /dev/null +++ b/licenses/third-party/psm-0.1.32/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/psm-0.1.32/LICENSE-MIT b/licenses/third-party/psm-0.1.32/LICENSE-MIT new file mode 100644 index 0000000..5f991a0 --- /dev/null +++ b/licenses/third-party/psm-0.1.32/LICENSE-MIT @@ -0,0 +1,16 @@ +Copyright © 2018, Simonas Kazlauskas + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/quote-1.0.47/LICENSE-APACHE b/licenses/third-party/quote-1.0.47/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/quote-1.0.47/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/quote-1.0.47/LICENSE-MIT b/licenses/third-party/quote-1.0.47/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/quote-1.0.47/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/r-efi-6.0.0/AUTHORS b/licenses/third-party/r-efi-6.0.0/AUTHORS new file mode 100644 index 0000000..79c8b7c --- /dev/null +++ b/licenses/third-party/r-efi-6.0.0/AUTHORS @@ -0,0 +1,75 @@ +LICENSE: + This project is triple-licensed under the MIT License, the Apache + License, Version 2.0, and the GNU Lesser General Public License, + Version 2.1+. + +AUTHORS-MIT: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +AUTHORS-ASL: + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +AUTHORS-LGPL: + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; If not, see . + +COPYRIGHT: (ordered alphabetically) + Copyright (C) 2017-2023 Red Hat, Inc. + Copyright (C) 2019-2023 Microsoft Corporation + Copyright (C) 2022-2023 David Rheinsberg + +AUTHORS: (ordered alphabetically) + Alan Egerton + Alex James + Ayush Singh + Boris-Chengbiao Zhou + Bret Barkelew + Christopher Zurcher + David Rheinsberg + Dmitry Mostovenko + Hiroki Tokunaga + Joe Richey + John Schock + Michael Kubacki + Oliver Smith-Denny + Richard Wiedenhöft + Rob Bradford , + Tom Gundersen + Trevor Gross diff --git a/licenses/third-party/ratatui-0.30.2/LICENSE b/licenses/third-party/ratatui-0.30.2/LICENSE new file mode 100644 index 0000000..776bf20 --- /dev/null +++ b/licenses/third-party/ratatui-0.30.2/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016-2022 Florian Dehau +Copyright (c) 2023-2025 The Ratatui Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/ratatui-core-0.1.2/LICENSE b/licenses/third-party/ratatui-core-0.1.2/LICENSE new file mode 100644 index 0000000..776bf20 --- /dev/null +++ b/licenses/third-party/ratatui-core-0.1.2/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016-2022 Florian Dehau +Copyright (c) 2023-2025 The Ratatui Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/ratatui-crossterm-0.1.2/LICENSE b/licenses/third-party/ratatui-crossterm-0.1.2/LICENSE new file mode 100644 index 0000000..776bf20 --- /dev/null +++ b/licenses/third-party/ratatui-crossterm-0.1.2/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016-2022 Florian Dehau +Copyright (c) 2023-2025 The Ratatui Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/ratatui-widgets-0.3.2/LICENSE b/licenses/third-party/ratatui-widgets-0.3.2/LICENSE new file mode 100644 index 0000000..776bf20 --- /dev/null +++ b/licenses/third-party/ratatui-widgets-0.3.2/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016-2022 Florian Dehau +Copyright (c) 2023-2025 The Ratatui Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/redox_syscall-0.5.18/LICENSE b/licenses/third-party/redox_syscall-0.5.18/LICENSE new file mode 100644 index 0000000..1292bb7 --- /dev/null +++ b/licenses/third-party/redox_syscall-0.5.18/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2017 Redox OS Developers + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/regex-1.12.4/LICENSE-APACHE b/licenses/third-party/regex-1.12.4/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/regex-1.12.4/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/regex-1.12.4/LICENSE-MIT b/licenses/third-party/regex-1.12.4/LICENSE-MIT new file mode 100644 index 0000000..39d4bdb --- /dev/null +++ b/licenses/third-party/regex-1.12.4/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/regex-automata-0.4.18/LICENSE-APACHE b/licenses/third-party/regex-automata-0.4.18/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/regex-automata-0.4.18/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/regex-automata-0.4.18/LICENSE-MIT b/licenses/third-party/regex-automata-0.4.18/LICENSE-MIT new file mode 100644 index 0000000..39d4bdb --- /dev/null +++ b/licenses/third-party/regex-automata-0.4.18/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/regex-syntax-0.8.11/LICENSE-APACHE b/licenses/third-party/regex-syntax-0.8.11/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/regex-syntax-0.8.11/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/regex-syntax-0.8.11/LICENSE-MIT b/licenses/third-party/regex-syntax-0.8.11/LICENSE-MIT new file mode 100644 index 0000000..39d4bdb --- /dev/null +++ b/licenses/third-party/regex-syntax-0.8.11/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/rusqlite-0.40.2/LICENSE b/licenses/third-party/rusqlite-0.40.2/LICENSE new file mode 100644 index 0000000..08c660f --- /dev/null +++ b/licenses/third-party/rusqlite-0.40.2/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 The rusqlite developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/third-party/rustc_version-0.4.1/LICENSE-APACHE b/licenses/third-party/rustc_version-0.4.1/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/rustc_version-0.4.1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/rustc_version-0.4.1/LICENSE-MIT b/licenses/third-party/rustc_version-0.4.1/LICENSE-MIT new file mode 100644 index 0000000..40b8817 --- /dev/null +++ b/licenses/third-party/rustc_version-0.4.1/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/rustix-1.1.4/COPYRIGHT b/licenses/third-party/rustix-1.1.4/COPYRIGHT new file mode 100644 index 0000000..a841525 --- /dev/null +++ b/licenses/third-party/rustix-1.1.4/COPYRIGHT @@ -0,0 +1,29 @@ +Short version for non-lawyers: + +`rustix` is triple-licensed under Apache 2.0 with the LLVM Exception, +Apache 2.0, and MIT terms. + + +Longer version: + +Copyrights in the `rustix` project are retained by their contributors. +No copyright assignment is required to contribute to the `rustix` +project. + +Some files include code derived from Rust's `libstd`; see the comments in +the code for details. + +Except as otherwise noted (below and/or in individual files), `rustix` +is licensed under: + + - the Apache License, Version 2.0, with the LLVM Exception + or + + - the Apache License, Version 2.0 + or + , + - or the MIT license + or + , + +at your option. diff --git a/licenses/third-party/rustix-1.1.4/LICENSE-APACHE b/licenses/third-party/rustix-1.1.4/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/rustix-1.1.4/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/rustix-1.1.4/LICENSE-APACHE-2.0_WITH_LLVM-exception b/licenses/third-party/rustix-1.1.4/LICENSE-APACHE-2.0_WITH_LLVM-exception new file mode 100644 index 0000000..f9d8195 --- /dev/null +++ b/licenses/third-party/rustix-1.1.4/LICENSE-APACHE-2.0_WITH_LLVM-exception @@ -0,0 +1,220 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + diff --git a/licenses/third-party/rustix-1.1.4/LICENSE-MIT b/licenses/third-party/rustix-1.1.4/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/rustix-1.1.4/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/rustix-openpty-0.2.0/COPYRIGHT b/licenses/third-party/rustix-openpty-0.2.0/COPYRIGHT new file mode 100644 index 0000000..221d370 --- /dev/null +++ b/licenses/third-party/rustix-openpty-0.2.0/COPYRIGHT @@ -0,0 +1,29 @@ +Short version for non-lawyers: + +`rustix-openpty` is triple-licensed under Apache 2.0 with the LLVM Exception, +Apache 2.0, and MIT terms. + + +Longer version: + +Copyrights in the `rustix-openpty` project are retained by their contributors. +No copyright assignment is required to contribute to the `rustix-openpty` +project. + +Some files include code derived from Rust's `libstd`; see the comments in +the code for details. + +Except as otherwise noted (below and/or in individual files), `rustix-openpty` +is licensed under: + + - the Apache License, Version 2.0, with the LLVM Exception + or + + - the Apache License, Version 2.0 + or + , + - or the MIT license + or + , + +at your option. diff --git a/licenses/third-party/rustix-openpty-0.2.0/LICENSE-APACHE b/licenses/third-party/rustix-openpty-0.2.0/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/rustix-openpty-0.2.0/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/rustix-openpty-0.2.0/LICENSE-APACHE-2.0_WITH_LLVM-exception b/licenses/third-party/rustix-openpty-0.2.0/LICENSE-APACHE-2.0_WITH_LLVM-exception new file mode 100644 index 0000000..f9d8195 --- /dev/null +++ b/licenses/third-party/rustix-openpty-0.2.0/LICENSE-APACHE-2.0_WITH_LLVM-exception @@ -0,0 +1,220 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + diff --git a/licenses/third-party/rustix-openpty-0.2.0/LICENSE-MIT b/licenses/third-party/rustix-openpty-0.2.0/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/rustix-openpty-0.2.0/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/rustversion-1.0.23/LICENSE-APACHE b/licenses/third-party/rustversion-1.0.23/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/rustversion-1.0.23/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/rustversion-1.0.23/LICENSE-MIT b/licenses/third-party/rustversion-1.0.23/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/rustversion-1.0.23/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/ryu-1.0.23/LICENSE-APACHE b/licenses/third-party/ryu-1.0.23/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/ryu-1.0.23/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/ryu-1.0.23/LICENSE-BOOST b/licenses/third-party/ryu-1.0.23/LICENSE-BOOST new file mode 100644 index 0000000..36b7cd9 --- /dev/null +++ b/licenses/third-party/ryu-1.0.23/LICENSE-BOOST @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/scopeguard-1.2.0/LICENSE-APACHE b/licenses/third-party/scopeguard-1.2.0/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/scopeguard-1.2.0/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/scopeguard-1.2.0/LICENSE-MIT b/licenses/third-party/scopeguard-1.2.0/LICENSE-MIT new file mode 100644 index 0000000..8514471 --- /dev/null +++ b/licenses/third-party/scopeguard-1.2.0/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016-2019 Ulrik Sverdrup "bluss" and scopeguard developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/semver-1.0.28/LICENSE-APACHE b/licenses/third-party/semver-1.0.28/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/semver-1.0.28/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/semver-1.0.28/LICENSE-MIT b/licenses/third-party/semver-1.0.28/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/semver-1.0.28/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/serde-1.0.229/LICENSE-APACHE b/licenses/third-party/serde-1.0.229/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/serde-1.0.229/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/serde-1.0.229/LICENSE-MIT b/licenses/third-party/serde-1.0.229/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/serde-1.0.229/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/serde_core-1.0.229/LICENSE-APACHE b/licenses/third-party/serde_core-1.0.229/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/serde_core-1.0.229/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/serde_core-1.0.229/LICENSE-MIT b/licenses/third-party/serde_core-1.0.229/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/serde_core-1.0.229/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/serde_derive-1.0.229/LICENSE-APACHE b/licenses/third-party/serde_derive-1.0.229/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/serde_derive-1.0.229/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/serde_derive-1.0.229/LICENSE-MIT b/licenses/third-party/serde_derive-1.0.229/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/serde_derive-1.0.229/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/serde_json-1.0.151/LICENSE-APACHE b/licenses/third-party/serde_json-1.0.151/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/serde_json-1.0.151/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/serde_json-1.0.151/LICENSE-MIT b/licenses/third-party/serde_json-1.0.151/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/serde_json-1.0.151/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/serde_spanned-0.6.9/LICENSE-APACHE b/licenses/third-party/serde_spanned-0.6.9/LICENSE-APACHE new file mode 100644 index 0000000..8f71f43 --- /dev/null +++ b/licenses/third-party/serde_spanned-0.6.9/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/licenses/third-party/serde_spanned-0.6.9/LICENSE-MIT b/licenses/third-party/serde_spanned-0.6.9/LICENSE-MIT new file mode 100644 index 0000000..a2d0108 --- /dev/null +++ b/licenses/third-party/serde_spanned-0.6.9/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) Individual contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/serde_stacker-0.1.14/LICENSE-APACHE b/licenses/third-party/serde_stacker-0.1.14/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/serde_stacker-0.1.14/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/serde_stacker-0.1.14/LICENSE-MIT b/licenses/third-party/serde_stacker-0.1.14/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/serde_stacker-0.1.14/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/serial2-0.2.38/LICENSE-APACHE b/licenses/third-party/serial2-0.2.38/LICENSE-APACHE new file mode 100644 index 0000000..9d1e75b --- /dev/null +++ b/licenses/third-party/serial2-0.2.38/LICENSE-APACHE @@ -0,0 +1,13 @@ +Copyright 2021, Maarten de Vries + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/sha2-0.11.0/LICENSE-APACHE b/licenses/third-party/sha2-0.11.0/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/licenses/third-party/sha2-0.11.0/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/sha2-0.11.0/LICENSE-MIT b/licenses/third-party/sha2-0.11.0/LICENSE-MIT new file mode 100644 index 0000000..9ce242c --- /dev/null +++ b/licenses/third-party/sha2-0.11.0/LICENSE-MIT @@ -0,0 +1,28 @@ +Copyright (c) 2016-2026 The RustCrypto Project Developers +Copyright (c) 2016 Artyom Pavlov +Copyright (c) 2009-2013 Mozilla Foundation +Copyright (c) 2006-2009 Graydon Hoare + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/shared_library-0.1.9/LICENSE-APACHE b/licenses/third-party/shared_library-0.1.9/LICENSE-APACHE new file mode 100644 index 0000000..1b22bef --- /dev/null +++ b/licenses/third-party/shared_library-0.1.9/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/licenses/third-party/shared_library-0.1.9/LICENSE-MIT b/licenses/third-party/shared_library-0.1.9/LICENSE-MIT new file mode 100644 index 0000000..4f2b149 --- /dev/null +++ b/licenses/third-party/shared_library-0.1.9/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2017 Pierre Krieger + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/licenses/third-party/shell-words-1.1.1/LICENSE-APACHE b/licenses/third-party/shell-words-1.1.1/LICENSE-APACHE new file mode 100644 index 0000000..356ea6f --- /dev/null +++ b/licenses/third-party/shell-words-1.1.1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/shell-words-1.1.1/LICENSE-MIT b/licenses/third-party/shell-words-1.1.1/LICENSE-MIT new file mode 100644 index 0000000..eac305d --- /dev/null +++ b/licenses/third-party/shell-words-1.1.1/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016 Tomasz Miąsko + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/shlex-2.0.1/LICENSE-APACHE b/licenses/third-party/shlex-2.0.1/LICENSE-APACHE new file mode 100644 index 0000000..3746504 --- /dev/null +++ b/licenses/third-party/shlex-2.0.1/LICENSE-APACHE @@ -0,0 +1,13 @@ +Copyright 2015 Nicholas Allegra (comex). + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/shlex-2.0.1/LICENSE-MIT b/licenses/third-party/shlex-2.0.1/LICENSE-MIT new file mode 100644 index 0000000..5ec1fe1 --- /dev/null +++ b/licenses/third-party/shlex-2.0.1/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Nicholas Allegra (comex). + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/third-party/signal-hook-0.3.18/LICENSE-APACHE b/licenses/third-party/signal-hook-0.3.18/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/signal-hook-0.3.18/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/signal-hook-0.3.18/LICENSE-MIT b/licenses/third-party/signal-hook-0.3.18/LICENSE-MIT new file mode 100644 index 0000000..ebe0bc9 --- /dev/null +++ b/licenses/third-party/signal-hook-0.3.18/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2017 tokio-jsonrpc developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/signal-hook-0.4.4/LICENSE-APACHE b/licenses/third-party/signal-hook-0.4.4/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/signal-hook-0.4.4/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/signal-hook-0.4.4/LICENSE-MIT b/licenses/third-party/signal-hook-0.4.4/LICENSE-MIT new file mode 100644 index 0000000..ebe0bc9 --- /dev/null +++ b/licenses/third-party/signal-hook-0.4.4/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2017 tokio-jsonrpc developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/signal-hook-mio-0.2.5/LICENSE-APACHE b/licenses/third-party/signal-hook-mio-0.2.5/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/signal-hook-mio-0.2.5/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/signal-hook-mio-0.2.5/LICENSE-MIT b/licenses/third-party/signal-hook-mio-0.2.5/LICENSE-MIT new file mode 100644 index 0000000..ebe0bc9 --- /dev/null +++ b/licenses/third-party/signal-hook-mio-0.2.5/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2017 tokio-jsonrpc developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/signal-hook-registry-1.4.8/LICENSE-APACHE b/licenses/third-party/signal-hook-registry-1.4.8/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/signal-hook-registry-1.4.8/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/signal-hook-registry-1.4.8/LICENSE-MIT b/licenses/third-party/signal-hook-registry-1.4.8/LICENSE-MIT new file mode 100644 index 0000000..ebe0bc9 --- /dev/null +++ b/licenses/third-party/signal-hook-registry-1.4.8/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2017 tokio-jsonrpc developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/smallvec-1.16.1/LICENSE-APACHE b/licenses/third-party/smallvec-1.16.1/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/smallvec-1.16.1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/smallvec-1.16.1/LICENSE-MIT b/licenses/third-party/smallvec-1.16.1/LICENSE-MIT new file mode 100644 index 0000000..9729c12 --- /dev/null +++ b/licenses/third-party/smallvec-1.16.1/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2018 The Servo Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/stacker-0.1.25/LICENSE-APACHE b/licenses/third-party/stacker-0.1.25/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/stacker-0.1.25/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/stacker-0.1.25/LICENSE-MIT b/licenses/third-party/stacker-0.1.25/LICENSE-MIT new file mode 100644 index 0000000..39e0ed6 --- /dev/null +++ b/licenses/third-party/stacker-0.1.25/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/static_assertions-1.1.0/LICENSE-APACHE b/licenses/third-party/static_assertions-1.1.0/LICENSE-APACHE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/licenses/third-party/static_assertions-1.1.0/LICENSE-APACHE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/static_assertions-1.1.0/LICENSE-MIT b/licenses/third-party/static_assertions-1.1.0/LICENSE-MIT new file mode 100644 index 0000000..ccc9445 --- /dev/null +++ b/licenses/third-party/static_assertions-1.1.0/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Nikolai Vazquez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/strsim-0.11.1/LICENSE b/licenses/third-party/strsim-0.11.1/LICENSE new file mode 100644 index 0000000..8d1fbe1 --- /dev/null +++ b/licenses/third-party/strsim-0.11.1/LICENSE @@ -0,0 +1,23 @@ +The MIT License (MIT) + +Copyright (c) 2015 Danny Guo +Copyright (c) 2016 Titus Wormer +Copyright (c) 2018 Akash Kurdekar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/strum-0.28.0/LICENSE b/licenses/third-party/strum-0.28.0/LICENSE new file mode 100644 index 0000000..588b4a7 --- /dev/null +++ b/licenses/third-party/strum-0.28.0/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Peter Glotfelty + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/strum_macros-0.28.0/LICENSE b/licenses/third-party/strum_macros-0.28.0/LICENSE new file mode 100644 index 0000000..588b4a7 --- /dev/null +++ b/licenses/third-party/strum_macros-0.28.0/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Peter Glotfelty + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/syn-2.0.119/LICENSE-APACHE b/licenses/third-party/syn-2.0.119/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/syn-2.0.119/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/syn-2.0.119/LICENSE-MIT b/licenses/third-party/syn-2.0.119/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/syn-2.0.119/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/syn-3.0.6/LICENSE-APACHE b/licenses/third-party/syn-3.0.6/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/syn-3.0.6/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/syn-3.0.6/LICENSE-MIT b/licenses/third-party/syn-3.0.6/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/syn-3.0.6/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/thiserror-1.0.69/LICENSE-APACHE b/licenses/third-party/thiserror-1.0.69/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/thiserror-1.0.69/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/thiserror-1.0.69/LICENSE-MIT b/licenses/third-party/thiserror-1.0.69/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/thiserror-1.0.69/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/thiserror-2.0.20/LICENSE-APACHE b/licenses/third-party/thiserror-2.0.20/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/thiserror-2.0.20/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/thiserror-2.0.20/LICENSE-MIT b/licenses/third-party/thiserror-2.0.20/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/thiserror-2.0.20/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/thiserror-impl-1.0.69/LICENSE-APACHE b/licenses/third-party/thiserror-impl-1.0.69/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/thiserror-impl-1.0.69/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/thiserror-impl-1.0.69/LICENSE-MIT b/licenses/third-party/thiserror-impl-1.0.69/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/thiserror-impl-1.0.69/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/thiserror-impl-2.0.20/LICENSE-APACHE b/licenses/third-party/thiserror-impl-2.0.20/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/thiserror-impl-2.0.20/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/thiserror-impl-2.0.20/LICENSE-MIT b/licenses/third-party/thiserror-impl-2.0.20/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/thiserror-impl-2.0.20/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/time-0.3.55/LICENSE-APACHE b/licenses/third-party/time-0.3.55/LICENSE-APACHE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/licenses/third-party/time-0.3.55/LICENSE-APACHE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/time-0.3.55/LICENSE-MIT b/licenses/third-party/time-0.3.55/LICENSE-MIT new file mode 100644 index 0000000..663cb2a --- /dev/null +++ b/licenses/third-party/time-0.3.55/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) Jacob Pratt et al. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/time-core-0.1.9/LICENSE-APACHE b/licenses/third-party/time-core-0.1.9/LICENSE-APACHE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/licenses/third-party/time-core-0.1.9/LICENSE-APACHE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/time-core-0.1.9/LICENSE-MIT b/licenses/third-party/time-core-0.1.9/LICENSE-MIT new file mode 100644 index 0000000..663cb2a --- /dev/null +++ b/licenses/third-party/time-core-0.1.9/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) Jacob Pratt et al. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/time-macros-0.2.32/LICENSE-APACHE b/licenses/third-party/time-macros-0.2.32/LICENSE-APACHE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/licenses/third-party/time-macros-0.2.32/LICENSE-APACHE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/time-macros-0.2.32/LICENSE-MIT b/licenses/third-party/time-macros-0.2.32/LICENSE-MIT new file mode 100644 index 0000000..663cb2a --- /dev/null +++ b/licenses/third-party/time-macros-0.2.32/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) Jacob Pratt et al. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/toml-0.8.23/LICENSE-APACHE b/licenses/third-party/toml-0.8.23/LICENSE-APACHE new file mode 100644 index 0000000..8f71f43 --- /dev/null +++ b/licenses/third-party/toml-0.8.23/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/licenses/third-party/toml-0.8.23/LICENSE-MIT b/licenses/third-party/toml-0.8.23/LICENSE-MIT new file mode 100644 index 0000000..a2d0108 --- /dev/null +++ b/licenses/third-party/toml-0.8.23/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) Individual contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/toml_datetime-0.6.11/LICENSE-APACHE b/licenses/third-party/toml_datetime-0.6.11/LICENSE-APACHE new file mode 100644 index 0000000..8f71f43 --- /dev/null +++ b/licenses/third-party/toml_datetime-0.6.11/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/licenses/third-party/toml_datetime-0.6.11/LICENSE-MIT b/licenses/third-party/toml_datetime-0.6.11/LICENSE-MIT new file mode 100644 index 0000000..a2d0108 --- /dev/null +++ b/licenses/third-party/toml_datetime-0.6.11/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) Individual contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/toml_edit-0.22.27/LICENSE-APACHE b/licenses/third-party/toml_edit-0.22.27/LICENSE-APACHE new file mode 100644 index 0000000..8f71f43 --- /dev/null +++ b/licenses/third-party/toml_edit-0.22.27/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/licenses/third-party/toml_edit-0.22.27/LICENSE-MIT b/licenses/third-party/toml_edit-0.22.27/LICENSE-MIT new file mode 100644 index 0000000..a2d0108 --- /dev/null +++ b/licenses/third-party/toml_edit-0.22.27/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) Individual contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/typenum-1.20.1/LICENSE b/licenses/third-party/typenum-1.20.1/LICENSE new file mode 100644 index 0000000..364b773 --- /dev/null +++ b/licenses/third-party/typenum-1.20.1/LICENSE @@ -0,0 +1 @@ +MIT OR Apache-2.0 \ No newline at end of file diff --git a/licenses/third-party/typenum-1.20.1/LICENSE-APACHE b/licenses/third-party/typenum-1.20.1/LICENSE-APACHE new file mode 100644 index 0000000..4bf355c --- /dev/null +++ b/licenses/third-party/typenum-1.20.1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2014 Paho Lurie-Gregg + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/licenses/third-party/typenum-1.20.1/LICENSE-MIT b/licenses/third-party/typenum-1.20.1/LICENSE-MIT new file mode 100644 index 0000000..e567a4d --- /dev/null +++ b/licenses/third-party/typenum-1.20.1/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Paho Lurie-Gregg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/unicode-ident-1.0.26/LICENSE-APACHE b/licenses/third-party/unicode-ident-1.0.26/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/unicode-ident-1.0.26/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/unicode-ident-1.0.26/LICENSE-MIT b/licenses/third-party/unicode-ident-1.0.26/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/unicode-ident-1.0.26/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/unicode-ident-1.0.26/LICENSE-UNICODE b/licenses/third-party/unicode-ident-1.0.26/LICENSE-UNICODE new file mode 100644 index 0000000..11f2842 --- /dev/null +++ b/licenses/third-party/unicode-ident-1.0.26/LICENSE-UNICODE @@ -0,0 +1,39 @@ +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 1991-2023 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. diff --git a/licenses/third-party/unicode-segmentation-1.13.3/COPYRIGHT b/licenses/third-party/unicode-segmentation-1.13.3/COPYRIGHT new file mode 100644 index 0000000..b286ec1 --- /dev/null +++ b/licenses/third-party/unicode-segmentation-1.13.3/COPYRIGHT @@ -0,0 +1,7 @@ +Licensed under the Apache License, Version 2.0 + or the MIT +license , +at your option. All files in the project carrying such +notice may not be copied, modified, or distributed except +according to those terms. diff --git a/licenses/third-party/unicode-segmentation-1.13.3/LICENSE-APACHE b/licenses/third-party/unicode-segmentation-1.13.3/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/unicode-segmentation-1.13.3/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/unicode-segmentation-1.13.3/LICENSE-MIT b/licenses/third-party/unicode-segmentation-1.13.3/LICENSE-MIT new file mode 100644 index 0000000..e69282e --- /dev/null +++ b/licenses/third-party/unicode-segmentation-1.13.3/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2015 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/unicode-truncate-2.0.1/COPYRIGHT b/licenses/third-party/unicode-truncate-2.0.1/COPYRIGHT new file mode 100644 index 0000000..b286ec1 --- /dev/null +++ b/licenses/third-party/unicode-truncate-2.0.1/COPYRIGHT @@ -0,0 +1,7 @@ +Licensed under the Apache License, Version 2.0 + or the MIT +license , +at your option. All files in the project carrying such +notice may not be copied, modified, or distributed except +according to those terms. diff --git a/licenses/third-party/unicode-truncate-2.0.1/LICENSE-APACHE b/licenses/third-party/unicode-truncate-2.0.1/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/unicode-truncate-2.0.1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/unicode-truncate-2.0.1/LICENSE-MIT b/licenses/third-party/unicode-truncate-2.0.1/LICENSE-MIT new file mode 100644 index 0000000..39ae1bc --- /dev/null +++ b/licenses/third-party/unicode-truncate-2.0.1/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2019 Aetf + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/unicode-width-0.2.2/COPYRIGHT b/licenses/third-party/unicode-width-0.2.2/COPYRIGHT new file mode 100644 index 0000000..b286ec1 --- /dev/null +++ b/licenses/third-party/unicode-width-0.2.2/COPYRIGHT @@ -0,0 +1,7 @@ +Licensed under the Apache License, Version 2.0 + or the MIT +license , +at your option. All files in the project carrying such +notice may not be copied, modified, or distributed except +according to those terms. diff --git a/licenses/third-party/unicode-width-0.2.2/LICENSE-APACHE b/licenses/third-party/unicode-width-0.2.2/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/unicode-width-0.2.2/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/unicode-width-0.2.2/LICENSE-MIT b/licenses/third-party/unicode-width-0.2.2/LICENSE-MIT new file mode 100644 index 0000000..e69282e --- /dev/null +++ b/licenses/third-party/unicode-width-0.2.2/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2015 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/vcpkg-0.2.15/LICENSE-APACHE b/licenses/third-party/vcpkg-0.2.15/LICENSE-APACHE new file mode 100644 index 0000000..7e81c31 --- /dev/null +++ b/licenses/third-party/vcpkg-0.2.15/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/licenses/third-party/vcpkg-0.2.15/LICENSE-MIT b/licenses/third-party/vcpkg-0.2.15/LICENSE-MIT new file mode 100644 index 0000000..180fc3b --- /dev/null +++ b/licenses/third-party/vcpkg-0.2.15/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (c) 2017 Jim McGrath + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + diff --git a/licenses/third-party/vte-0.15.0/LICENSE-APACHE b/licenses/third-party/vte-0.15.0/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/licenses/third-party/vte-0.15.0/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/licenses/third-party/vte-0.15.0/LICENSE-MIT b/licenses/third-party/vte-0.15.0/LICENSE-MIT new file mode 100644 index 0000000..bb419c2 --- /dev/null +++ b/licenses/third-party/vte-0.15.0/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016 Joe Wilm + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-APACHE b/licenses/third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/licenses/third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/licenses/third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-APACHE-2.0_WITH_LLVM-exception b/licenses/third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-APACHE-2.0_WITH_LLVM-exception new file mode 100644 index 0000000..f9d8195 --- /dev/null +++ b/licenses/third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-APACHE-2.0_WITH_LLVM-exception @@ -0,0 +1,220 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + diff --git a/licenses/third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-MIT b/licenses/third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/winapi-0.3.9/LICENSE-APACHE b/licenses/third-party/winapi-0.3.9/LICENSE-APACHE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/licenses/third-party/winapi-0.3.9/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/winapi-0.3.9/LICENSE-MIT b/licenses/third-party/winapi-0.3.9/LICENSE-MIT new file mode 100644 index 0000000..6f1b4c8 --- /dev/null +++ b/licenses/third-party/winapi-0.3.9/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) 2015-2018 The winapi-rs Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/winapi-i686-pc-windows-gnu-0.4.0/LICENSE-APACHE b/licenses/third-party/winapi-i686-pc-windows-gnu-0.4.0/LICENSE-APACHE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/licenses/third-party/winapi-i686-pc-windows-gnu-0.4.0/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/winapi-i686-pc-windows-gnu-0.4.0/LICENSE-MIT b/licenses/third-party/winapi-i686-pc-windows-gnu-0.4.0/LICENSE-MIT new file mode 100644 index 0000000..6f1b4c8 --- /dev/null +++ b/licenses/third-party/winapi-i686-pc-windows-gnu-0.4.0/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) 2015-2018 The winapi-rs Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/winapi-x86_64-pc-windows-gnu-0.4.0/LICENSE-APACHE b/licenses/third-party/winapi-x86_64-pc-windows-gnu-0.4.0/LICENSE-APACHE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/licenses/third-party/winapi-x86_64-pc-windows-gnu-0.4.0/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/winapi-x86_64-pc-windows-gnu-0.4.0/LICENSE-MIT b/licenses/third-party/winapi-x86_64-pc-windows-gnu-0.4.0/LICENSE-MIT new file mode 100644 index 0000000..6f1b4c8 --- /dev/null +++ b/licenses/third-party/winapi-x86_64-pc-windows-gnu-0.4.0/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) 2015-2018 The winapi-rs Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/third-party/windows-link-0.2.1/LICENSE-APACHE-2.0 b/licenses/third-party/windows-link-0.2.1/LICENSE-APACHE-2.0 new file mode 100644 index 0000000..b5ed4ec --- /dev/null +++ b/licenses/third-party/windows-link-0.2.1/LICENSE-APACHE-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/windows-link-0.2.1/LICENSE-MIT b/licenses/third-party/windows-link-0.2.1/LICENSE-MIT new file mode 100644 index 0000000..9e841e7 --- /dev/null +++ b/licenses/third-party/windows-link-0.2.1/LICENSE-MIT @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/licenses/third-party/windows-sys-0.59.0/LICENSE-APACHE-2.0 b/licenses/third-party/windows-sys-0.59.0/LICENSE-APACHE-2.0 new file mode 100644 index 0000000..b5ed4ec --- /dev/null +++ b/licenses/third-party/windows-sys-0.59.0/LICENSE-APACHE-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/windows-sys-0.59.0/LICENSE-MIT b/licenses/third-party/windows-sys-0.59.0/LICENSE-MIT new file mode 100644 index 0000000..9e841e7 --- /dev/null +++ b/licenses/third-party/windows-sys-0.59.0/LICENSE-MIT @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/licenses/third-party/windows-sys-0.61.2/LICENSE-APACHE-2.0 b/licenses/third-party/windows-sys-0.61.2/LICENSE-APACHE-2.0 new file mode 100644 index 0000000..b5ed4ec --- /dev/null +++ b/licenses/third-party/windows-sys-0.61.2/LICENSE-APACHE-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/windows-sys-0.61.2/LICENSE-MIT b/licenses/third-party/windows-sys-0.61.2/LICENSE-MIT new file mode 100644 index 0000000..9e841e7 --- /dev/null +++ b/licenses/third-party/windows-sys-0.61.2/LICENSE-MIT @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/licenses/third-party/windows-targets-0.52.6/LICENSE-APACHE-2.0 b/licenses/third-party/windows-targets-0.52.6/LICENSE-APACHE-2.0 new file mode 100644 index 0000000..b5ed4ec --- /dev/null +++ b/licenses/third-party/windows-targets-0.52.6/LICENSE-APACHE-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/windows-targets-0.52.6/LICENSE-MIT b/licenses/third-party/windows-targets-0.52.6/LICENSE-MIT new file mode 100644 index 0000000..9e841e7 --- /dev/null +++ b/licenses/third-party/windows-targets-0.52.6/LICENSE-MIT @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/licenses/third-party/windows_aarch64_gnullvm-0.52.6/LICENSE-APACHE-2.0 b/licenses/third-party/windows_aarch64_gnullvm-0.52.6/LICENSE-APACHE-2.0 new file mode 100644 index 0000000..b5ed4ec --- /dev/null +++ b/licenses/third-party/windows_aarch64_gnullvm-0.52.6/LICENSE-APACHE-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/windows_aarch64_gnullvm-0.52.6/LICENSE-MIT b/licenses/third-party/windows_aarch64_gnullvm-0.52.6/LICENSE-MIT new file mode 100644 index 0000000..9e841e7 --- /dev/null +++ b/licenses/third-party/windows_aarch64_gnullvm-0.52.6/LICENSE-MIT @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/licenses/third-party/windows_aarch64_msvc-0.52.6/LICENSE-APACHE-2.0 b/licenses/third-party/windows_aarch64_msvc-0.52.6/LICENSE-APACHE-2.0 new file mode 100644 index 0000000..b5ed4ec --- /dev/null +++ b/licenses/third-party/windows_aarch64_msvc-0.52.6/LICENSE-APACHE-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/windows_aarch64_msvc-0.52.6/LICENSE-MIT b/licenses/third-party/windows_aarch64_msvc-0.52.6/LICENSE-MIT new file mode 100644 index 0000000..9e841e7 --- /dev/null +++ b/licenses/third-party/windows_aarch64_msvc-0.52.6/LICENSE-MIT @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/licenses/third-party/windows_i686_gnu-0.52.6/LICENSE-APACHE-2.0 b/licenses/third-party/windows_i686_gnu-0.52.6/LICENSE-APACHE-2.0 new file mode 100644 index 0000000..b5ed4ec --- /dev/null +++ b/licenses/third-party/windows_i686_gnu-0.52.6/LICENSE-APACHE-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/windows_i686_gnu-0.52.6/LICENSE-MIT b/licenses/third-party/windows_i686_gnu-0.52.6/LICENSE-MIT new file mode 100644 index 0000000..9e841e7 --- /dev/null +++ b/licenses/third-party/windows_i686_gnu-0.52.6/LICENSE-MIT @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/licenses/third-party/windows_i686_gnullvm-0.52.6/LICENSE-APACHE-2.0 b/licenses/third-party/windows_i686_gnullvm-0.52.6/LICENSE-APACHE-2.0 new file mode 100644 index 0000000..b5ed4ec --- /dev/null +++ b/licenses/third-party/windows_i686_gnullvm-0.52.6/LICENSE-APACHE-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/windows_i686_gnullvm-0.52.6/LICENSE-MIT b/licenses/third-party/windows_i686_gnullvm-0.52.6/LICENSE-MIT new file mode 100644 index 0000000..9e841e7 --- /dev/null +++ b/licenses/third-party/windows_i686_gnullvm-0.52.6/LICENSE-MIT @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/licenses/third-party/windows_i686_msvc-0.52.6/LICENSE-APACHE-2.0 b/licenses/third-party/windows_i686_msvc-0.52.6/LICENSE-APACHE-2.0 new file mode 100644 index 0000000..b5ed4ec --- /dev/null +++ b/licenses/third-party/windows_i686_msvc-0.52.6/LICENSE-APACHE-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/windows_i686_msvc-0.52.6/LICENSE-MIT b/licenses/third-party/windows_i686_msvc-0.52.6/LICENSE-MIT new file mode 100644 index 0000000..9e841e7 --- /dev/null +++ b/licenses/third-party/windows_i686_msvc-0.52.6/LICENSE-MIT @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/licenses/third-party/windows_x86_64_gnu-0.52.6/LICENSE-APACHE-2.0 b/licenses/third-party/windows_x86_64_gnu-0.52.6/LICENSE-APACHE-2.0 new file mode 100644 index 0000000..b5ed4ec --- /dev/null +++ b/licenses/third-party/windows_x86_64_gnu-0.52.6/LICENSE-APACHE-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/windows_x86_64_gnu-0.52.6/LICENSE-MIT b/licenses/third-party/windows_x86_64_gnu-0.52.6/LICENSE-MIT new file mode 100644 index 0000000..9e841e7 --- /dev/null +++ b/licenses/third-party/windows_x86_64_gnu-0.52.6/LICENSE-MIT @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/licenses/third-party/windows_x86_64_gnullvm-0.52.6/LICENSE-APACHE-2.0 b/licenses/third-party/windows_x86_64_gnullvm-0.52.6/LICENSE-APACHE-2.0 new file mode 100644 index 0000000..b5ed4ec --- /dev/null +++ b/licenses/third-party/windows_x86_64_gnullvm-0.52.6/LICENSE-APACHE-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/windows_x86_64_gnullvm-0.52.6/LICENSE-MIT b/licenses/third-party/windows_x86_64_gnullvm-0.52.6/LICENSE-MIT new file mode 100644 index 0000000..9e841e7 --- /dev/null +++ b/licenses/third-party/windows_x86_64_gnullvm-0.52.6/LICENSE-MIT @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/licenses/third-party/windows_x86_64_msvc-0.52.6/LICENSE-APACHE-2.0 b/licenses/third-party/windows_x86_64_msvc-0.52.6/LICENSE-APACHE-2.0 new file mode 100644 index 0000000..b5ed4ec --- /dev/null +++ b/licenses/third-party/windows_x86_64_msvc-0.52.6/LICENSE-APACHE-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/third-party/windows_x86_64_msvc-0.52.6/LICENSE-MIT b/licenses/third-party/windows_x86_64_msvc-0.52.6/LICENSE-MIT new file mode 100644 index 0000000..9e841e7 --- /dev/null +++ b/licenses/third-party/windows_x86_64_msvc-0.52.6/LICENSE-MIT @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/licenses/third-party/winnow-0.7.15/LICENSE-MIT b/licenses/third-party/winnow-0.7.15/LICENSE-MIT new file mode 100644 index 0000000..c9b44cb --- /dev/null +++ b/licenses/third-party/winnow-0.7.15/LICENSE-MIT @@ -0,0 +1,18 @@ +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/third-party/winreg-0.10.1/LICENSE b/licenses/third-party/winreg-0.10.1/LICENSE new file mode 100644 index 0000000..b9d358e --- /dev/null +++ b/licenses/third-party/winreg-0.10.1/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015 Igor Shaula + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/third-party/zmij-1.0.23/LICENSE-MIT b/licenses/third-party/zmij-1.0.23/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/licenses/third-party/zmij-1.0.23/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/licenses/upstream/herdr/LICENSE b/licenses/upstream/herdr/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/licenses/upstream/herdr/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/scripts/rust_dependency_audit.py b/scripts/rust_dependency_audit.py new file mode 100644 index 0000000..a17ccee --- /dev/null +++ b/scripts/rust_dependency_audit.py @@ -0,0 +1,634 @@ +#!/usr/bin/env python3 +"""Check the locked Rust dependency licenses/notices and emit CycloneDX SBOM.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import pathlib +import re +import subprocess +import sys +import tomllib +from collections import defaultdict +from urllib.parse import quote + +ROOT = pathlib.Path(__file__).resolve().parents[1] +NOTICE = ROOT / "licenses" / "THIRD_PARTY_NOTICES.md" +LOCKFILE = ROOT / "Cargo.lock" +LICENSE_FILENAMES = ( + "LICENSE", + "LICENSE-MIT", + "LICENSE-APACHE", + "LICENSE-UNICODE", + "LICENSE-CC0", + "LICENSE-BOOST", + "LICENSE-Apache-2.0_WITH_LLVM-exception", + "LICENSE.txt", + "LICENSE.md", + "license-apache-2.0", + "license-mit", + "COPYING", + "COPYING-MIT", + "COPYING-APACHE", + "COPYRIGHT", + "AUTHORS", + "UNLICENSE", +) +LICENSE_FILENAMES_CASEFOLD = {filename.casefold() for filename in LICENSE_FILENAMES} +ALLOWED_LICENSE_EXPRESSIONS = { + "MIT", + "Apache-2.0", + "MIT OR Apache-2.0", + "Apache-2.0 OR MIT", + "MIT OR Apache-2.0 OR LGPL-2.1-or-later", + "(MIT OR Apache-2.0) AND Unicode-3.0", + "MIT/Apache-2.0", + "Apache-2.0/MIT", + "Unlicense OR MIT", + "Apache-2.0 WITH LLVM-exception", + "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "BSD-2-Clause OR Apache-2.0", + "MIT OR Apache-2.0 OR Zlib", + "MIT OR Apache-2.0 OR CC0-1.0", + "Zlib", + "Apache-2.0 OR BSL-1.0", +} +LICENSE_EXPRESSION_NORMALIZATION = { + "MIT/Apache-2.0": "MIT OR Apache-2.0", + "Apache-2.0/MIT": "Apache-2.0 OR MIT", +} +SUPPORTED_TARGETS = ( + "x86_64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-pc-windows-msvc", +) +LOCK_REGISTRY = "registry+https://github.com/rust-lang/crates.io-index" +NOTICE_ROW = re.compile(r"^\| `([^`]+)` \| ([^|]+) \| ([^|]+) \| (.+) \|$", re.M) +NOTICE_FILE = re.compile(r"\[`([^`]+)`\]\(([^)]+)\) `([0-9a-f]{64})`") +BUNDLED_SQLITE_ROW = re.compile( + r"^\| `SQLite ([^`]+)` \| `libsqlite3-sys ([^`]+)` \| public-domain blessing \| " + r"\[`SQLITE_BLESSING\.txt`\]\(([^)]+)\) `([0-9a-f]{64})` \|$", + re.M, +) +SQLITE_SOURCE_PATH = pathlib.Path("sqlite3") / "sqlite3.c" +TREE_LINE = re.compile(r"^(\d+)([A-Za-z0-9_.+-]+) v([^\s]+)(?:\s+.*)?$") + + +class AuditError(Exception): + """A lockfile, license, or notice integrity failure.""" + + +def recognized_license_files(source_dir: pathlib.Path) -> dict[str, str]: + """Map stable bundled names to actual source names without case aliases.""" + return { + bundle_license_filename(path.name): path.name + for path in source_dir.iterdir() + if path.is_file() and path.name.casefold() in LICENSE_FILENAMES_CASEFOLD + } + + +def license_source(package: dict, packages: dict) -> tuple[pathlib.Path, dict[str, str]]: + """Find exact license files, including omitted files shared by one upstream repository.""" + source_dir = pathlib.Path(package["manifest_path"]).parent + files = recognized_license_files(source_dir) + if files: + return source_dir, files + + repository = package.get("repository") + expression = canonical_license_expression(package.get("license")) + candidates = sorted( + ( + candidate + for candidate in packages.values() + if candidate is not package + and repository + and candidate.get("repository") == repository + and canonical_license_expression(candidate.get("license")) == expression + ), + key=lambda candidate: (candidate["name"], candidate["version"]), + ) + for candidate in candidates: + candidate_dir = pathlib.Path(candidate["manifest_path"]).parent + files = recognized_license_files(candidate_dir) + if files: + return candidate_dir, files + return source_dir, {} + + +def bundle_license_filename(source_filename: str) -> str: + """Use stable bundle casing while retaining source spelling for byte reads.""" + return { + "license-mit": "LICENSE-MIT", + "license-apache": "LICENSE-APACHE", + "license-apache-2.0": "LICENSE-APACHE-2.0", + "license-apache-2.0_with_llvm-exception": "LICENSE-APACHE-2.0_WITH_LLVM-exception", + "license-unicode": "LICENSE-UNICODE", + "license-cc0": "LICENSE-CC0", + "license-boost": "LICENSE-BOOST", + "license.txt": "LICENSE.txt", + "license.md": "LICENSE.md", + "unlicense": "UNLICENSE", + }.get(source_filename.casefold(), source_filename) + + +def package_key(package: dict) -> tuple[str, str]: + return package["name"], package["version"] + + +def package_purl(name: str, version: str) -> str: + return f"pkg:cargo/{quote(name, safe='-._~')}@{quote(version, safe='-._~')}" + + +def canonical_license_expression(expression: str) -> str: + if expression not in ALLOWED_LICENSE_EXPRESSIONS: + raise AuditError(f"unreviewed SPDX license expression: {expression}") + return LICENSE_EXPRESSION_NORMALIZATION.get(expression, expression) + + +def sqlite_bundled_notice(package: dict) -> tuple[str, bytes, bytes]: + source_dir = pathlib.Path(package["manifest_path"]).parent + sqlite_source = source_dir / SQLITE_SOURCE_PATH + source_bytes = sqlite_source.read_bytes() + version_match = re.search(rb'^#define SQLITE_VERSION\s+"([^"\r\n]+)"', source_bytes, re.M) + if not version_match: + raise AuditError(f"SQLite version not found in bundled source: {sqlite_source}") + lines = source_bytes.splitlines(keepends=True) + start = next( + (index for index, line in enumerate(lines) if b"The author disclaims copyright to this source code." in line), + None, + ) + if start is None: + raise AuditError(f"SQLite public-domain blessing not found in {sqlite_source}") + end = next( + (index for index in range(start + 1, len(lines)) if lines[index].startswith(b"********")), + None, + ) + if end is None: + raise AuditError(f"SQLite public-domain blessing terminator not found in {sqlite_source}") + notice = b"".join(lines[start:end]) + return version_match.group(1).decode("ascii"), source_bytes, notice + + +def registry_packages(metadata: dict) -> dict[tuple[str, str], dict]: + packages = {} + for package in metadata["packages"]: + source = package.get("source") + if source is None: + continue + if source != LOCK_REGISTRY: + raise AuditError(f"unreviewed Rust package source: {package['name']} {source}") + key = package_key(package) + if key in packages: + raise AuditError(f"duplicate registry package in metadata: {key}") + packages[key] = package + return packages + + +def read_notice_rows(text: str) -> dict[tuple[str, str], tuple[str, list[tuple[str, str, str]]]]: + rows = {} + package_table = text.partition("## Bundled third-party source notice")[0] + for match in NOTICE_ROW.finditer(package_table): + name, version, license_expression, file_column = match.groups() + key = (name, version.strip()) + if key in rows: + raise AuditError(f"duplicate third-party notice row: {key}") + files = NOTICE_FILE.findall(file_column) + rows[key] = (license_expression.strip(), files) + return rows + + +def load_cargo_data(root: pathlib.Path = ROOT) -> tuple[dict, dict]: + command = [ + "cargo", + "metadata", + "--format-version", + "1", + "--locked", + "--offline", + ] + try: + result = subprocess.run(command, cwd=root, text=True, capture_output=True, check=True) + except (OSError, subprocess.CalledProcessError) as error: + detail = getattr(error, "stderr", "") or str(error) + raise AuditError(f"Cargo metadata failed: {detail.strip()}") from error + return json.loads(result.stdout), tomllib.loads((root / "Cargo.lock").read_text()) + + +def check_notices(metadata: dict, lock: dict, root: pathlib.Path = ROOT) -> dict: + packages = registry_packages(metadata) + lock_registry = { + package_key(package): package + for package in lock["package"] + if package.get("source") == LOCK_REGISTRY + } + if set(packages) != set(lock_registry): + missing = sorted(set(lock_registry) - set(packages)) + extra = sorted(set(packages) - set(lock_registry)) + raise AuditError(f"Cargo.lock/metadata package mismatch; missing={missing}, extra={extra}") + + rows = read_notice_rows((root / "licenses" / "THIRD_PARTY_NOTICES.md").read_text()) + if set(rows) != set(packages): + missing = sorted(set(packages) - set(rows)) + extra = sorted(set(rows) - set(packages)) + raise AuditError(f"notice package mismatch; missing={missing}, extra={extra}") + + expected_bundle_files = set() + for key, package in packages.items(): + name, version = key + license_expression = package.get("license") + if not license_expression: + raise AuditError(f"missing SPDX license expression: {name} {version}") + try: + canonical_expression = canonical_license_expression(license_expression) + except AuditError as error: + raise AuditError( + f"unreviewed SPDX license expression for {name} {version}: {license_expression}" + ) from error + notice_expression, files = rows[key] + if notice_expression != canonical_expression: + raise AuditError(f"notice license mismatch for {name} {version}") + + source_dir, source_files = license_source(package, packages) + noticed_files = {filename for filename, _, _ in files} + if not source_files or noticed_files != set(source_files): + raise AuditError( + f"license/notice file mismatch for {name} {version}: " + f"source={sorted(source_files)}, bundle={sorted(noticed_files)}" + ) + + for filename, relative_path, recorded_hash in files: + source_filename = source_files[filename] + expected_relative = pathlib.Path("third-party") / f"{name}-{version}" / filename + if pathlib.Path(relative_path) != expected_relative: + raise AuditError(f"unexpected notice path for {name} {version}: {relative_path}") + source_bytes = (source_dir / source_filename).read_bytes() + bundled_path = root / "licenses" / expected_relative + if not bundled_path.is_file(): + raise AuditError(f"missing bundled license/notice: {bundled_path}") + bundled_bytes = bundled_path.read_bytes() + actual_hash = hashlib.sha256(source_bytes).hexdigest() + if actual_hash != recorded_hash or bundled_bytes != source_bytes: + raise AuditError(f"license/notice bytes or SHA-256 differ: {name} {version}/{filename}") + expected_bundle_files.add(bundled_path.resolve()) + + sqlite_packages = [ + package + for key, package in packages.items() + if key[0] == "libsqlite3-sys" and key[1] == "0.38.2" + ] + if sqlite_packages: + if len(sqlite_packages) != 1: + raise AuditError("unexpected duplicate bundled SQLite package") + sqlite_version, _, sqlite_notice = sqlite_bundled_notice(sqlite_packages[0]) + bundled_path = pathlib.Path("third-party") / "libsqlite3-sys-0.38.2" / "SQLITE_BLESSING.txt" + bundled_match = BUNDLED_SQLITE_ROW.search((root / "licenses" / "THIRD_PARTY_NOTICES.md").read_text()) + if not bundled_match: + raise AuditError("missing bundled SQLite public-domain notice row") + row_version, row_crate_version, relative_path, recorded_hash = bundled_match.groups() + expected_hash = hashlib.sha256(sqlite_notice).hexdigest() + output_path = root / "licenses" / bundled_path + if ( + row_version != sqlite_version + or row_crate_version != "0.38.2" + or pathlib.Path(relative_path) != bundled_path + or recorded_hash != expected_hash + or not output_path.is_file() + or output_path.read_bytes() != sqlite_notice + ): + raise AuditError("bundled SQLite public-domain notice or source version differs") + expected_bundle_files.add(output_path.resolve()) + elif BUNDLED_SQLITE_ROW.search((root / "licenses" / "THIRD_PARTY_NOTICES.md").read_text()): + raise AuditError("stale bundled SQLite notice row without locked libsqlite3-sys") + + actual_bundle_files = { + path.resolve() + for path in (root / "licenses" / "third-party").rglob("*") + if path.is_file() + } + if actual_bundle_files != expected_bundle_files: + stale = sorted(str(path.relative_to(root)) for path in actual_bundle_files - expected_bundle_files) + missing = sorted(str(path.relative_to(root)) for path in expected_bundle_files - actual_bundle_files) + raise AuditError(f"third-party notice file set differs; stale={stale}, missing={missing}") + return packages + + +def refresh_notice_bundle(metadata: dict, root: pathlib.Path = ROOT) -> None: + """Copy the exact locked license files and render their inventory table.""" + packages = registry_packages(metadata) + third_party_root = root / "licenses" / "third-party" + if third_party_root.exists(): + for path in sorted(third_party_root.rglob("*"), reverse=True): + if path.is_file(): + path.unlink() + elif path.is_dir(): + try: + path.rmdir() + except OSError: + pass + expected_files = set() + rows = [ + "# Third party notices", + "", + "This inventory covers all registry packages in `Cargo.lock`, including optional and target-specific packages.", + "Legacy slash-separated license alternatives are normalized to SPDX `OR` expressions.", + "License and author files are copied from the resolved crate sources and checked byte-for-byte; when a target-specific package omits them, an identical SPDX declaration from the same upstream repository supplies the files.", + "", + "| Package | Version | SPDX license expression | Included license/author files (SHA-256) |", + "|---|---:|---|---|", + ] + + for key, package in sorted(packages.items()): + name, version = key + expression = canonical_license_expression(package.get("license")) + source_dir, source_files = license_source(package, packages) + if not source_files: + raise AuditError(f"no recognized license/notice files for {name} {version}") + file_links = [] + for filename, source_filename in sorted(source_files.items()): + relative = pathlib.Path("third-party") / f"{name}-{version}" / filename + output = root / "licenses" / relative + output.parent.mkdir(parents=True, exist_ok=True) + data = (source_dir / source_filename).read_bytes() + output.write_bytes(data) + expected_files.add(output.resolve()) + file_links.append( + f"[`{filename}`]({relative.as_posix()}) `{hashlib.sha256(data).hexdigest()}`" + ) + rows.append( + f"| `{name}` | {version} | {expression} | " + + "
".join(file_links) + + " |" + ) + + sqlite_package = packages.get(("libsqlite3-sys", "0.38.2")) + if sqlite_package: + sqlite_version, _, sqlite_notice = sqlite_bundled_notice(sqlite_package) + relative = pathlib.Path("third-party/libsqlite3-sys-0.38.2/SQLITE_BLESSING.txt") + output = root / "licenses" / relative + output.parent.mkdir(parents=True, exist_ok=True) + output.write_bytes(sqlite_notice) + expected_files.add(output.resolve()) + rows.extend( + [ + "", + "## Bundled third-party source notice", + "", + "The `bundled` rusqlite feature compiles this SQLite amalgamation into Rover. The exact public-domain blessing block is retained separately from the libsqlite3-sys MIT license.", + "", + "| Source component | Bundling crate | License notice | Included notice (SHA-256) |", + "|---|---|---|---|", + f"| `SQLite {sqlite_version}` | `libsqlite3-sys 0.38.2` | public-domain blessing | [`SQLITE_BLESSING.txt`]({relative.as_posix()}) `{hashlib.sha256(sqlite_notice).hexdigest()}` |", + ] + ) + + if third_party_root.exists(): + for path in sorted(third_party_root.rglob("*"), reverse=True): + if path.is_file() and path.resolve() not in expected_files: + path.unlink() + elif path.is_dir(): + try: + path.rmdir() + except OSError: + pass + (root / "licenses" / "THIRD_PARTY_NOTICES.md").write_text("\n".join(rows) + "\n") + + +def cargo_tree_targets( + root: pathlib.Path = ROOT, +) -> tuple[dict[str, set[tuple[str, str]]], dict[str, set[tuple[tuple[str, str], tuple[str, str]]]]]: + active_by_target = {} + edges_by_target = {} + for target in SUPPORTED_TARGETS: + command = [ + "cargo", + "tree", + "--locked", + "--offline", + "--target", + target, + "--prefix", + "depth", + "-e", + "normal,build", + ] + try: + result = subprocess.run(command, cwd=root, text=True, capture_output=True, check=True) + except (OSError, subprocess.CalledProcessError) as error: + detail = getattr(error, "stderr", "") or str(error) + raise AuditError(f"Cargo tree failed for {target}: {detail.strip()}") from error + found = set() + edges = set() + stack = [] + for line in result.stdout.splitlines(): + match = TREE_LINE.match(line) + if match: + depth = int(match.group(1)) + package = (match.group(2), match.group(3)) + found.add(package) + if depth and len(stack) >= depth: + edges.add((stack[depth - 1], package)) + stack = stack[:depth] + stack.append(package) + if not found: + raise AuditError(f"Cargo tree returned no packages for target {target}") + active_by_target[target] = found + edges_by_target[target] = edges + return active_by_target, edges_by_target + + +def generate_sbom( + metadata: dict, + lock: dict, + packages: dict, + active_by_target: dict[str, set[tuple[str, str]]], + edges_by_target: dict[str, set[tuple[tuple[str, str], tuple[str, str]]]], + root: pathlib.Path = ROOT, +) -> dict: + lock_packages = {package_key(package): package for package in lock["package"]} + components = [] + package_refs = {} + + workspace_packages = [ + package + for package in metadata["packages"] + if package["id"] in metadata["workspace_members"] + ] + for package in workspace_packages: + key = package_key(package) + license_expression = package.get("license") + try: + license_expression = canonical_license_expression(license_expression) + except AuditError as error: + raise AuditError( + f"unreviewed SPDX license expression for workspace package {key}: {license_expression}" + ) from error + package_refs[key] = package_purl(*key) + components.append( + { + "type": "library", + "bom-ref": package_purl(*key), + "name": key[0], + "version": key[1], + "purl": package_purl(*key), + "scope": "required", + "licenses": [{"license": {"id": license_expression, "acknowledgement": "declared"}}], + "externalReferences": [ + {"type": "vcs", "url": "https://github.com/GrayCodeAI/rover"} + ], + } + ) + + for key, package in sorted(packages.items()): + locked = lock_packages[key] + if not locked.get("checksum"): + raise AuditError(f"registry crate lacks Cargo.lock checksum: {key}") + active_targets = sorted(target for target, active in active_by_target.items() if key in active) + package_refs[key] = package_purl(*key) + component = { + "type": "library", + "bom-ref": package_purl(*key), + "name": key[0], + "version": key[1], + "purl": package_purl(*key), + "scope": "required" if active_targets else "optional", + "licenses": [ + { + "expression": canonical_license_expression(package["license"]), + "acknowledgement": "declared", + } + ], + "hashes": [{"alg": "SHA-256", "content": locked["checksum"]}], + "externalReferences": [ + {"type": "distribution", "url": f"https://crates.io/crates/{key[0]}/{key[1]}"} + ], + } + if active_targets: + component["properties"] = [ + {"name": "rover:rust:targets", "value": ",".join(active_targets)} + ] + else: + component["properties"] = [ + {"name": "rover:rust:locked-but-not-active", "value": "true"} + ] + components.append(component) + + sqlite_package = packages.get(("libsqlite3-sys", "0.38.2")) + sqlite_identity = None + if sqlite_package: + sqlite_version, sqlite_source, _ = sqlite_bundled_notice(sqlite_package) + sqlite_identity = ("sqlite-amalgamation", sqlite_version) + sqlite_ref = f"pkg:generic/sqlite@{quote(sqlite_version, safe='-._~')}" + package_refs[sqlite_identity] = sqlite_ref + components.append( + { + "type": "library", + "bom-ref": sqlite_ref, + "name": "SQLite amalgamation", + "version": sqlite_version, + "purl": sqlite_ref, + "scope": "required", + "licenses": [ + {"license": {"name": "SQLite public-domain blessing"}} + ], + "hashes": [ + { + "alg": "SHA-256", + "content": hashlib.sha256(sqlite_source).hexdigest(), + } + ], + "externalReferences": [ + {"type": "website", "url": "https://www.sqlite.org/"} + ], + "properties": [ + { + "name": "rover:bundled-by", + "value": "libsqlite3-sys 0.38.2 bundled feature", + } + ], + } + ) + + all_edges = set().union(*edges_by_target.values()) + dependencies_by_package: dict[tuple[str, str], set[tuple[str, str]]] = defaultdict(set) + for parent, child in all_edges: + dependencies_by_package[parent].add(child) + if sqlite_identity: + dependencies_by_package[("libsqlite3-sys", "0.38.2")].add(sqlite_identity) + dependency_items = [] + for package_key_value, ref in sorted(package_refs.items()): + dependencies = sorted( + package_refs[child] + for child in dependencies_by_package.get(package_key_value, set()) + if child in package_refs + ) + dependency_items.append({"ref": ref, "dependsOn": dependencies}) + + # Cargo metadata describes all lockfile-resolved edges, including optional + # and target-specific dependencies. The scoped component inventory is exact; + # the graph records Cargo's resolved lockfile relationships. + version = (root / "VERSION").read_text().strip() + root_ref = f"pkg:generic/rover@{quote(version, safe='-._~')}" + root_component = { + "type": "application", + "bom-ref": root_ref, + "name": "rover", + "version": version, + "purl": root_ref, + "licenses": [{"license": {"id": "MIT", "acknowledgement": "declared"}}], + } + dependency_items.append( + { + "ref": root_ref, + "dependsOn": sorted( + package_purl(package["name"], package["version"]) + for package in workspace_packages + ), + } + ) + + bom = { + "$schema": "https://cyclonedx.org/schema/bom-1.7.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.7", + "version": 1, + "metadata": {"component": root_component}, + "components": components, + "dependencies": dependency_items, + } + return bom + + +def run_check(root: pathlib.Path = ROOT) -> tuple[dict, dict, dict]: + metadata, lock = load_cargo_data(root) + packages = check_notices(metadata, lock, root) + active, edges = cargo_tree_targets(root) + return metadata, lock, generate_sbom(metadata, lock, packages, active, edges, root) + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--check", action="store_true", help="verify lockfile license metadata and bundled notices") + parser.add_argument("--refresh-notices", action="store_true", help="copy exact locked license notices into licenses/third-party") + parser.add_argument("--sbom", type=pathlib.Path, help="write a CycloneDX 1.7 JSON SBOM at this path") + arguments = parser.parse_args(argv) + try: + if arguments.refresh_notices: + metadata, _ = load_cargo_data() + refresh_notice_bundle(metadata) + _, _, bom = run_check() + if arguments.sbom: + arguments.sbom.parent.mkdir(parents=True, exist_ok=True) + arguments.sbom.write_text(json.dumps(bom, indent=2, sort_keys=True) + "\n") + print(f"Wrote {len(bom['components'])} Rust SBOM components to {arguments.sbom}") + else: + print("Rust dependency licenses and bundled notices match Cargo.lock.") + return 0 + except (AuditError, OSError, json.JSONDecodeError, tomllib.TOMLDecodeError) as error: + print(f"Rust dependency audit failed: {error}", file=sys.stderr) + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/test_herdr_manifest_audit.py b/scripts/test_herdr_manifest_audit.py new file mode 100644 index 0000000..b909521 --- /dev/null +++ b/scripts/test_herdr_manifest_audit.py @@ -0,0 +1,43 @@ +"""Verify the byte-identical Herdr detection data reuse record.""" + +import hashlib +import re +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +AUDIT = ROOT / "docs/design/UPSTREAM_SOURCE_AUDIT.md" +SOURCE_COMMIT = "c411883ec639c9893ed9c33021c890485e91727b" +MANIFEST_ROW = re.compile( + r"^\| `([^`]+\.toml)` \| `([^`]+\.toml)` \| `([0-9a-f]{64})` \|$", + re.MULTILINE, +) + + +class HerdrManifestAuditTests(unittest.TestCase): + def test_all_bundled_manifest_files_match_the_documented_upstream_hashes(self): + audit = AUDIT.read_text(encoding="utf-8") + self.assertIn(SOURCE_COMMIT, audit) + rows = MANIFEST_ROW.findall(audit) + self.assertEqual(len(rows), 22) + upstream_paths = set() + rover_paths = set() + for source_path, rover_path, expected in rows: + self.assertTrue(source_path.startswith("src/detect/manifests/")) + self.assertTrue(rover_path.startswith("crates/rover-agents/src/herdr_manifests/")) + upstream_paths.add(source_path) + rover_paths.add(rover_path) + actual = hashlib.sha256((ROOT / rover_path).read_bytes()).hexdigest() + self.assertEqual(actual, expected, rover_path) + self.assertEqual(len(upstream_paths), 22) + self.assertEqual(len(rover_paths), 22) + + def test_root_apache_license_copy_matches_the_recorded_digest(self): + expected = "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4" + license_path = ROOT / "licenses/upstream/herdr/LICENSE" + self.assertEqual(hashlib.sha256(license_path.read_bytes()).hexdigest(), expected) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test_rust_dependency_audit.py b/scripts/test_rust_dependency_audit.py new file mode 100644 index 0000000..e5e98ca --- /dev/null +++ b/scripts/test_rust_dependency_audit.py @@ -0,0 +1,192 @@ +import hashlib +import pathlib +import tempfile +import unittest + +from rust_dependency_audit import ( + AuditError, + canonical_license_expression, + check_notices, + generate_sbom, + license_source, + package_purl, + recognized_license_files, + sqlite_bundled_notice, +) + + +class DependencyAuditTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory() + self.root = pathlib.Path(self.temp.name) + (self.root / "VERSION").write_text("0.1.0\n") + self.licenses = self.root / "licenses" + self.licenses.mkdir() + self.source = self.root / "registry" / "sample-1.2.3" + self.source.mkdir(parents=True) + self.license_text = b"Sample license notice\n" + (self.source / "LICENSE-MIT").write_bytes(self.license_text) + self.bundle_file = self.licenses / "third-party" / "sample-1.2.3" / "LICENSE-MIT" + self.bundle_file.parent.mkdir(parents=True) + self.bundle_file.write_bytes(self.license_text) + self.license_hash = hashlib.sha256(self.license_text).hexdigest() + (self.licenses / "third-party").mkdir(exist_ok=True) + (self.licenses / "THIRD_PARTY_NOTICES.md").write_text( + "| Package | Version | SPDX license expression | Included license/author files (SHA-256) |\n" + "|---|---:|---|---|\n" + "| `sample` | 1.2.3 | MIT OR Apache-2.0 | " + f"[`LICENSE-MIT`](third-party/sample-1.2.3/LICENSE-MIT) `{self.license_hash}` |\n" + ) + self.registry_package = { + "name": "sample", + "version": "1.2.3", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "license": "MIT OR Apache-2.0", + "manifest_path": str(self.source / "Cargo.toml"), + } + (self.source / "Cargo.toml").write_text('[package]\nname="sample"\n') + self.metadata = {"packages": [self.registry_package]} + self.lock = { + "package": [ + { + "name": "sample", + "version": "1.2.3", + "source": self.registry_package["source"], + "checksum": "a" * 64, + } + ] + } + + def tearDown(self): + self.temp.cleanup() + + def test_purl_encodes_package_identity(self): + self.assertEqual(package_purl("sample+crate", "1.2.3"), "pkg:cargo/sample%2Bcrate@1.2.3") + + def test_license_file_inventory_preserves_actual_case_on_case_insensitive_filesystems(self): + lowercase_source = self.root / "lowercase-license" + lowercase_source.mkdir() + (lowercase_source / "license-mit").write_text("MIT license\n") + self.assertEqual(recognized_license_files(lowercase_source), {"LICENSE-MIT": "license-mit"}) + + def test_markdown_license_files_are_included_in_the_notice_inventory(self): + markdown_source = self.root / "markdown-license" + markdown_source.mkdir() + (markdown_source / "LICENSE.md").write_text("MIT license\n") + self.assertEqual( + recognized_license_files(markdown_source), {"LICENSE.md": "LICENSE.md"} + ) + + def test_cc0_and_boost_license_files_are_retained(self): + source = self.root / "alternative-licenses" + source.mkdir() + (source / "license-cc0").write_text("CC0 license\n") + (source / "LICENSE-BOOST").write_text("Boost license\n") + self.assertEqual( + recognized_license_files(source), + {"LICENSE-CC0": "license-cc0", "LICENSE-BOOST": "LICENSE-BOOST"}, + ) + + def test_omitted_target_license_uses_matching_files_from_the_same_repository(self): + self.registry_package["repository"] = "https://example.test/upstream" + target_source = self.root / "target-import" + target_source.mkdir() + target = { + "name": "sample-target-import", + "version": "0.1.0", + "license": "MIT OR Apache-2.0", + "repository": "https://example.test/upstream", + "manifest_path": str(target_source / "Cargo.toml"), + } + source_dir, files = license_source( + target, {("sample", "1.2.3"): self.registry_package, ("sample-target-import", "0.1.0"): target} + ) + self.assertEqual(source_dir, self.source) + self.assertEqual(files, {"LICENSE-MIT": "LICENSE-MIT"}) + + def test_legacy_manifest_license_label_normalizes_to_spdx(self): + self.assertEqual(canonical_license_expression("Apache-2.0"), "Apache-2.0") + self.assertEqual(canonical_license_expression("MIT/Apache-2.0"), "MIT OR Apache-2.0") + self.assertEqual(canonical_license_expression("Apache-2.0/MIT"), "Apache-2.0 OR MIT") + self.assertEqual( + canonical_license_expression("Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT"), + "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + ) + self.assertEqual( + canonical_license_expression("BSD-2-Clause OR Apache-2.0"), + "BSD-2-Clause OR Apache-2.0", + ) + for expression in ( + "MIT OR Apache-2.0 OR CC0-1.0", + "Zlib", + "Apache-2.0 OR BSL-1.0", + ): + with self.subTest(expression=expression): + self.assertEqual(canonical_license_expression(expression), expression) + with self.assertRaises(AuditError): + canonical_license_expression("GPL-3.0-only") + + def test_bundled_sqlite_notice_is_extracted_from_pinned_source(self): + source = self.root / "sqlite-source" + sqlite_dir = source / "sqlite3" + sqlite_dir.mkdir(parents=True) + source_text = ( + b'#define SQLITE_VERSION "3.53.2"\n' + b'** The author disclaims copyright to this source code. In place of\n' + b'** a legal notice, here is a blessing:\n' + b'**\n' + b'** May you do good and not evil.\n' + b'*************************************************************************\n' + ) + (sqlite_dir / "sqlite3.c").write_bytes(source_text) + package = {"manifest_path": str(source / "Cargo.toml")} + version, exact_source, notice = sqlite_bundled_notice(package) + self.assertEqual(version, "3.53.2") + self.assertEqual(exact_source, source_text) + self.assertTrue(notice.startswith(b"** The author disclaims copyright")) + self.assertIn(b"May you do good and not evil.", notice) + + def test_notice_check_rejects_byte_or_hash_drift(self): + packages = check_notices(self.metadata, self.lock, self.root) + self.assertEqual(set(packages), {("sample", "1.2.3")}) + self.bundle_file.write_bytes(b"changed notice") + with self.assertRaises(AuditError): + check_notices(self.metadata, self.lock, self.root) + + def test_notice_check_rejects_unreviewed_license_expression(self): + self.registry_package["license"] = "GPL-3.0-only" + with self.assertRaises(AuditError): + check_notices(self.metadata, self.lock, self.root) + + def test_cyclonedx_inventory_contains_pinned_component_and_graph(self): + workspace = { + "id": "path+file:///rover#rover-core@0.1.0", + "name": "rover-core", + "version": "0.1.0", + "license": "MIT", + } + metadata = { + "packages": [workspace], + "workspace_members": [workspace["id"]], + } + lock = { + "package": [ + {"name": "rover-core", "version": "0.1.0"}, + self.lock["package"][0], + ] + } + active = {"test-target": {("rover-core", "0.1.0"), ("sample", "1.2.3")}} + edges = {"test-target": {(('rover-core', '0.1.0'), ('sample', '1.2.3'))}} + bom = generate_sbom(metadata, lock, {("sample", "1.2.3"): self.registry_package}, active, edges, self.root) + self.assertEqual(bom["bomFormat"], "CycloneDX") + self.assertEqual(bom["specVersion"], "1.7") + component_refs = {component["bom-ref"] for component in bom["components"]} + self.assertIn("pkg:cargo/sample@1.2.3", component_refs) + self.assertIn("pkg:cargo/rover-core@0.1.0", component_refs) + refs = {row["ref"] for row in bom["dependencies"]} + self.assertEqual(refs, component_refs | {bom["metadata"]["component"]["bom-ref"]}) + self.assertTrue(all(set(row["dependsOn"]) <= refs for row in bom["dependencies"])) + + +if __name__ == "__main__": + unittest.main() From e88c7c068b446dd8281bbf71f5a4468ba8a651b3 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:42:26 +0530 Subject: [PATCH 04/23] ci(rust): gate the Rust workspace in Make and source-validation Add CARGO-overridable make targets: rust-fmt-check, rust-clippy (--all-targets, -D warnings), rust-test, rust-deps-check (script unit tests plus the locked license/notice audit), the aggregate rust-check, rust-sbom and rust-notices. CI installs the pinned 1.88.0 toolchain with clippy and rustfmt, fetches the locked dependencies and runs rust-check and rust-sbom. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 8 ++++++++ Makefile | 25 ++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8840e4..b5b19da 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,14 @@ jobs: cache: false - name: Build dependencies run: sudo apt-get update && sudo apt-get install -y build-essential libsqlite3-dev python3 + - name: Install pinned Rust MSRV toolchain + run: rustup toolchain install 1.88.0 --profile minimal --component clippy --component rustfmt + - name: Fetch locked Rust dependencies + run: cargo +1.88.0 fetch --locked + - name: Format, lint, test, and audit Rust workspace + run: make rust-check CARGO='cargo +1.88.0' + - name: Generate Rust CycloneDX SBOM + run: make rust-sbom - name: Validate Go toolchain run: make toolchain-check - name: Validate version surfaces diff --git a/Makefile b/Makefile index 28694e9..e818015 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ GO ?= go PYTHON ?= python3 +CARGO ?= cargo export GOTOOLCHAIN ?= go1.26.6+auto export CGO_ENABLED := 1 @@ -7,7 +8,7 @@ VERSION_VALUE := $(strip $(shell tr -d '\r\n' < VERSION)) COMMIT_VALUE := $(strip $(shell git rev-parse --verify HEAD 2>/dev/null || printf unknown)) LDFLAGS := -X github.com/GrayCodeAI/rover/internal/model.Version=$(VERSION_VALUE) -X github.com/GrayCodeAI/rover/internal/model.Commit=$(COMMIT_VALUE) -.PHONY: build test race vet fmt fmt-check check demo fuzz install clean cross-build sbom vulncheck toolchain-check version version-check manifest manifest-check +.PHONY: build test race vet fmt fmt-check check demo fuzz install clean cross-build sbom vulncheck toolchain-check version version-check manifest manifest-check rust-fmt-check rust-clippy rust-test rust-deps-check rust-check rust-sbom rust-notices build: mkdir -p bin $(GO) build -trimpath -ldflags '$(LDFLAGS)' -o bin/rover ./cmd/rover @@ -44,6 +45,28 @@ fmt-check: check: fmt-check vet test +rust-fmt-check: + $(CARGO) fmt --all -- --check + +rust-clippy: + $(CARGO) clippy --workspace --all-targets --locked --offline -- -D warnings + +rust-test: + $(CARGO) test --workspace --locked --offline + +rust-deps-check: + $(PYTHON) -m unittest discover -s scripts -p 'test_*.py' + $(PYTHON) scripts/rust_dependency_audit.py --check + +rust-check: rust-fmt-check rust-clippy rust-test rust-deps-check + +rust-sbom: + mkdir -p bin + $(PYTHON) scripts/rust_dependency_audit.py --sbom bin/rover-rust-sbom.cdx.json + +rust-notices: + $(PYTHON) scripts/rust_dependency_audit.py --refresh-notices + demo: build $(PYTHON) scripts/demo.py --binary bin/rover From d0306c21ee0097f9b5fc5c15b6ebea349d655919 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:42:51 +0530 Subject: [PATCH 05/23] chore: remove the Python, TypeScript and Go SDK source trees Per product direction Rover ships no SDK clients. Delete sdk/, the sdk-test make target and the three SDK CI steps, drop the SDK package version checks from TestVersionSurfaces and add TestSDKSourceTreeRemoved so the tree cannot silently return. Historical plans and audit reports gain a banner saying their sdk/ citations describe removed code. The SDK clients were never published to a package registry. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 6 - CHANGELOG.md | 5 + Makefile | 6 +- docs/CI.md | 3 +- docs/PLAN_ALL.md | 10 +- internal/model/version_test.go | 28 ++-- reports/Rover OSS comparison 2026.md | 2 + sdk/README.md | 13 -- sdk/go/README.md | 21 --- sdk/go/rover_client.go | 199 --------------------------- sdk/go/rover_client_test.go | 58 -------- sdk/python/README.md | 18 --- sdk/python/pyproject.toml | 24 ---- sdk/python/rover_client.py | 72 ---------- sdk/python/test_client.py | 23 ---- sdk/typescript/README.md | 20 --- sdk/typescript/package.json | 16 --- sdk/typescript/src/rover_client.ts | 115 ---------------- sdk/typescript/test/client.test.ts | 33 ----- 19 files changed, 27 insertions(+), 645 deletions(-) delete mode 100644 sdk/README.md delete mode 100644 sdk/go/README.md delete mode 100644 sdk/go/rover_client.go delete mode 100644 sdk/go/rover_client_test.go delete mode 100644 sdk/python/README.md delete mode 100644 sdk/python/pyproject.toml delete mode 100644 sdk/python/rover_client.py delete mode 100644 sdk/python/test_client.py delete mode 100644 sdk/typescript/README.md delete mode 100644 sdk/typescript/package.json delete mode 100644 sdk/typescript/src/rover_client.ts delete mode 100644 sdk/typescript/test/client.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b5b19da..e54bcc4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,12 +46,6 @@ jobs: run: make demo - name: Extended terminal, workflow and protocol scenarios run: make demo-extended - - name: Python CLI client - run: make sdk-test - - name: TypeScript CLI client - run: node --test sdk/typescript/test/*.test.ts - - name: Go CLI client - run: go test ./sdk/go -count=1 -v - name: Cross-compile for all supported platforms run: make cross-build - name: Vulnerability scanning diff --git a/CHANGELOG.md b/CHANGELOG.md index b4c76ac..449da36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Unreleased + +- Remove the Python, TypeScript, and Go SDK source trees and their CI/Make + targets per product direction. Historical validation records remain dated. + ## 0.0.1 - Establish the current public release identity and fail-closed version checks. diff --git a/Makefile b/Makefile index e818015..8150588 100644 --- a/Makefile +++ b/Makefile @@ -108,10 +108,6 @@ install: build clean: rm -f bin/rover -.PHONY: demo-extended sdk-test +.PHONY: demo-extended demo-extended: build python3 scripts/demo_extended.py --binary bin/rover -sdk-test: - python3 -m unittest discover -s sdk/python -p 'test_*.py' - node --test sdk/typescript/test/*.test.ts - go test ./sdk/go -count=1 -v diff --git a/docs/CI.md b/docs/CI.md index 7be8fc9..b21adea 100644 --- a/docs/CI.md +++ b/docs/CI.md @@ -10,14 +10,13 @@ Triggers on `push`, `pull_request`, and `workflow_dispatch`. Runs on | Step | Make target | What it checks | |------|-------------|----------------| | Go toolchain | `make toolchain-check` | Go 1.26.6 or newer | -| Version contract | `make version-check` | `VERSION`, runtime, SDK, capability, documentation, and release metadata agree | +| Version contract | `make version-check` | `VERSION`, runtime, capability, documentation, and release metadata agree; removed SDK tree stays absent | | Source manifest | `make manifest-check` | Every tracked source file matches its generated size and SHA-256 entry | | Format, vet, tests | `make check` | `gofmt -l`, `go vet`, `go test ./...` (25 packages) | | Race detector | `make race` | `go test -race ./...` — data race detection | | Fuzz campaigns | `make fuzz` | `FuzzDecode`, `FuzzSafeName`, `FuzzResultParser`, `FuzzTranscript`, `FuzzEnvelope` (3s each) | | Demo smoke | `make demo` | 12 end-to-end CLI scenarios (no model/network) | | Extended scenarios | `make demo-extended` | 16 scenarios: PTY, TUI, workflow, MCP, remote, backup | -| SDK tests | `make sdk-test` + inline | Python 3/3, TypeScript 3/3, Go 2/2 SDK tests | | Cross-compile | `make cross-build` | linux/amd64 (cgo), darwin/amd64 + darwin/arm64 (cgo-free) | | Vulnerability scan | `make vulncheck` | Pinned `govulncheck` v1.8.0 — scans for known CVEs | diff --git a/docs/PLAN_ALL.md b/docs/PLAN_ALL.md index b3df657..e1ccc1f 100644 --- a/docs/PLAN_ALL.md +++ b/docs/PLAN_ALL.md @@ -1,5 +1,9 @@ # Detailed plan to implement all remaining gaps — honest, end-to-end, verifiable +> Historical plan, superseded by `docs/design/RUST_PARITY_PLAN.md`. SDK results +> below describe clients that existed when this plan was written; the SDK source +> has since been removed by user direction. + This plan covers every one of the 40 acceptance IDs and the 9 ROADMAP gates. It does not invent live provider, Docker daemon, remote-host, SSO, or hosted-CI results. For each gap it states: domain owner, reviewed contract, regression @@ -13,7 +17,7 @@ test, and exact limitation that remains until the external dependency is supplie - Increment C: local gaps — `doctor --verify` (A02), `publish --id/--to` (A28/A31), local fencing docs (A29), formal-scope labelling (A26), absence-by-design (A39) - `make check` green (24 pkgs, 90 top funcs, 5 fuzz), `make race` green, - `make fuzz`/`make sdk-test` green; demo 12/12 and demo_extended 16/16 via + `make fuzz`/then-current SDK tests green; demo 12/12 and demo_extended 16/16 via `GOFLAGS=-buildvcs=false make demo` (the TCC-blocked `.git` under `~/Desktop` breaks VCS stamping on the plain `make build` path) - `STATUS.md` updated per increment with honest Linux-reverification gate @@ -42,7 +46,7 @@ test, and exact limitation that remains until the external dependency is supplie 1. Toolchain/OS: darwin PTY done; **Linux/amd64 re-run DONE** (`go1.23.12 linux/amd64` in `golang:1.23-bookworm` on OrbStack VM: check/race/demo - 12/demo-extended 16/sdk 3/fuzz all green — see `verification_log` in the + 12/demo-extended 16/legacy SDK 3/fuzz all green — see `verification_log` in the implementation map); Windows stub remains a stub 2. Restricted executor + CI publisher: Docker flags done; **functional smoke ran a real pinned container on the local daemon from Linux** (same machine, not @@ -80,7 +84,7 @@ After all code: run in this environment (darwin/arm64, go1.26.6, no external Go - `make race` (`go test -race`) - `GOFLAGS=-buildvcs=false make demo` (12 scenarios) - `GOFLAGS=-buildvcs=false make demo-extended` (16 scenarios) -- `make sdk-test` (`python3 -m unittest discover -s sdk/python`) +- Legacy SDK tests (historical only; SDK source has been removed) - `make fuzz` (3s × 2: `FuzzDecode`, `FuzzSafeName`, `FuzzResultParser`, `FuzzTranscript`, `FuzzEnvelope`) - schema validation 10/10 Each run reports exact pass/fail; no invented CI/provider results. diff --git a/internal/model/version_test.go b/internal/model/version_test.go index 3c6c161..8b79aa9 100644 --- a/internal/model/version_test.go +++ b/internal/model/version_test.go @@ -12,7 +12,6 @@ import ( ) var versionPattern = regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$`) -var pyprojectVersion = regexp.MustCompile(`(?m)^\s*version\s*=\s*"([^"]+)"\s*$`) var releaseHeading = regexp.MustCompile(`(?m)^## ([0-9][^\n]*)$`) var toolchainPattern = regexp.MustCompile(`^go([0-9]+)\.([0-9]+)\.([0-9]+)`) @@ -64,22 +63,6 @@ func TestVersionSurfaces(t *testing.T) { t.Fatalf("manifest version = %q, want %q", manifest.Version, expected) } - var packageData struct { - Version string `json:"version"` - } - if err := json.Unmarshal(repositoryFile(t, "sdk/typescript/package.json"), &packageData); err != nil { - t.Fatalf("TypeScript package JSON: %v", err) - } - if packageData.Version != expected { - t.Fatalf("TypeScript package version = %q, want %q", packageData.Version, expected) - } - - pyproject := string(repositoryFile(t, "sdk/python/pyproject.toml")) - matches := pyprojectVersion.FindAllStringSubmatch(pyproject, -1) - if len(matches) != 1 || matches[0][1] != expected { - t.Fatalf("Python package version does not match %q", expected) - } - readme := string(repositoryFile(t, "README.md")) if !strings.Contains(readme, "version-"+expected+"-blue") { t.Fatalf("README version badge does not match %q", expected) @@ -131,6 +114,17 @@ func TestVersionSurfaces(t *testing.T) { } } +func TestSDKSourceTreeRemoved(t *testing.T) { + _, file, _, ok := runtime.Caller(0) + if !ok { + t.Fatal("runtime.Caller failed") + } + path := filepath.Clean(filepath.Join(filepath.Dir(file), "..", "..", "sdk")) + if _, err := os.Stat(path); !os.IsNotExist(err) { + t.Fatalf("SDK source tree must be removed; stat %s returned %v", path, err) + } +} + func TestToolchainMinimum(t *testing.T) { match := toolchainPattern.FindStringSubmatch(runtime.Version()) if len(match) != 4 { diff --git a/reports/Rover OSS comparison 2026.md b/reports/Rover OSS comparison 2026.md index d921a67..94eb311 100644 --- a/reports/Rover OSS comparison 2026.md +++ b/reports/Rover OSS comparison 2026.md @@ -2,6 +2,8 @@ > This report records the pre-change audit baseline. A first release-truth slice was implemented after the audit; the original findings remain as the baseline for measuring that work. +> **Stale references.** The Python, TypeScript, and Go SDK source trees were removed after this audit by explicit product direction (see `CHANGELOG.md` and `docs/design/RUST_COMPATIBILITY_BASELINE.md`). Every `sdk/...` line citation below therefore refers to code that no longer exists. The analysis is left as written because it is a dated baseline; do not read those paths as current. + ## Implementation checkpoint after the audit - `VERSION` now drives Makefile linker metadata, and the CLI reports the injected commit. diff --git a/sdk/README.md b/sdk/README.md deleted file mode 100644 index 69fcbaa..0000000 --- a/sdk/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Rover SDKs — monorepo - -All three clients are thin argv wrappers around your explicit `bin/rover --state --json`. No shell, no auto-retry, no provider credentials. `task run` exit 0 = admitted, not accepted. - -| SDK | Path | Install | Test | -|-----|------|---------|------| -| Python | `sdk/python/` | `pip install rover-client` (after publish) or copy `rover_client.py` | `python3 -m unittest discover -s sdk/python -p 'test_*.py'` | -| TypeScript | `sdk/typescript/` | `npm install @graycodeai/rover` (after publish) | `node --test sdk/typescript/test/*.test.ts` | -| Go | `sdk/go/` | `go get github.com/GrayCodeAI/rover/sdk/go` | `go test ./sdk/go -count=1` | - -Or run all: `make sdk-test`. - -Mirrors: same `Rover` surface — `inspect` / `verify` / `runTask` / `status` / `report` + bounded JSON + explicit timeout. No telemetry or auto-publish. diff --git a/sdk/go/README.md b/sdk/go/README.md deleted file mode 100644 index df2b269..0000000 --- a/sdk/go/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Go CLI client - -`rover_client.go` requires only Go's standard library. It invokes your explicitly -selected Rover binary with argv, never a shell. No external dependencies. - -```go -import rover "github.com/GrayCodeAI/rover/sdk/go" - -client, _ := rover.New("/absolute/path/to/rover", "/absolute/path/to/private-state") -result, _ := client.Status() -fmt.Println(result.ExitCode, result.Data) -``` - -Task submission exit zero means admitted, not accepted software. Verification -codes 1/2/3 remain ordinary structured `Result`s rather than being hidden by an -error. A client timeout may leave an admitted detached task running. Reconcile -before retrying. External installations and credentials are never automatic. - -Run tests: `go test ./sdk/go -count=1` - -Mirrors `sdk/python/rover_client.py` and `sdk/typescript/src/rover_client.ts` — same argv surface, bounds, and error semantics. diff --git a/sdk/go/rover_client.go b/sdk/go/rover_client.go deleted file mode 100644 index 5263ed0..0000000 --- a/sdk/go/rover_client.go +++ /dev/null @@ -1,199 +0,0 @@ -// Package rover is a small standard-library client for Rover's CLI (v0.x API). -// No shell, automatic retries, provider credentials, or acceptance inference. -// A CLI submission may continue after a client timeout; reconcile before retrying. -package rover - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "os/exec" - "path/filepath" - "time" -) - -// RoverError is a protocol, invocation or bounded-output failure. -type RoverError struct{ msg string } - -func (e *RoverError) Error() string { return e.msg } - -// Result is a bounded JSON result from the CLI. -type Result struct { - ExitCode int - Data any -} - -// Decision returns data["decision"] when present. -func (r Result) Decision() string { - if m, ok := r.Data.(map[string]any); ok { - if s, ok := m["decision"].(string); ok { - return s - } - } - return "" -} - -// Rover invokes an explicit Rover binary with argv, never a shell. -type Rover struct { - Binary string - State string - Timeout time.Duration - MaxOutput int -} - -// New creates a Rover client. -func New(binary, state string, opts ...Option) (*Rover, error) { - absBin, err := filepath.Abs(binary) - if err != nil { - return nil, err - } - absState, err := filepath.Abs(state) - if err != nil { - return nil, err - } - r := &Rover{Binary: absBin, State: absState, Timeout: 60 * time.Second, MaxOutput: 16 << 20} - for _, o := range opts { - o(r) - } - if r.Timeout <= 0 || r.MaxOutput < 1024 { - return nil, errors.New("positive timeout and output bound required") - } - return r, nil -} - -type Option func(*Rover) - -func WithTimeout(d time.Duration) Option { return func(r *Rover) { r.Timeout = d } } -func WithMaxOutput(n int) Option { return func(r *Rover) { r.MaxOutput = n } } - -// Call invokes: rover --state --json -func (r *Rover) Call(args []string) (Result, error) { - for _, a := range args { - if a == "" { - return Result{}, &RoverError{"args must be non-empty strings"} - } - } - argv := append([]string{"--state", r.State}, args...) - argv = append(argv, "--json") - ctx, cancel := context.WithTimeout(context.Background(), r.Timeout) - defer cancel() - cmd := exec.CommandContext(ctx, r.Binary, argv...) - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - err := cmd.Run() - if ctx.Err() == context.DeadlineExceeded { - return Result{}, &RoverError{"Client timed out. An admitted task may still be running; inspect state before retrying."} - } - if stdout.Len() > r.MaxOutput { - return Result{}, &RoverError{"Rover output exceeded configured client limit"} - } - if err != nil { - // still try to parse JSON; Rover may return JSON on non-zero exit - if stdout.Len() == 0 { - return Result{}, &RoverError{fmt.Sprintf("Rover did not return JSON (exit %d): %s", exitCode(cmd), truncate(stderr.String(), 400))} - } - } - var data any - if err := json.Unmarshal(stdout.Bytes(), &data); err != nil { - return Result{}, &RoverError{fmt.Sprintf("Rover did not return JSON (exit %d): %s", exitCode(cmd), truncate(stderr.String(), 400))} - } - return Result{ExitCode: exitCode(cmd), Data: data}, nil -} - -func exitCode(cmd *exec.Cmd) int { - if cmd.ProcessState == nil { - return -1 - } - return cmd.ProcessState.ExitCode() -} - -func truncate(s string, n int) string { - if len(s) > n { - return s[:n] - } - return s -} - -func (r *Rover) Inspect(repo string, opts ...InspectOption) (Result, error) { - c := inspectConfig{base: "HEAD"} - for _, o := range opts { - o(&c) - } - args := []string{"inspect", "--repo", repo, "--base", c.base} - if c.worktree { - args = append(args, "--worktree") - } - return r.Call(args) -} - -type inspectConfig struct { - base string - worktree bool -} -type InspectOption func(*inspectConfig) - -func WithBase(base string) InspectOption { return func(c *inspectConfig) { c.base = base } } -func WithWorktree(v bool) InspectOption { return func(c *inspectConfig) { c.worktree = v } } -func (r *Rover) Verify(repo string, opts ...VerifyOption) (Result, error) { - c := verifyConfig{base: "HEAD"} - for _, o := range opts { - o(&c) - } - args := []string{"verify", "--repo", repo, "--base", c.base} - if c.allowLocal { - args = append(args, "--allow-local") - } - if c.worktree { - args = append(args, "--worktree") - } - return r.Call(args) -} - -type verifyConfig struct { - base string - allowLocal bool - worktree bool -} -type VerifyOption func(*verifyConfig) - -func WithVerifyBase(base string) VerifyOption { return func(c *verifyConfig) { c.base = base } } -func WithAllowLocal(v bool) VerifyOption { return func(c *verifyConfig) { c.allowLocal = v } } -func WithVerifyWorktree(v bool) VerifyOption { return func(c *verifyConfig) { c.worktree = v } } - -func (r *Rover) RunTask(taskFile string, opts ...RunTaskOption) (Result, error) { - c := runTaskConfig{} - for _, o := range opts { - o(&c) - } - args := []string{"task", "run", "--file", taskFile} - if c.allowLocal { - args = append(args, "--allow-local") - } - if c.key != "" { - args = append(args, "--key", c.key) - } - return r.Call(args) -} - -type runTaskConfig struct { - allowLocal bool - key string -} -type RunTaskOption func(*runTaskConfig) - -func WithRunAllowLocal(v bool) RunTaskOption { return func(c *runTaskConfig) { c.allowLocal = v } } -func WithKey(key string) RunTaskOption { return func(c *runTaskConfig) { c.key = key } } - -func (r *Rover) Status(taskID ...string) (Result, error) { - if len(taskID) > 0 && taskID[0] != "" { - return r.Call([]string{"status", "--id", taskID[0]}) - } - return r.Call([]string{"status"}) -} - -func (r *Rover) Report(investigationID string) (Result, error) { - return r.Call([]string{"report", "--id", investigationID}) -} diff --git a/sdk/go/rover_client_test.go b/sdk/go/rover_client_test.go deleted file mode 100644 index 54fd87d..0000000 --- a/sdk/go/rover_client_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package rover - -import ( - "encoding/json" - "os" - "path/filepath" - "testing" -) - -func TestArgvAndFailedDecisionAreNotHidden(t *testing.T) { - dir := t.TempDir() - fixture := filepath.Join(dir, "fixture") - script := "#!/usr/bin/env python3\nimport json,sys\nprint(json.dumps({\"decision\":\"BLOCKED\",\"argv\":sys.argv[1:]}))\n" - if err := os.WriteFile(fixture, []byte(script), 0700); err != nil { - t.Fatal(err) - } - client, err := New(fixture, filepath.Join(dir, "state")) - if err != nil { - t.Fatal(err) - } - r, err := client.Call([]string{"inspect", "--repo", "space ; $(not-a-shell)"}) - if err != nil { - t.Fatal(err) - } - if r.ExitCode != 0 { - t.Fatalf("exit %d", r.ExitCode) - } - if r.Decision() != "BLOCKED" { - t.Fatal(r.Data) - } - b, _ := json.Marshal(r.Data) - if !contains(string(b), "space ; $(not-a-shell)") { - t.Fatal(string(b)) - } -} - -func TestNonJSONRejected(t *testing.T) { - dir := t.TempDir() - p := filepath.Join(dir, "fixture") - if err := os.WriteFile(p, []byte("#!/bin/sh\nprintf not-json\n"), 0700); err != nil { - t.Fatal(err) - } - client, _ := New(p, filepath.Join(dir, "state")) - if _, err := client.Call([]string{"status"}); err == nil { - t.Fatal("non-JSON accepted") - } -} - -func contains(s, sub string) bool { - return len(s) >= len(sub) && (func() bool { - for i := 0; i <= len(s)-len(sub); i++ { - if s[i:i+len(sub)] == sub { - return true - } - } - return false - })() -} diff --git a/sdk/python/README.md b/sdk/python/README.md deleted file mode 100644 index a417fbd..0000000 --- a/sdk/python/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Python CLI client - -`rover_client.py` requires only Python's standard library. It invokes your explicitly -selected Rover binary with argv, never a shell. No PyPI package is claimed. - -```python -from rover_client import Rover -client = Rover("/absolute/path/to/rover", "/absolute/path/to/private-state") -result = client.status() -print(result.exit_code, result.data) -``` - -Task submission exit zero means admitted, not accepted software. Verification -codes 1/2/3 remain ordinary structured `Result`s rather than being hidden by an -exception. A client timeout may leave an admitted detached task running. Reconcile -before retrying. External installations and credentials are never automatic. - -Run tests: `python3 -m unittest discover -s sdk/python -p 'test_*.py'`. diff --git a/sdk/python/pyproject.toml b/sdk/python/pyproject.toml deleted file mode 100644 index e2b32d7..0000000 --- a/sdk/python/pyproject.toml +++ /dev/null @@ -1,24 +0,0 @@ -[build-system] -requires = ["setuptools>=61"] -build-backend = "setuptools.build_meta" - -[project] -name = "rover-client" -version = "0.0.1" -description = "Small stdlib client for Rover's CLI — argv only, no shell, bounded JSON" -readme = "README.md" -license = {text = "MIT"} -requires-python = ">=3.9" -authors = [{name = "GrayCodeAI"}] -keywords = ["rover", "agent-orchestration", "verification", "mcp"] -classifiers = [ - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", -] - -[project.urls] -Homepage = "https://github.com/GrayCodeAI/rover" -Repository = "https://github.com/GrayCodeAI/rover" - -[tool.setuptools] -py-modules = ["rover_client"] diff --git a/sdk/python/rover_client.py b/sdk/python/rover_client.py deleted file mode 100644 index 1b6d1d6..0000000 --- a/sdk/python/rover_client.py +++ /dev/null @@ -1,72 +0,0 @@ -"""Small standard-library client for Rover's CLI (v0.x API). -No shell, automatic retries, provider credentials, or acceptance inference. -A CLI submission may continue after a client timeout; reconcile before retrying. -""" -from __future__ import annotations -from dataclasses import dataclass -import json -import os -from pathlib import Path -import subprocess -import tempfile -from typing import Any, Sequence - -class RoverError(RuntimeError): - """Protocol, invocation or bounded-output failure.""" - -@dataclass(frozen=True) -class Result: - exit_code: int - data: Any - - @property - def decision(self) -> str | None: - return self.data.get("decision") if isinstance(self.data, dict) else None - -class Rover: - def __init__(self, binary: str | os.PathLike[str], state: str | os.PathLike[str], *, timeout: float = 60, max_output: int = 16 << 20): - self.binary = str(Path(binary).resolve()) - self.state = str(Path(state).resolve()) - if timeout <= 0 or max_output < 1024: - raise ValueError("positive timeout and output bound required") - self.timeout = timeout - self.max_output = max_output - - def call(self, args: Sequence[str]) -> Result: - if isinstance(args, str) or any(not isinstance(x, str) for x in args): - raise TypeError("args must be an argv sequence, not a shell string") - argv = [self.binary, "--state", self.state, *args, "--json"] - # Avoid accumulating unbounded pipe output in memory. Rover's own output - # limits remain relevant; operating-system disk quotas are still needed. - with tempfile.TemporaryFile() as stdout, tempfile.TemporaryFile() as stderr: - try: - proc = subprocess.run(argv, stdin=subprocess.DEVNULL, stdout=stdout, stderr=stderr, timeout=self.timeout, check=False) - except subprocess.TimeoutExpired as exc: - raise RoverError("Client timed out. An admitted task may still be running; inspect state before retrying.") from exc - stdout.seek(0); raw = stdout.read(self.max_output + 1) - stderr.seek(0); err = stderr.read(65536).decode("utf-8", "replace") - if len(raw) > self.max_output: - raise RoverError("Rover output exceeded configured client limit") - try: - data = json.loads(raw) - except (ValueError, UnicodeError) as exc: - raise RoverError(f"Rover did not return JSON (exit {proc.returncode}): {err}") from exc - return Result(proc.returncode, data) - - def inspect(self, repo: str, *, base: str = "HEAD", worktree: bool = False) -> Result: - return self.call(["inspect", "--repo", repo, "--base", base] + (["--worktree"] if worktree else [])) - - def verify(self, repo: str, *, base: str = "HEAD", allow_local: bool = False, worktree: bool = False) -> Result: - return self.call(["verify", "--repo", repo, "--base", base] + (["--allow-local"] if allow_local else []) + (["--worktree"] if worktree else [])) - - def run_task(self, task_file: str, *, allow_local: bool = False, key: str | None = None) -> Result: - argv = ["task", "run", "--file", task_file] - if allow_local: argv.append("--allow-local") - if key is not None: argv += ["--key", key] - return self.call(argv) - - def status(self, task_id: str | None = None) -> Result: - return self.call(["status"] + (["--id", task_id] if task_id else [])) - - def report(self, investigation_id: str) -> Result: - return self.call(["report", "--id", investigation_id]) diff --git a/sdk/python/test_client.py b/sdk/python/test_client.py deleted file mode 100644 index b3c08e5..0000000 --- a/sdk/python/test_client.py +++ /dev/null @@ -1,23 +0,0 @@ -import json -from pathlib import Path -import tempfile -import unittest -from rover_client import Rover, RoverError - -class ClientTests(unittest.TestCase): - def test_argv_and_failed_decision_are_not_hidden(self): - with tempfile.TemporaryDirectory() as d: - p = Path(d) / 'fixture' - p.write_text('#!/usr/bin/env python3\nimport json,sys\nprint(json.dumps({"decision":"BLOCKED","argv":sys.argv[1:]}))\nsys.exit(1)\n') - p.chmod(0o700) - r = Rover(p, Path(d)/'state').call(['inspect', '--repo', 'space ; $(not-a-shell)']) - self.assertEqual(r.exit_code, 1) - self.assertEqual(r.decision, 'BLOCKED') - self.assertIn('space ; $(not-a-shell)', r.data['argv']) - def test_non_json_rejected(self): - with tempfile.TemporaryDirectory() as d: - p=Path(d)/'fixture';p.write_text('#!/bin/sh\nprintf not-json\n');p.chmod(0o700) - with self.assertRaises(RoverError): Rover(p,Path(d)/'state').call(['status']) - def test_no_shell_strings(self): - with self.assertRaises(TypeError): Rover('/missing','/tmp/missing-state').call('status; echo bad') -if __name__=='__main__':unittest.main() diff --git a/sdk/typescript/README.md b/sdk/typescript/README.md deleted file mode 100644 index c2b23a1..0000000 --- a/sdk/typescript/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# TypeScript / Node CLI client - -`src/rover_client.ts` requires only Node 18+ built-ins. It invokes your explicitly -selected Rover binary with argv, never a shell. No npm credential or provider magic. - -```ts -import { Rover } from "./src/rover_client.ts"; -const client = new Rover("/absolute/path/to/rover", "/absolute/path/to/private-state"); -const result = client.status(); -console.log(result.exitCode, result.data); -``` - -Task submission exit zero means admitted, not accepted software. Verification -codes 1/2/3 remain ordinary structured `Result`s rather than being hidden by an -exception. A client timeout may leave an admitted detached task running. Reconcile -before retrying. External installations and credentials are never automatic. - -Run tests: `npm test` (uses `node --test`). - -Mirrors `sdk/python/rover_client.py` — same argv surface, bounds, and error semantics. diff --git a/sdk/typescript/package.json b/sdk/typescript/package.json deleted file mode 100644 index b52a556..0000000 --- a/sdk/typescript/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "@graycodeai/rover", - "version": "0.0.1", - "description": "Small Node client for Rover's CLI — argv only, no shell, bounded JSON", - "type": "module", - "main": "src/rover_client.ts", - "types": "src/rover_client.ts", - "license": "MIT", - "repository": { "type": "git", "url": "https://github.com/GrayCodeAI/rover.git", "directory": "sdk/typescript" }, - "engines": { "node": ">=18" }, - "scripts": { - "test": "node --test test/*.test.ts", - "check": "tsc --noEmit" - }, - "keywords": ["rover", "agent-orchestration", "verification", "mcp"] -} diff --git a/sdk/typescript/src/rover_client.ts b/sdk/typescript/src/rover_client.ts deleted file mode 100644 index 6c43276..0000000 --- a/sdk/typescript/src/rover_client.ts +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Small Node client for Rover's CLI (v0.x API). - * No shell, automatic retries, provider credentials, or acceptance inference. - * A CLI submission may continue after a client timeout; reconcile before retrying. - */ -import { spawnSync } from "node:child_process"; -import * as path from "node:path"; - -export class RoverError extends Error { - override name = "RoverError"; -} - -export interface Result { - exitCode: number; - data: unknown; - /** convenience when data is {decision: string} */ - get decision(): string | null; -} - -class ResultImpl implements Result { - exitCode: number; - data: unknown; - constructor(exitCode: number, data: unknown) { - this.exitCode = exitCode; - this.data = data; - } - get decision(): string | null { - if (dataIsRecord(this.data) && typeof this.data["decision"] === "string") return this.data["decision"] as string; - return null; - } -} - -function dataIsRecord(v: unknown): v is Record { - return typeof v === "object" && v !== null; -} - -export class Rover { - readonly binary: string; - readonly state: string; - readonly timeoutMs: number; - readonly maxOutput: number; - - constructor(binary: string, state: string, opts: { timeoutMs?: number; maxOutput?: number } = {}) { - this.binary = path.resolve(binary); - this.state = path.resolve(state); - this.timeoutMs = opts.timeoutMs ?? 60_000; - this.maxOutput = opts.maxOutput ?? 16 * 1024 * 1024; - if (this.timeoutMs <= 0 || this.maxOutput < 1024) throw new RangeError("positive timeout and output bound required"); - } - - call(args: string[]): Result { - if (typeof args === "string" || !Array.isArray(args) || args.some((x) => typeof x !== "string")) { - throw new TypeError("args must be an argv string[] , not a shell string"); - } - const argv = [this.binary, "--state", this.state, ...args, "--json"]; - // spawnSync avoids shell; timeout is OS-level. Rover's own output limits remain relevant. - const r = spawnSync(argv[0], argv.slice(1), { - input: undefined, - timeout: this.timeoutMs, - maxBuffer: this.maxOutput + 1024, - encoding: "buffer", - } as const); - - if (r.error) { - const e = r.error as NodeJS.ErrnoException; - if (e.code === "ETIMEDOUT" || r.signal === "SIGTERM") { - throw new RoverError("Client timed out. An admitted task may still be running; inspect state before retrying."); - } - throw new RoverError(String(e.message)); - } - - const stdout = r.stdout as Buffer | null; - const stderr = r.stderr as Buffer | null; - const raw = stdout ?? Buffer.alloc(0); - const errText = (stderr ?? Buffer.alloc(0)).toString("utf8", 0, 65536).replace(/\0/g, ""); - - if (raw.length > this.maxOutput) throw new RoverError("Rover output exceeded configured client limit"); - - let data: unknown; - try { - data = JSON.parse(raw.toString("utf8")); - } catch (e) { - throw new RoverError(`Rover did not return JSON (exit ${r.status ?? -1}): ${errText.slice(0, 400)}`); - } - return new ResultImpl(r.status ?? 0, data); - } - - inspect(repo: string, opts: { base?: string; worktree?: boolean } = {}): Result { - const a = ["inspect", "--repo", repo, "--base", opts.base ?? "HEAD"]; - if (opts.worktree) a.push("--worktree"); - return this.call(a); - } - - verify(repo: string, opts: { base?: string; allowLocal?: boolean; worktree?: boolean } = {}): Result { - const a = ["verify", "--repo", repo, "--base", opts.base ?? "HEAD"]; - if (opts.allowLocal) a.push("--allow-local"); - if (opts.worktree) a.push("--worktree"); - return this.call(a); - } - - runTask(taskFile: string, opts: { allowLocal?: boolean; key?: string } = {}): Result { - const a = ["task", "run", "--file", taskFile]; - if (opts.allowLocal) a.push("--allow-local"); - if (opts.key) a.push("--key", opts.key); - return this.call(a); - } - - status(taskId?: string): Result { - return this.call(taskId ? ["status", "--id", taskId] : ["status"]); - } - - report(investigationId: string): Result { - return this.call(["report", "--id", investigationId]); - } -} diff --git a/sdk/typescript/test/client.test.ts b/sdk/typescript/test/client.test.ts deleted file mode 100644 index 1e8d391..0000000 --- a/sdk/typescript/test/client.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { describe, it } from "node:test"; -import assert from "node:assert/strict"; -import * as fs from "node:fs"; -import * as os from "node:os"; -import * as path from "node:path"; -import { Rover, RoverError } from "../src/rover_client.ts"; - -describe("Rover client", () => { - it("argv and failed decision are not hidden", () => { - const d = fs.mkdtempSync(path.join(os.tmpdir(), "rover-ts-")); - const fixture = path.join(d, "fixture"); - fs.writeFileSync( - fixture, - '#!/usr/bin/env python3\nimport json,sys\nprint(json.dumps({"decision":"BLOCKED","argv":sys.argv[1:]}))\n', - { mode: 0o700 }, - ); - const r = new Rover(fixture, path.join(d, "state")).call(["inspect", "--repo", "space ; $(not-a-shell)"]); - assert.equal(r.exitCode, 0); - assert.equal(r.decision, "BLOCKED"); - assert.ok(String(JSON.stringify((r.data as any).argv)).includes("space ; $(not-a-shell)")); - }); - - it("non-JSON is rejected", () => { - const d = fs.mkdtempSync(path.join(os.tmpdir(), "rover-ts-")); - const p = path.join(d, "fixture"); - fs.writeFileSync(p, "#!/bin/sh\nprintf not-json\n", { mode: 0o700 }); - assert.throws(() => new Rover(p, path.join(d, "state")).call(["status"]), RoverError); - }); - - it("no shell strings", () => { - assert.throws(() => new Rover("/missing", "/tmp/missing-state").call("status; echo bad" as unknown as string[]), TypeError); - }); -}); From 9a68e647d4d45cb6d48fdf6336e03c44219cbd02 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:42:57 +0530 Subject: [PATCH 06/23] docs: describe the Rust port preview in README, STATUS and security model Record what the Rust workspace implements so far (store, importer, source capture, execution, PTY sessions and the attached TUI), the Rust TUI file browser/editor, saved-command and briefing-draft trust boundaries in the security model, and the Unreleased rover-execution changes in the changelog. Go remains the authoritative product. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 14 ++++++ README.md | 104 +++++++++++++++++++++++++++++++++++++-- STATUS.md | 109 ++++++++++++++++++++++++++++++++++++++++- docs/SECURITY_MODEL.md | 27 ++++++++++ 4 files changed, 250 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 449da36..8fd1ab9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ - Remove the Python, TypeScript, and Go SDK source trees and their CI/Make targets per product direction. Historical validation records remain dated. +- `ProcessGroupIdentity` gains a `conflicting` field listing every equally + ranked candidate when the group is ambiguous (additive; empty otherwise). The + TUI now emits one `foreground_process` evidence entry per conflicting + candidate instead of a single generic message, so an operator can see which + agents disagreed rather than only that they did. Identity selection is + unchanged: an ambiguous group still resolves to unknown. +- `rover-execution`: `parse_nul_terminated_argv` and the `MAX_PROCESS_ARG_*` + bounds are now `cfg(target_os = "linux")`. They only ever served the Linux + `/proc` reader — the macOS backend deliberately reports unknown argv rather + than parse a lossy `ps` command line — so the wider gate left them dead on + macOS. +- `rover-execution`: reject a negative pid parsed out of `ps` output instead of + casting it to `u32`, and drop the unused `&self` from + `foreground_process_group_details_for`. ## 0.0.1 diff --git a/README.md b/README.md index d3176f1..fb13c16 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,105 @@ **Terminal-first agent workspaces, persistent sessions, parallel workflows, verification, review, and evidence.** -`0.0.1` is the initial public OSS release — terminal-first, agent-neutral, monorepo (Go + Python + TypeScript + Go SDKs). See [STATUS.md](STATUS.md) for explicit boundaries. +`0.0.1` is the initial public OSS release — terminal-first and agent-neutral. See [STATUS.md](STATUS.md) for explicit boundaries. + +The Rust migration is in progress. The current published CLI and TUI remain Go; +the Rust workspace now contains core model and repository identity primitives, +versioned SQLite migrations, transactional record/event/idempotency APIs, +project-scoped grant services, and a Unix state-root and blob/file layer. It is +not yet a product replacement. Store-level agent/resource reservations, +local budget accounting, read-only legacy-state inventory, and an opt-in Go +state importer are also ported. The importer preserves the source tree, requires +explicit operator assertions, excludes runtime task/check trees, and sanitizes +grants and execution process state. Repeat imports verify copied rows, events, +request keys, receipt counts, and object digests. See the +[migration and rollback guide](docs/design/GO_RUST_STATE_MIGRATION.md). +The `rover-source` crate captures committed Git blobs and working-tree bytes +with explicit consistency labels; persistence currently uses the Unix +state-root adapter. The `rover-execution` crate has a tested Unix runner for +argv-only commands, scrubbed environments, bounded live output logs, timeouts, +and process-group cancellation. A Rust `rover tui` executable now provides an +initial Unix path that starts or reattaches to a project-scoped login shell, +streams its terminal output through the bounded screen model, and saves layout +state. Session owner crash recovery, broader CLI behavior, and native runtime +validation on Linux and Windows remain open. A separate `rover-tui` library +renders validated workspace layouts and handles basic +tab/pane keys, and can display filtered text supplied by Rover's terminal +screen model. On Unix it can attach through a caller-supplied session client, +stream output through a bounded queue, send child input/resize events, and +detach with Ctrl-]. Workspace shortcuts use Ctrl-B as a prefix while attached. +Press `?` for keyboard help in the standalone workspace, or Ctrl-B then `?` +while attached; the overlay keeps keys such as `q` from reaching the shell. +Ctrl-B then `t` opens a read-only snapshot of this project's stored tasks; +use the arrow keys to browse, Enter to inspect recorded task/process metadata, +`r` refreshes the project task snapshot while preserving the selected task when +possible. `c` requests task cancellation after a `y` confirmation; it does not +undo prior external actions. `o` or `e` opens the selected task's stdout or +stderr after checking its bytes against the recorded SHA-256. Output is +control-character sanitized and clipped to a 64 KiB display window; use the +arrow keys or Page Up/Page Down to scroll, and Esc to return to task details. +When a task links an investigation, `i` opens its stored decision, checks, +unknowns, and findings. This view labels the material as recorded assessment +evidence; it does not independently verify or rerun those checks. The loader +rejects evidence whose repository or candidate does not match the selected +task. +In task detail, `d` displays the bounded list of added, changed, and deleted +paths between the task's verified base and candidate snapshots. The full +applicable patch remains available through `rover diff`. +In the task list, `/` opens a case-insensitive search over task ID, status, +objective, candidate, and error text; Enter applies it and Esc discards the +draft. `s` cycles deterministic Updated, Status, and Objective ordering. Rover +saves the query and ordering per canonical repository in its Rust state store. +Esc or `t` closes the list. +Ctrl-B then `p` opens a searchable command palette with task, tab, help, and +file-browser actions supported by the current TUI provider. +When this project pane has an explicitly bound Codex or Claude session, +Ctrl-B then `a` shows the exact saved identity. Press `r` to review its native +interactive resume command, then `y` to send it to the attached project shell; +`d` removes Rover's binding after confirmation without stopping a running +agent. Resume uses the provider's configured permissions and sandbox; Rover +does not mediate them. +For scripts, `rover agent session bind --repo PATH --pane active --adapter codex-exec +--id EXACT_ID` records a user-selected identity for a pane in the saved project +workspace (`--pane ID` selects another saved pane). `rover agent session list --repo PATH` lists bindings; +`rover agent session status --repo PATH --pane ID` inspects one; and +`rover agent session clear --repo PATH --pane ID` removes Rover's binding only. +List and status distinguish a saved identity from live agent status, which +remains unknown until Rover has process or lifecycle evidence. Use +`--adapter claude-print` for a Claude session. +Ctrl-B then `n` opens repository-scoped local notes for reusable prompt/context +snippets. Press `n` to create, Enter to edit, or `d` to delete after confirmation; +the first line labels each snippet. In the editor, Left/Right, Home/End, +Backspace, and Delete move or change text; Ctrl-S saves and Esc asks before +discarding edits. Rover stores at most 32 notes, 32 KiB each and 128 KiB total. +Notes remain local and are not submitted to an agent automatically. +Ctrl-B then `d` opens local task briefing drafts. `n` creates a draft, Enter edits, +and `d` deletes after confirmation. Drafts hold a title, path globs, dependencies, +quality-gate text, and multiline prompt; Ctrl-S saves and Esc confirms before +discarding edits. Up to 32 drafts are stored per repository (120-byte title, +8 KiB paths, 4 KiB dependencies, 1 KiB gate, and 16 KiB prompt). Drafts are not +tasks, path globs and gate text are not interpreted, and nothing is sent to a worker. +Ctrl-B then `k` opens repository-scoped saved shell commands. Use `n` to create, +Enter to edit, `d` to delete after confirmation, and `r` to review a command +before sending it once to the attached shell. Commands are single lines, limited +to 256 bytes, and run with Rover's OS-user authority; they are not sandboxed. +Ctrl-B then `f` opens the repository file tree; arrows move, Enter +opens directories or a file preview, Backspace goes to the parent, `h` toggles +hidden entries, and `r` refreshes. Git changes receive status labels and colors. +Ctrl-B then `.` opens quick open, which searches hidden repository files too. +Previews refuse symlinks and special files, sanitize terminal controls, and show +at most 256 KiB. Press `e` on a text preview to edit files up to 8 MiB; Ctrl-S +saves with an atomic replacement and stale-content check, Ctrl-D shows the +unsaved unified diff with three context lines. Ctrl-E opens a private working +copy in the executable named by `VISUAL` or `EDITOR` (default `vi`), and Esc +prompts before discarding edits. +The editor setting is one executable path, invoked without a shell; its changes +return to the TUI buffer and still require Ctrl-S to save. `.git` metadata is +excluded from both browsers. +This initial executable path is not a product replacement; Go remains +authoritative until the Rust parity gates pass. +Track the staged port in [the Rust parity +plan](docs/design/RUST_PARITY_PLAN.md). ## Run the complete demonstrations @@ -26,7 +124,7 @@ context, reversible agent instructions, MCP, remote CLI control, revocation, backup/restore, and signed evidence. No automatic GitHub push, merge, or deployment. Build prerequisites: **Linux, Git, Go 1.26.6 or newer, a C compiler and system SQLite development -headers/library**. Python 3 runs the demonstrations and optional SDK. There are no +headers/library**. Python 3 runs the deterministic demonstrations. There are no external Go modules. This release still uses cgo/system SQLite, not a dependency-free static binary. The current release gate requires the patched Go 1.26.6 line; validate with a supported toolchain. @@ -186,11 +284,11 @@ is claimed. The public naming collision noted in the design remains unresolved. ## Documentation - [Implementation status](STATUS.md) +- [Rust parity plan](docs/design/RUST_PARITY_PLAN.md) · [Rust third-party notices](licenses/THIRD_PARTY_NOTICES.md) - [Command reference](docs/CLI.md) - [Architecture](docs/ARCHITECTURE.md) - [Security and limitations](docs/SECURITY_MODEL.md) - [Current validation](docs/validation/v0.2.0/REPORT.md) -- [Python CLI client](sdk/python/README.md) · [TypeScript CLI client](sdk/typescript/README.md) · [Go CLI client](sdk/go/README.md) - [Original ten-layer plan and acceptance inventory](docs/acceptance/) MIT. No telemetry, model training, automatic upload, push, merge, or deployment diff --git a/STATUS.md b/STATUS.md index be8c0a7..ac34661 100644 --- a/STATUS.md +++ b/STATUS.md @@ -4,9 +4,112 @@ This is an expanded local OSS implementation, not completion of every capability the original ten-layer vision. Working code and independently tested boundaries are not interchangeable. See the validation report for exact observed tests. +The Rust port is also in progress. Its workspace provides validated core and +repository identity primitives, versioned SQLite migrations, transactional +record/event/idempotency APIs, project-scoped grant services, and a Unix +state-root/blob/file layer, plus store-level agent and named-resource +reservations, atomic local budget accounting, read-only legacy-state inventory, +and an opt-in Go state importer. The importer retains the source tree, excludes +runtime task/check trees, revokes grants, and sanitizes process state; independent +repeat-import verification checks records, events, request keys, receipt counts, +and content-addressed object digests. See +`docs/design/GO_RUST_STATE_MIGRATION.md` for source-retention and rollback +instructions. `rover-source` now captures committed Git blobs and working-tree +bytes with explicit consistency labels; persistence currently uses the Unix +state-root adapter. The new `rover-execution` crate has a tested Unix +argv-only runner with scrubbed environment, per-stream bounds, live private log +capture, timeout, and process-group cancellation. Its supervisor module now +has transactional task claim, private detached worker launch, a managed +heartbeat loop, cancellation, and conservative stale-process reconciliation; +confirmed `LOST` transitions release that task's leases, while normal task +finalization and client reconnect remain unimplemented. +The new PTY wrapper covers local create/read/write/resize/close on native Unix +and Windows ConPTY backends; native runtime validation currently covers macOS +only. `rover-tui` has a Ratatui workspace renderer, basic tab/pane input, and a +Unix API for attaching a caller-supplied session client. The bridge sends input +and resize events, reads output on a bounded queue, and renders only text from +Rover's terminal emulator. It preserves Ctrl-] detach and Ctrl-B-prefixed +workspace controls. The new `rover-cli` binary provides an initial Unix-only +`rover tui` launch path: it admits a dedicated Rust state root, starts or +reattaches to a project-scoped login shell owned by a detached local process, +persists the workspace layout during attached use, and can display a bounded, +read-only snapshot of project task records from Rover's SQLite store. Ctrl-B +then `t` opens the task board; Enter opens recorded task and process metadata. +Press `r` on the task list or detail to refresh records while preserving the +selected task where possible. Press `c` to request cancellation after an explicit +`y` confirmation; the store rejects terminal tasks and the UI warns that prior +external actions are not undone. Press `o` or `e` in task detail to read the +selected task's stdout or stderr from the blob store; the TUI independently +verifies the full output against the recorded SHA-256, sanitizes controls, and +shows at most a 64 KiB excerpt. Press `i` to inspect the task's linked stored +investigation decision, checks, unknowns, and findings; the view treats these +as recorded evidence and enforces repository and candidate binding. Press `d` +in task detail to inspect a bounded path inventory from verified base and +candidate snapshots; use `rover diff` for the complete applicable patch. +In the task list, `/` starts a case-insensitive search over ID, status, +objective, candidate, and error; Enter applies it and Esc discards it. `s` +cycles Updated, Status, and Objective sorting. Query and order preferences are +stored per repository in Rover's Rust state database. +Ctrl-B then `p` opens a searchable command palette for provider-supported task, +tab, help, and file-browser actions. +If the active pane has a saved Codex or Claude session binding, Ctrl-B then `a` +shows its exact identity. `r` opens a command review and a second `y` confirmation +sends the interactive resume command to the attached shell; `d` removes the +binding after confirmation. Rover leaves permission and sandbox behavior to the +provider. +The CLI supports explicit exact-ID binding to an existing saved workspace pane, +project-scoped list/status, and binding clear with `rover agent session`. +The attached TUI samples the owned PTY's foreground process group and displays +recognized identity while keeping state unknown until manifests or lifecycle +evidence are available. CLI inventory and lifecycle transport remain separate +work; see ADR-0025 and ADR-0026. +The agents crate now contains an independently implemented, bounded Herdr +manifest evaluator and all 22 hash-verified bundled detection TOML files. Its +gate operators, 15 region selectors, rule ordering, OSC fields, visibility +flags, and fallback behavior are covered by Rust tests. The attached TUI feeds +the live viewport, OSC title, and bounded OSC 9;4 progress payloads to the +evaluator for audited direct executable mappings. Lifecycle-authority +integration and wrapper/process-group expansion remain open in +P-062/P-166/P-167; transcript-viewer skip rules preserve the latest same-agent +state in the attached TUI. See ADR-0027. +Ctrl-B then `n` opens a repository-scoped notes list for reusable prompt/context +snippets. `n` creates, Enter edits, and `d` deletes after confirmation; the first +line labels each item. Editing supports cursor movement, bounded UTF-8 text, +explicit Ctrl-S persistence, and an Esc discard prompt. Up to 32 notes (32 KiB +each, 128 KiB total) are stored locally and are not submitted to an agent. +Ctrl-B then `d` opens repository-scoped task briefing drafts with create, edit, +and confirmed delete. Drafts store title, path globs, dependencies, quality-gate +text, and multiline prompt. They save explicitly and are never dispatched or +interpreted; schema v1 records load with defaults and new saves use schema v2. +Limits are 32 drafts, 120 title bytes, 8 KiB paths, 4 KiB dependencies, 1 KiB +gate, and 16 KiB prompt. +Ctrl-B then `k` opens repository-scoped saved shell commands. Create/edit/delete +are explicit; `r` displays the full bounded single-line command and the same-user, +unsandboxed execution warning before `y` sends it once to the attached shell. +Commands are capped at 256 bytes and 32 entries. +Ctrl-B then `f` opens a file tree with Git status labels and colors; +arrows navigate, Enter opens a directory or preview, Backspace goes up, `h` +shows hidden files, and `r` refreshes. Ctrl-B then `.` opens hidden-file quick +open. `.git`, symlinks, and special files are not traversed or previewed; text +previews are sanitized and capped at 256 KiB. Text files up to 8 MiB can be +edited with `e`; Ctrl-S saves atomically after a stale-content check, Ctrl-D +shows a bounded unified diff with three context lines, Ctrl-E opens +`$VISUAL`/`$EDITOR` on a private +working copy, and Esc confirms before discarding dirty edits. External editor +changes return to the buffer and still require Ctrl-S to save. +Session recovery after owner crash/restart and other task actions, +full keyboard/layout interaction, platform parity, and the broader product +CLI remain open; this is an early Rust slice, not a Go runtime replacement. +Non-Unix process execution beyond the PTY wrapper remains unimplemented. Rover +does not yet replace the Go runtime. +Go remains authoritative until the parity tasks and removal gates in +`docs/design/RUST_PARITY_PLAN.md` pass. Python, TypeScript, and Go SDK source +was removed per the user's explicit product-scope decision; dated validation +records below retain the results from when those clients existed. + | Layer | Implemented | Remaining / not validated | |---|---|---| -| L1 interface | CLI/JSON, Linux/macOS keyboard TUI and PTY attach, setup preview, Python client, reversible agent instructions | Desktop/mobile, signed public installer, full platform parity, broad accessibility validation | +| L1 interface | CLI/JSON, Linux/macOS keyboard TUI and PTY attach, setup preview, reversible agent instructions | Desktop/mobile, signed public installer, full platform parity, broad accessibility validation; no SDK interfaces by product decision | | L2 interoperability | Generic headless/PTY (Linux + macOS) adapters; Codex exec and Claude print argument/event adapters; MCP stdio/JSON HTTP; remote CLI client | Live provider certification, App Server/ACP, full MCP transport feature set, plugin marketplace | | L3 runtime | Detached supervisors, terminal broker (Linux + macOS), reconnect, resize/input, cancellation, bounded output, resource admission, repair attempts | Cross-host worker recovery/fencing, native conversation restore, host-loss continuation | | L4 environments | Worktrees, retained exact committed snapshots, disposable checks, explicit overlays, conservative dependency composition, patch export | Atomic mutable-tree capture, symlinks/submodules/LFS, managed development services/ports/databases, live Docker validation | @@ -19,6 +122,10 @@ not interchangeable. See the validation report for exact observed tests. ## Unreleased platform increments on top of `0.0.1` (pending Linux re-verification and review) +The older increment records in this section are historical. SDK test results +refer to the removed Python, TypeScript, and Go clients and are not current +product or CI claims. + These source changes exist in the working tree. They are **not** part of any validated release until they are re-run on owned Linux/amd64 with a supported toolchain and reviewed: diff --git a/docs/SECURITY_MODEL.md b/docs/SECURITY_MODEL.md index c38042d..edd2c66 100644 --- a/docs/SECURITY_MODEL.md +++ b/docs/SECURITY_MODEL.md @@ -28,6 +28,33 @@ on a host holding unrelated credentials or run hostile repositories there. - PTY sockets are same-user and private. Full interactive terminals inherently expose untrusted terminal output; use trusted code. Noninteractive views sanitize control characters; use `diff --output` or JSON for exact raw patch data. +- The Rust TUI repository browser validates directory names against an already-open + repository handle; traversal and preview refuse + symlinks, and the viewer opens regular files with no-follow descriptor-relative + operations, reads at most 256 KiB, and sanitizes terminal controls. `.git` metadata + is excluded. Pressing `e` opens a bounded UTF-8 inline editor; control-character + files are refused, displayed text is sanitized, and Ctrl-S saves through a rooted + atomic replacement that preserves existing permission bits. The saver compares + the current bytes with the editor's starting digest and refuses stale content. + Ctrl-D shows a sanitized three-line-context diff preview; the LCS work is capped + at one million cells and the display at 512 changed lines per side. This is a + review view, not a patch export. + Ctrl-E launches the configured `VISUAL`/`EDITOR` executable (without a shell) on a + 0600 copy in a 0700 temporary directory. Returned bytes are read through the + opened directory handle and return to the TUI buffer; saving to the repository + still requires Ctrl-S and uses the same digest check. + This check is optimistic and cannot prevent an external writer racing between the + final read and rename. The configured editor is trusted code running with Rover's + OS-user authority. Git status labels are local metadata, not a security or + integrity assessment. File operations run with the same OS-user authority as Rover + and do not sandbox code. +- Repository-scoped saved shell commands are bounded printable single lines. The + TUI shows the command and warns that execution is unsandboxed before `y` sends + the line once to the attached PTY. This is ordinary shell input under Rover's + OS-user authority; confirmation does not sandbox, inspect, or restrict effects. +- Task briefing drafts are local data only. The TUI does not submit them to a + worker, interpret path globs, or run gate text; each record is repository-bound + and capped by count and UTF-8 byte size. ## Evidence limitations From 36acb828a861401d9850ee407314cc34d6ff609a Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:44:20 +0530 Subject: [PATCH 07/23] fix(rust): name the preview binary rover-rs so it cannot shadow rover Both the Go product and the Rust preview built an executable called `rover` with different `tui` keymaps and state roots, so a `cargo install --path crates/rover-cli` or a stray PATH entry silently replaced the product binary and bug reports could not tell them apart. The Rust binary is now `rover-rs`; its usage and error prefix say so, and `rover-rs --version` prints the crate version labelled as an unreleased Rust port preview that is not the Rover product binary. The parity plan records that the `rover` name moves only at cutover (P-164). tests/cli_identity.rs covers the version banner and usage text. Refs: F067 Co-Authored-By: Claude Opus 5.5 --- crates/rover-cli/Cargo.toml | 4 ++- crates/rover-cli/src/main.rs | 23 ++++++++++++++--- crates/rover-cli/tests/cli_identity.rs | 33 ++++++++++++++++++++++++ crates/rover-cli/tests/session_daemon.rs | 2 +- docs/design/RUST_PARITY_PLAN.md | 2 +- 5 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 crates/rover-cli/tests/cli_identity.rs diff --git a/crates/rover-cli/Cargo.toml b/crates/rover-cli/Cargo.toml index 19e5193..ed60501 100644 --- a/crates/rover-cli/Cargo.toml +++ b/crates/rover-cli/Cargo.toml @@ -5,8 +5,10 @@ edition.workspace = true license.workspace = true rust-version.workspace = true +# The preview binary must not shadow the Go `rover` product binary on PATH. +# It takes the `rover` name only when the RUST_PARITY_PLAN removal gates pass. [[bin]] -name = "rover" +name = "rover-rs" path = "src/main.rs" [dependencies] diff --git a/crates/rover-cli/src/main.rs b/crates/rover-cli/src/main.rs index d018715..61f6ffc 100644 --- a/crates/rover-cli/src/main.rs +++ b/crates/rover-cli/src/main.rs @@ -43,9 +43,22 @@ use rover_tui::{ MAX_WORKSPACE_NOTES_COUNT, MAX_WORKSPACE_NOTES_TOTAL_BYTES, }; +/// Name of the Rust preview executable. It deliberately differs from the Go +/// `rover` product binary so the two cannot shadow each other on `PATH`. +const BINARY_NAME: &str = "rover-rs"; + +const USAGE: &str = "usage: rover-rs tui [--state PATH] [--repo PATH] | rover-rs agents [--json] | rover-rs agent list | rover-rs agent capabilities | rover-rs agent session ... (run `rover-rs agent session` for its options) | rover-rs --version"; + +fn version_banner() -> String { + format!( + "{BINARY_NAME} {} (Rust port preview; unreleased and not the Rover product binary, which is the Go `rover`)", + env!("CARGO_PKG_VERSION") + ) +} + fn main() { if let Err(error) = run() { - eprintln!("rover: {error}"); + eprintln!("{BINARY_NAME}: {error}"); std::process::exit(1); } } @@ -57,13 +70,17 @@ fn run() -> io::Result<()> { .and_then(|arg| arg.into_string().ok()) .as_deref() { + Some("--version" | "version") => { + println!("{}", version_banner()); + Ok(()) + } Some("tui") => run_tui(args.collect()), Some("agents") => run_agent_command("agents", args.collect()), Some("agent") => run_agent_command("agent", args.collect()), #[cfg(unix)] Some("__session_daemon") => run_daemon(args.collect()), _ => { - eprintln!("usage: rover tui [--state PATH] [--repo PATH] | rover agents [--json] | rover agent list | rover agent capabilities | rover agent session list --repo PATH [--state PATH] | rover agent session status --repo PATH --pane ID [--state PATH]"); + eprintln!("{USAGE}"); Err(io::Error::new( io::ErrorKind::InvalidInput, "unknown command", @@ -1688,7 +1705,7 @@ fn required_string(values: &BTreeMap, key: &str) -> fn run_tui(_args: Vec) -> io::Result<()> { Err(io::Error::new( io::ErrorKind::Unsupported, - "rover tui session hosting currently requires Unix", + "rover-rs tui session hosting currently requires Unix", )) } diff --git a/crates/rover-cli/tests/cli_identity.rs b/crates/rover-cli/tests/cli_identity.rs new file mode 100644 index 0000000..5a68448 --- /dev/null +++ b/crates/rover-cli/tests/cli_identity.rs @@ -0,0 +1,33 @@ +//! The Rust preview binary must identify itself as a preview and must never +//! present itself as the Go `rover` product binary. + +use std::process::Command; + +fn preview() -> Command { + Command::new(env!("CARGO_BIN_EXE_rover-rs")) +} + +#[test] +fn version_identifies_the_unreleased_rust_preview() { + for flag in ["--version", "version"] { + let output = preview().arg(flag).output().expect("run rover-rs"); + assert!(output.status.success(), "{flag} exited {:?}", output.status); + let stdout = String::from_utf8(output.stdout).expect("UTF-8 version output"); + assert!( + stdout.starts_with(&format!("rover-rs {} ", env!("CARGO_PKG_VERSION"))), + "{stdout}" + ); + assert!(stdout.contains("Rust port preview"), "{stdout}"); + assert!(stdout.contains("not the Rover product binary"), "{stdout}"); + } +} + +#[test] +fn usage_and_errors_name_the_preview_binary() { + let output = preview().output().expect("run rover-rs"); + assert!(!output.status.success()); + let stderr = String::from_utf8(output.stderr).expect("UTF-8 usage output"); + assert!(stderr.starts_with("usage: rover-rs tui "), "{stderr}"); + assert!(stderr.contains("rover-rs: unknown command"), "{stderr}"); + assert!(!stderr.contains("usage: rover tui"), "{stderr}"); +} diff --git a/crates/rover-cli/tests/session_daemon.rs b/crates/rover-cli/tests/session_daemon.rs index 4dbee5d..6cc9d5c 100644 --- a/crates/rover-cli/tests/session_daemon.rs +++ b/crates/rover-cli/tests/session_daemon.rs @@ -40,7 +40,7 @@ fn cli_session_daemon_accepts_named_client_and_exits_with_shell() { .expect("make controlled shell executable"); let key = SessionKey::new("integration_project", "shell").expect("valid session key"); - let mut daemon = Command::new(env!("CARGO_BIN_EXE_rover")) + let mut daemon = Command::new(env!("CARGO_BIN_EXE_rover-rs")) .args([ "__session_daemon", "--sessions", diff --git a/docs/design/RUST_PARITY_PLAN.md b/docs/design/RUST_PARITY_PLAN.md index 6b94175..0509856 100644 --- a/docs/design/RUST_PARITY_PLAN.md +++ b/docs/design/RUST_PARITY_PLAN.md @@ -254,7 +254,7 @@ Gate: every additional current-documentation feature row has a per-feature accep - [ ] P-161 Independently review process, PTY, remote, plugin, browser, crypto, and storage code. - [ ] P-162 Audit source-copy provenance, license notices, third-party assets, and SBOM. - [ ] P-163 Complete Go-to-Rust data migration/rollback rehearsal on copied state. -- [ ] P-164 Make final cutover only after Rust parity gates pass; do not auto-delete Go first. +- [ ] P-164 Make final cutover only after Rust parity gates pass; do not auto-delete Go first. (Until cutover the Rust preview executable is `rover-rs`, built with `cargo build -p rover-cli`, so it cannot shadow the Go `rover` product binary on `PATH`; `rover-rs --version` labels itself as an unreleased preview. Cutover renames it to `rover`.) - [ ] P-165 Publish migration/release notes listing tested features, partials, and remaining limits. - [ ] P-166 Wire visible terminal-screen snapshots and OSC title/progress events from the TUI to the bounded Herdr evaluator; map its identity/state/visibility/skip result into Rover evidence and lifecycle authority without treating scrollback as live evidence. (In progress: terminal and TUI pass only the live viewport, bounded OSC title, and OSC 9;4 progress payloads across fragmented PTY chunks and BEL/ST terminators to all 22 audited direct executable mappings. Evidence is retained; same-agent transcript-viewer skip rules preserve the previous status and do not accumulate duplicate evidence, while an identity change resets to the new result. Lifecycle-source transport and visibility application remain open.) - [ ] P-167 Port platform-owned foreground process-group enumeration and wrapper argv recognition for Linux/macOS; preserve bounded sampling, PID reuse protection, explicit Letta filtering, and unknown results where attribution is ambiguous. (In progress: the owned PTY now returns bounded, PID-stability-checked executable basenames from its foreground group on Linux and macOS through an additive session-frame field, and the TUI checks recognized group members after the leader while preserving leader priority and returning unknown for conflicting member identities. PTY/runtime and TUI tests pass on the current macOS host. Wrapper argv parsing, Letta-specific filtering, and Linux runtime verification remain open.) From 4537e0253f12c0f75344043e5bcbde32c93d1f08 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:44:52 +0530 Subject: [PATCH 08/23] fix(rust): run the dependency audit with the CARGO the Makefile uses scripts/rust_dependency_audit.py invoked a literal `cargo`, so `make rust-check CARGO='cargo +1.88.0'` applied the pinned toolchain only to fmt/clippy/test and the audit relied on rust-toolchain.toml happening to resolve `cargo`. The script now splits the CARGO environment variable (default `cargo`), rejects an empty or malformed value, and the Makefile passes CARGO to the check, SBOM and notice-refresh invocations. Refs: F073 Co-Authored-By: Claude Opus 5.5 --- Makefile | 8 +++++--- scripts/rust_dependency_audit.py | 22 ++++++++++++++++++++-- scripts/test_rust_dependency_audit.py | 17 +++++++++++++++++ 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 8150588..d3fa899 100644 --- a/Makefile +++ b/Makefile @@ -54,18 +54,20 @@ rust-clippy: rust-test: $(CARGO) test --workspace --locked --offline +# The audit script runs `cargo metadata`/`cargo tree`; pass CARGO through so a +# `make rust-check CARGO='cargo +1.88.0'` audit uses the same toolchain. rust-deps-check: $(PYTHON) -m unittest discover -s scripts -p 'test_*.py' - $(PYTHON) scripts/rust_dependency_audit.py --check + CARGO='$(CARGO)' $(PYTHON) scripts/rust_dependency_audit.py --check rust-check: rust-fmt-check rust-clippy rust-test rust-deps-check rust-sbom: mkdir -p bin - $(PYTHON) scripts/rust_dependency_audit.py --sbom bin/rover-rust-sbom.cdx.json + CARGO='$(CARGO)' $(PYTHON) scripts/rust_dependency_audit.py --sbom bin/rover-rust-sbom.cdx.json rust-notices: - $(PYTHON) scripts/rust_dependency_audit.py --refresh-notices + CARGO='$(CARGO)' $(PYTHON) scripts/rust_dependency_audit.py --refresh-notices demo: build $(PYTHON) scripts/demo.py --binary bin/rover diff --git a/scripts/rust_dependency_audit.py b/scripts/rust_dependency_audit.py index a17ccee..c499da6 100644 --- a/scripts/rust_dependency_audit.py +++ b/scripts/rust_dependency_audit.py @@ -6,8 +6,10 @@ import argparse import hashlib import json +import os import pathlib import re +import shlex import subprocess import sys import tomllib @@ -80,6 +82,22 @@ class AuditError(Exception): """A lockfile, license, or notice integrity failure.""" +def cargo_command(environ: dict[str, str] | None = None) -> list[str]: + """Return the Cargo invocation, honouring the same CARGO override as the Makefile. + + `make rust-check CARGO='cargo +1.88.0'` must audit with the pinned toolchain + too, not with whatever `cargo` resolves to on PATH. + """ + value = (os.environ if environ is None else environ).get("CARGO", "cargo") + try: + command = shlex.split(value) + except ValueError as error: + raise AuditError(f"CARGO is not a valid command line: {error}") from error + if not command: + raise AuditError("CARGO is set but empty") + return command + + def recognized_license_files(source_dir: pathlib.Path) -> dict[str, str]: """Map stable bundled names to actual source names without case aliases.""" return { @@ -201,7 +219,7 @@ def read_notice_rows(text: str) -> dict[tuple[str, str], tuple[str, list[tuple[s def load_cargo_data(root: pathlib.Path = ROOT) -> tuple[dict, dict]: command = [ - "cargo", + *cargo_command(), "metadata", "--format-version", "1", @@ -402,7 +420,7 @@ def cargo_tree_targets( edges_by_target = {} for target in SUPPORTED_TARGETS: command = [ - "cargo", + *cargo_command(), "tree", "--locked", "--offline", diff --git a/scripts/test_rust_dependency_audit.py b/scripts/test_rust_dependency_audit.py index e5e98ca..9d5beb0 100644 --- a/scripts/test_rust_dependency_audit.py +++ b/scripts/test_rust_dependency_audit.py @@ -6,6 +6,7 @@ from rust_dependency_audit import ( AuditError, canonical_license_expression, + cargo_command, check_notices, generate_sbom, license_source, @@ -60,6 +61,22 @@ def setUp(self): def tearDown(self): self.temp.cleanup() + def test_cargo_command_defaults_to_cargo(self): + self.assertEqual(cargo_command({}), ["cargo"]) + + def test_cargo_command_honours_the_makefile_override(self): + self.assertEqual(cargo_command({"CARGO": "cargo +1.88.0"}), ["cargo", "+1.88.0"]) + self.assertEqual( + cargo_command({"CARGO": "'/opt/rust tools/cargo' +1.88.0"}), + ["/opt/rust tools/cargo", "+1.88.0"], + ) + + def test_cargo_command_rejects_empty_or_malformed_override(self): + with self.assertRaises(AuditError): + cargo_command({"CARGO": " "}) + with self.assertRaises(AuditError): + cargo_command({"CARGO": "'unterminated"}) + def test_purl_encodes_package_identity(self): self.assertEqual(package_purl("sample+crate", "1.2.3"), "pkg:cargo/sample%2Bcrate@1.2.3") From d9c46b90c594062553e452531040c519b36f2fca Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:46:03 +0530 Subject: [PATCH 09/23] ci: run Go product and Rust preview gates as independent cached jobs The Rust fmt/clippy/test/audit steps ran first inside the single 20 minute Go job with no cache, so a cold Rust build (bundled SQLite, alacritty_terminal, ratatui) risked the timeout and any Rust failure hid every Go result. source-validation now has a `go` job with the unchanged Go gates and a separate `rust` job with its own 30 minute limit and a SHA-pinned Swatinem/rust-cache v2.9.2 for ~/.cargo and target/rust-1.88.0 that only main saves. The trailing comment no longer says hosted execution is pending. docs/CI.md documents both jobs, the Rust steps and what each checks, and corrects the package count to 24 (21 with tests). Refs: F069, F072 Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 43 ++++++++++++++++++++++++++++++---------- docs/CI.md | 29 +++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e54bcc4..427c130 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,10 @@ concurrency: group: source-validation-${{ github.ref }} cancel-in-progress: true jobs: - linux: + # The Go binary is the Rover product. Its gates run in their own job so a + # Rust preview failure never hides Go results (and vice versa). + go: + name: go (linux/amd64) runs-on: ubuntu-24.04 timeout-minutes: 20 steps: @@ -22,14 +25,6 @@ jobs: cache: false - name: Build dependencies run: sudo apt-get update && sudo apt-get install -y build-essential libsqlite3-dev python3 - - name: Install pinned Rust MSRV toolchain - run: rustup toolchain install 1.88.0 --profile minimal --component clippy --component rustfmt - - name: Fetch locked Rust dependencies - run: cargo +1.88.0 fetch --locked - - name: Format, lint, test, and audit Rust workspace - run: make rust-check CARGO='cargo +1.88.0' - - name: Generate Rust CycloneDX SBOM - run: make rust-sbom - name: Validate Go toolchain run: make toolchain-check - name: Validate version surfaces @@ -50,5 +45,31 @@ jobs: run: make cross-build - name: Vulnerability scanning run: make vulncheck -# This validates Rover's own source. It is not an independently trusted Rover -# acceptance publisher for arbitrary candidate PRs. Hosted execution is pending. + # Unreleased Rust port preview (crates/, gated by docs/design/RUST_PARITY_PLAN.md). + rust: + name: rust preview (linux/amd64, 1.88.0) + runs-on: ubuntu-24.04 + timeout-minutes: 30 + env: + CARGO_TERM_COLOR: never + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + - name: Install pinned Rust MSRV toolchain + run: rustup toolchain install 1.88.0 --profile minimal --component clippy --component rustfmt + - name: Restore Rust build cache + uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + with: + # .cargo/config.toml sets target-dir = target/rust-1.88.0. + workspaces: '. -> target/rust-1.88.0' + # Only main writes the cache; branches and PRs restore from it. + save-if: ${{ github.ref == 'refs/heads/main' }} + - name: Fetch locked Rust dependencies + run: cargo +1.88.0 fetch --locked + - name: Format, lint, test, and audit Rust workspace + run: make rust-check CARGO='cargo +1.88.0' + - name: Generate Rust CycloneDX SBOM + run: make rust-sbom CARGO='cargo +1.88.0' +# This validates Rover's own source on GitHub-hosted runners. It is not an +# independently trusted Rover acceptance publisher for arbitrary candidate PRs. diff --git a/docs/CI.md b/docs/CI.md index b21adea..abaf63d 100644 --- a/docs/CI.md +++ b/docs/CI.md @@ -2,17 +2,18 @@ ## Source validation — `.github/workflows/ci.yml` -Triggers on `push`, `pull_request`, and `workflow_dispatch`. Runs on -`ubuntu-24.04` with Go 1.26.6 and system SQLite/C toolchain installed. +Triggers on `push`, `pull_request`, and `workflow_dispatch`. Two independent +jobs run on GitHub-hosted `ubuntu-24.04` runners, so a failure in one never +hides the other's results. -### Steps +### `go` job — the Rover product (Go 1.26.6, system SQLite/C toolchain, 20 min limit) | Step | Make target | What it checks | |------|-------------|----------------| | Go toolchain | `make toolchain-check` | Go 1.26.6 or newer | | Version contract | `make version-check` | `VERSION`, runtime, capability, documentation, and release metadata agree; removed SDK tree stays absent | | Source manifest | `make manifest-check` | Every tracked source file matches its generated size and SHA-256 entry | -| Format, vet, tests | `make check` | `gofmt -l`, `go vet`, `go test ./...` (25 packages) | +| Format, vet, tests | `make check` | `gofmt -l`, `go vet`, `go test ./...` (24 packages, 21 with tests) | | Race detector | `make race` | `go test -race ./...` — data race detection | | Fuzz campaigns | `make fuzz` | `FuzzDecode`, `FuzzSafeName`, `FuzzResultParser`, `FuzzTranscript`, `FuzzEnvelope` (3s each) | | Demo smoke | `make demo` | 12 end-to-end CLI scenarios (no model/network) | @@ -20,6 +21,26 @@ Triggers on `push`, `pull_request`, and `workflow_dispatch`. Runs on | Cross-compile | `make cross-build` | linux/amd64 (cgo), darwin/amd64 + darwin/arm64 (cgo-free) | | Vulnerability scan | `make vulncheck` | Pinned `govulncheck` v1.8.0 — scans for known CVEs | +### `rust` job — unreleased Rust port preview (Rust 1.88.0 MSRV, 30 min limit) + +The Rust workspace under `crates/` is a preview gated by +`docs/design/RUST_PARITY_PLAN.md`; it is not part of any release. + +| Step | Command | What it checks | +|------|---------|----------------| +| Toolchain | `rustup toolchain install 1.88.0 --profile minimal --component clippy --component rustfmt` | Pinned MSRV from `rust-toolchain.toml` | +| Build cache | `Swatinem/rust-cache` (SHA-pinned) | Restores `~/.cargo` and `target/rust-1.88.0`, keyed on `Cargo.lock`, `rust-toolchain.toml` and `.cargo/config.toml`; only `main` saves | +| Locked fetch | `cargo +1.88.0 fetch --locked` | `Cargo.lock` is complete; later steps run `--offline` | +| Format | `make rust-fmt-check` (via `rust-check`) | `cargo fmt --all -- --check` | +| Lint | `make rust-clippy` (via `rust-check`) | `clippy --workspace --all-targets --locked --offline -- -D warnings` with the workspace's `clippy::pedantic` and `unsafe_code = "forbid"` lints | +| Tests | `make rust-test` (via `rust-check`) | `cargo test --workspace --locked --offline` | +| Dependency audit | `make rust-deps-check` (via `rust-check`) | `scripts/test_*.py` unit tests, then `scripts/rust_dependency_audit.py --check`: every locked crate's SPDX expression and byte-exact bundled notice (SHA-256) in `licenses/` | +| Rust SBOM | `make rust-sbom` | CycloneDX 1.7 inventory of packages active on the supported targets | + +All Rust steps take `CARGO='cargo +1.88.0'`; the audit script honours the same +`CARGO` value. Cold-cache duration on the hosted runner has not been measured +yet; record it here after the first run on `main`. + ### Cross-compilation matrix | Target | CGO | SQLite | Notes | From e9716db7f8ba8d82e59d0c387b316d6c43bbcd55 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:46:14 +0530 Subject: [PATCH 10/23] ci(rust): keep the generated Rust SBOM as a workflow artifact make rust-sbom wrote bin/rover-rust-sbom.cdx.json and the runner then discarded it, so the step was only a smoke test. Upload it with the SHA-pinned actions/upload-artifact v7.0.1 (fail if missing, 30-day retention). Releases still ship only the Go binaries and their SBOM; the Rust preview is not released, so its SBOM is not a release asset. Refs: F073 Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 7 +++++++ docs/CI.md | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 427c130..4a54388 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,5 +71,12 @@ jobs: run: make rust-check CARGO='cargo +1.88.0' - name: Generate Rust CycloneDX SBOM run: make rust-sbom CARGO='cargo +1.88.0' + - name: Keep the Rust SBOM as a run artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: rover-rust-sbom + path: bin/rover-rust-sbom.cdx.json + if-no-files-found: error + retention-days: 30 # This validates Rover's own source on GitHub-hosted runners. It is not an # independently trusted Rover acceptance publisher for arbitrary candidate PRs. diff --git a/docs/CI.md b/docs/CI.md index abaf63d..fa848f3 100644 --- a/docs/CI.md +++ b/docs/CI.md @@ -35,10 +35,11 @@ The Rust workspace under `crates/` is a preview gated by | Lint | `make rust-clippy` (via `rust-check`) | `clippy --workspace --all-targets --locked --offline -- -D warnings` with the workspace's `clippy::pedantic` and `unsafe_code = "forbid"` lints | | Tests | `make rust-test` (via `rust-check`) | `cargo test --workspace --locked --offline` | | Dependency audit | `make rust-deps-check` (via `rust-check`) | `scripts/test_*.py` unit tests, then `scripts/rust_dependency_audit.py --check`: every locked crate's SPDX expression and byte-exact bundled notice (SHA-256) in `licenses/` | -| Rust SBOM | `make rust-sbom` | CycloneDX 1.7 inventory of packages active on the supported targets | +| Rust SBOM | `make rust-sbom` | CycloneDX 1.7 inventory of packages active on the supported targets, uploaded as the `rover-rust-sbom` run artifact (30-day retention) | All Rust steps take `CARGO='cargo +1.88.0'`; the audit script honours the same -`CARGO` value. Cold-cache duration on the hosted runner has not been measured +`CARGO` value. The Rust SBOM is not attached to releases because releases ship +only the Go binaries. Cold-cache duration on the hosted runner has not been measured yet; record it here after the first run on `main`. ### Cross-compilation matrix From c0b97295144accd134eb08e98ecc0fa69d73da8e Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:46:56 +0530 Subject: [PATCH 11/23] ci(release): attest build provenance for every released asset Releases published sha256 checksums from the same origin as the binaries, which only detects transport corruption: a replaced binary-plus-checksums pair was indistinguishable from a genuine one. The release job now requests id-token/attestations write and runs the SHA-pinned actions/attest-build-provenance v4.2.2 over bin/checksums.txt, so each binary and the SBOM gets a keyless Sigstore-signed provenance statement tied to the workflow run, commit and repository. No long-lived signing key is stored or injected. docs/CI.md documents `gh attestation verify`, and TestReleaseWorkflowAttestsProvenance keeps the step, its permissions and its position between checksums and publishing. Refs: F258 Co-Authored-By: Claude Opus 5.5 --- .github/workflows/release.yml | 11 ++++++++++- docs/CI.md | 18 ++++++++++++++++-- internal/model/version_test.go | 20 ++++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1051690..de767c2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,6 +11,9 @@ on: default: true permissions: contents: write + # Keyless (OIDC) Sigstore signing of build provenance; no long-lived key. + id-token: write + attestations: write concurrency: group: release-${{ github.ref }} cancel-in-progress: false @@ -66,6 +69,10 @@ jobs: run: | cd bin sha256sum rover rover-linux-amd64 rover-darwin-amd64 rover-darwin-arm64 rover-sbom.json > checksums.txt + - name: Attest build provenance for every checksummed asset + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 + with: + subject-checksums: bin/checksums.txt - name: Create release uses: softprops/action-gh-release@8f7ab71908ac24848f30f6066c520d5ddf38a571 # v2 with: @@ -83,4 +90,6 @@ jobs: # This release workflow is opt-in (workflow_dispatch only). It does NOT run # automatically on push or PR. Review the build artifacts and govulncheck # results before publishing. Per project policy, no automatic credential upload, -# signing key injection, or deployment occurs. +# signing key injection, or deployment occurs. Provenance is signed keylessly +# with the run's short-lived OIDC identity (GitHub artifact attestations); +# verify a download with `gh attestation verify --repo GrayCodeAI/rover`. diff --git a/docs/CI.md b/docs/CI.md index fa848f3..07bd075 100644 --- a/docs/CI.md +++ b/docs/CI.md @@ -75,8 +75,22 @@ before publishing. 7. Generate SBOM (`make sbom`) 8. Run govulncheck (`make vulncheck`) 9. Run all checks (`make check`, `make race`) -10. Compute SHA-256 checksums -11. Create GitHub release with artifacts +10. Compute SHA-256 checksums (`bin/checksums.txt`) +11. Attest build provenance for every checksummed asset with + `actions/attest-build-provenance` (keyless Sigstore signing through the + run's OIDC token; needs `id-token: write` and `attestations: write`) +12. Create GitHub release with artifacts + +### Verifying a release download + +```sh +sha256sum -c checksums.txt --ignore-missing +gh attestation verify rover-linux-amd64 --repo GrayCodeAI/rover +``` + +The attestation proves which workflow run, commit and repository built the +file. It does not prove the code is correct or secure. The existing `v0.0.1` +release is a source-only tag with no binary assets, so it has no attestations. **No automatic publication, merge, release, deployment, or credential upload occurs.** The release workflow requires explicit `workflow_dispatch` trigger. diff --git a/internal/model/version_test.go b/internal/model/version_test.go index 8b79aa9..c5d5227 100644 --- a/internal/model/version_test.go +++ b/internal/model/version_test.go @@ -114,6 +114,26 @@ func TestVersionSurfaces(t *testing.T) { } } +func TestReleaseWorkflowAttestsProvenance(t *testing.T) { + release := string(repositoryFile(t, ".github/workflows/release.yml")) + for _, required := range []string{ + "id-token: write", + "attestations: write", + "uses: actions/attest-build-provenance@", + "subject-checksums: bin/checksums.txt", + } { + if !strings.Contains(release, required) { + t.Fatalf("release workflow must attest provenance of its checksummed assets; missing %q", required) + } + } + checksums := strings.Index(release, "> checksums.txt") + attest := strings.Index(release, "actions/attest-build-provenance@") + publish := strings.Index(release, "softprops/action-gh-release@") + if checksums < 0 || publish < 0 || !(checksums < attest && attest < publish) { + t.Fatal("release workflow must attest after computing checksums and before publishing") + } +} + func TestSDKSourceTreeRemoved(t *testing.T) { _, file, _, ok := runtime.Caller(0) if !ok { From e8741d85add052183d0a30440839e14f0988adff Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:51:44 +0530 Subject: [PATCH 12/23] docs: restructure README and STATUS around the shipped Go product README opened with a ~100 line paragraph that mixed store internals, the unreleased Rust port and a Ctrl-B keybinding manual, and documented `rover agent session` commands and TUI keys that only the Rust preview has; the installed Go `rover` rejects them. It also said Linux was required, called the 0.2.0-alpha.1 report "Current validation", and claimed no public repo, hosted CI or release existed. - README now leads with what Rover is, install-from-source steps, one platform matrix (Linux validated in hosted CI; macOS observed when built with cgo, cross-built darwin binaries have a stub store; Windows unsupported), the quickstart and the existing Go usage. A short "Rust port (preview, not the product)" section points to the new docs/RUST_PREVIEW.md, which holds the rover-rs build steps, state location, TUI key table, agent session commands and gaps. - docs/validation/v0.0.1/REPORT.md records the hosted CI run for the v0.0.1 tag (go1.27.1 from setup-go 'stable', steps and results) and the later main run; README labels the 0.2.0-alpha.1 report historical. - STATUS keeps the Rust progress narrative in its own section, states that Rover does not build, test or support Windows (portable-pty's ConPTY backend is not a Rover capability), reframes the increment log as history (A-H shipped in v0.0.1, I is unreleased, make sdk-test no longer exists) and replaces "no hosted workflow was run" and "Linux validation pending" with the hosted CI facts. Refs: F064, F065, F066, F070, F071, F074, F275, F276 Co-Authored-By: Claude Opus 5.5 --- README.md | 168 ++++++++---------------- STATUS.md | 215 ++++++++++++++----------------- docs/RUST_PREVIEW.md | 123 ++++++++++++++++++ docs/validation/v0.0.1/REPORT.md | 47 +++++++ 4 files changed, 320 insertions(+), 233 deletions(-) create mode 100644 docs/RUST_PREVIEW.md create mode 100644 docs/validation/v0.0.1/REPORT.md diff --git a/README.md b/README.md index fb13c16..8740f63 100644 --- a/README.md +++ b/README.md @@ -5,107 +5,38 @@ **Terminal-first agent workspaces, persistent sessions, parallel workflows, verification, review, and evidence.** -`0.0.1` is the initial public OSS release — terminal-first and agent-neutral. See [STATUS.md](STATUS.md) for explicit boundaries. - -The Rust migration is in progress. The current published CLI and TUI remain Go; -the Rust workspace now contains core model and repository identity primitives, -versioned SQLite migrations, transactional record/event/idempotency APIs, -project-scoped grant services, and a Unix state-root and blob/file layer. It is -not yet a product replacement. Store-level agent/resource reservations, -local budget accounting, read-only legacy-state inventory, and an opt-in Go -state importer are also ported. The importer preserves the source tree, requires -explicit operator assertions, excludes runtime task/check trees, and sanitizes -grants and execution process state. Repeat imports verify copied rows, events, -request keys, receipt counts, and object digests. See the -[migration and rollback guide](docs/design/GO_RUST_STATE_MIGRATION.md). -The `rover-source` crate captures committed Git blobs and working-tree bytes -with explicit consistency labels; persistence currently uses the Unix -state-root adapter. The `rover-execution` crate has a tested Unix runner for -argv-only commands, scrubbed environments, bounded live output logs, timeouts, -and process-group cancellation. A Rust `rover tui` executable now provides an -initial Unix path that starts or reattaches to a project-scoped login shell, -streams its terminal output through the bounded screen model, and saves layout -state. Session owner crash recovery, broader CLI behavior, and native runtime -validation on Linux and Windows remain open. A separate `rover-tui` library -renders validated workspace layouts and handles basic -tab/pane keys, and can display filtered text supplied by Rover's terminal -screen model. On Unix it can attach through a caller-supplied session client, -stream output through a bounded queue, send child input/resize events, and -detach with Ctrl-]. Workspace shortcuts use Ctrl-B as a prefix while attached. -Press `?` for keyboard help in the standalone workspace, or Ctrl-B then `?` -while attached; the overlay keeps keys such as `q` from reaching the shell. -Ctrl-B then `t` opens a read-only snapshot of this project's stored tasks; -use the arrow keys to browse, Enter to inspect recorded task/process metadata, -`r` refreshes the project task snapshot while preserving the selected task when -possible. `c` requests task cancellation after a `y` confirmation; it does not -undo prior external actions. `o` or `e` opens the selected task's stdout or -stderr after checking its bytes against the recorded SHA-256. Output is -control-character sanitized and clipped to a 64 KiB display window; use the -arrow keys or Page Up/Page Down to scroll, and Esc to return to task details. -When a task links an investigation, `i` opens its stored decision, checks, -unknowns, and findings. This view labels the material as recorded assessment -evidence; it does not independently verify or rerun those checks. The loader -rejects evidence whose repository or candidate does not match the selected -task. -In task detail, `d` displays the bounded list of added, changed, and deleted -paths between the task's verified base and candidate snapshots. The full -applicable patch remains available through `rover diff`. -In the task list, `/` opens a case-insensitive search over task ID, status, -objective, candidate, and error text; Enter applies it and Esc discards the -draft. `s` cycles deterministic Updated, Status, and Objective ordering. Rover -saves the query and ordering per canonical repository in its Rust state store. -Esc or `t` closes the list. -Ctrl-B then `p` opens a searchable command palette with task, tab, help, and -file-browser actions supported by the current TUI provider. -When this project pane has an explicitly bound Codex or Claude session, -Ctrl-B then `a` shows the exact saved identity. Press `r` to review its native -interactive resume command, then `y` to send it to the attached project shell; -`d` removes Rover's binding after confirmation without stopping a running -agent. Resume uses the provider's configured permissions and sandbox; Rover -does not mediate them. -For scripts, `rover agent session bind --repo PATH --pane active --adapter codex-exec ---id EXACT_ID` records a user-selected identity for a pane in the saved project -workspace (`--pane ID` selects another saved pane). `rover agent session list --repo PATH` lists bindings; -`rover agent session status --repo PATH --pane ID` inspects one; and -`rover agent session clear --repo PATH --pane ID` removes Rover's binding only. -List and status distinguish a saved identity from live agent status, which -remains unknown until Rover has process or lifecycle evidence. Use -`--adapter claude-print` for a Claude session. -Ctrl-B then `n` opens repository-scoped local notes for reusable prompt/context -snippets. Press `n` to create, Enter to edit, or `d` to delete after confirmation; -the first line labels each snippet. In the editor, Left/Right, Home/End, -Backspace, and Delete move or change text; Ctrl-S saves and Esc asks before -discarding edits. Rover stores at most 32 notes, 32 KiB each and 128 KiB total. -Notes remain local and are not submitted to an agent automatically. -Ctrl-B then `d` opens local task briefing drafts. `n` creates a draft, Enter edits, -and `d` deletes after confirmation. Drafts hold a title, path globs, dependencies, -quality-gate text, and multiline prompt; Ctrl-S saves and Esc confirms before -discarding edits. Up to 32 drafts are stored per repository (120-byte title, -8 KiB paths, 4 KiB dependencies, 1 KiB gate, and 16 KiB prompt). Drafts are not -tasks, path globs and gate text are not interpreted, and nothing is sent to a worker. -Ctrl-B then `k` opens repository-scoped saved shell commands. Use `n` to create, -Enter to edit, `d` to delete after confirmation, and `r` to review a command -before sending it once to the attached shell. Commands are single lines, limited -to 256 bytes, and run with Rover's OS-user authority; they are not sandboxed. -Ctrl-B then `f` opens the repository file tree; arrows move, Enter -opens directories or a file preview, Backspace goes to the parent, `h` toggles -hidden entries, and `r` refreshes. Git changes receive status labels and colors. -Ctrl-B then `.` opens quick open, which searches hidden repository files too. -Previews refuse symlinks and special files, sanitize terminal controls, and show -at most 256 KiB. Press `e` on a text preview to edit files up to 8 MiB; Ctrl-S -saves with an atomic replacement and stale-content check, Ctrl-D shows the -unsaved unified diff with three context lines. Ctrl-E opens a private working -copy in the executable named by `VISUAL` or `EDITOR` (default `vi`), and Esc -prompts before discarding edits. -The editor setting is one executable path, invoked without a shell; its changes -return to the TUI buffer and still require Ctrl-S to save. `.git` metadata is -excluded from both browsers. -This initial executable path is not a product replacement; Go remains -authoritative until the Rust parity gates pass. -Track the staged port in [the Rust parity -plan](docs/design/RUST_PARITY_PLAN.md). - -## Run the complete demonstrations +`0.0.1` is the initial public OSS release: a pre-1.0 alpha, terminal-first and +agent-neutral. Rover runs coding agents or your own commands in separate Git +worktrees (not a sandbox), verifies the result with the checks you approve, and +keeps replayable evidence of what happened, optionally signed with your own +Ed25519 key. The product is the Go `rover` binary. See [STATUS.md](STATUS.md) for +explicit boundaries. + +## Install from source + +There is no package-manager listing and the `v0.0.1` release carries no +binaries, so build from a clone: + +```sh +git clone https://github.com/GrayCodeAI/rover.git +cd rover +make build # writes ./bin/rover +make install # optional: copies to ~/.local/bin/rover; refuses to overwrite +``` + +Build prerequisites: Git, Go 1.26.6 or newer, a C compiler, and SQLite +development headers/library (Debian/Ubuntu: `build-essential libsqlite3-dev`; +macOS: the Xcode Command Line Tools). Python 3 runs the deterministic +demonstrations. There are no external Go modules. The store uses cgo and system +SQLite, so this is not a dependency-free static binary. + +| Platform | Status | +|---|---| +| Linux/amd64 | Validated: hosted CI (`source-validation`, ubuntu-24.04) runs `make check`, `race`, `fuzz`, `demo`, `demo-extended`, `cross-build` and `vulncheck` on every push and pull request. | +| macOS (darwin/arm64) | Observed locally, including PTY/TUI, when built from source with cgo. Binaries from `make cross-build` use `CGO_ENABLED=0` and contain a **non-functional stub store**, so they cannot run real workflows. Build on the Mac instead. | +| Windows | Not supported (the Go code uses `syscall.O_NOFOLLOW`). | + +## Quickstart: run the demonstrations ```sh make build @@ -123,12 +54,6 @@ candidate integration, verification, counterfactual tests, finite mutations, context, reversible agent instructions, MCP, remote CLI control, revocation, backup/restore, and signed evidence. No automatic GitHub push, merge, or deployment. -Build prerequisites: **Linux, Git, Go 1.26.6 or newer, a C compiler and system SQLite development -headers/library**. Python 3 runs the deterministic demonstrations. There are no -external Go modules. This release still uses cgo/system SQLite, not a dependency-free -static binary. The current release gate requires the patched Go 1.26.6 line; validate -with a supported toolchain. - ## Two supported workflow shapes 1. Keep using your coding agent and invoke Rover to inspect and verify its output. @@ -268,27 +193,40 @@ autonomous self-modifying product, or enterprise-separated evaluator. modify same-user host resources. Scoped HTTP grants restrict API operations, not arbitrary code once local execution is authorized. Keep untrusted code away from credential-bearing hosts. The restricted Docker adapter has argument/admission tests -but has not been executed against a Docker daemon here. No protected CI publisher, +and one functional smoke against a local daemon (same machine, not a security +certification). No protected CI publisher, team SSO, remote worker fencing or hostile-code security certification is claimed. Source capture still rejects symlinks, submodules, unresolved LFS pointers, unsafe or case-colliding paths and oversized source states. Working-tree capture uses repeated reads, not an atomic filesystem transaction. Prefer committed snapshots. -Linux/amd64 is the validated envelope. darwin/arm64 is observed locally (PTY/TUI -supported via posix_openpt); native macOS and Windows full validation remain -pending. The repository and package name -are proposed: no public GitHub repo, package-manager release or hosted CI execution -is claimed. The public naming collision noted in the design remains unresolved. +The repository is public at . `v0.0.1` is +a source-only release tag; there are no binary assets and no package-manager +listing. Hosted CI runs `source-validation` on GitHub Actions for every push and +pull request. Report problems in GitHub issues ([SUPPORT.md](SUPPORT.md)) and +vulnerabilities privately ([SECURITY.md](SECURITY.md)). The public naming +collision noted in the design remains unresolved. + +## Rust port (preview, not the product) + +A Rust port lives in `crates/`. It is unreleased and is not a replacement for the +Go `rover`. Go stays authoritative until the parity gates pass. Its executable is +named `rover-rs` so it cannot shadow `rover` (`cargo +1.88.0 build -p rover-cli`, +Unix only). The TUI keys, commands, and limits are in +[docs/RUST_PREVIEW.md](docs/RUST_PREVIEW.md), progress is in +[STATUS.md](STATUS.md#rust-port-preview-unreleased), and the staged plan is in +[the Rust parity plan](docs/design/RUST_PARITY_PLAN.md). ## Documentation - [Implementation status](STATUS.md) -- [Rust parity plan](docs/design/RUST_PARITY_PLAN.md) · [Rust third-party notices](licenses/THIRD_PARTY_NOTICES.md) - [Command reference](docs/CLI.md) - [Architecture](docs/ARCHITECTURE.md) - [Security and limitations](docs/SECURITY_MODEL.md) -- [Current validation](docs/validation/v0.2.0/REPORT.md) +- [CI and release reference](docs/CI.md) +- [Validation for 0.0.1](docs/validation/v0.0.1/REPORT.md) · [Historical validation (0.2.0-alpha.1, before the renumbering to 0.0.1)](docs/validation/v0.2.0/REPORT.md) +- [Rust port preview](docs/RUST_PREVIEW.md) · [Rust parity plan](docs/design/RUST_PARITY_PLAN.md) · [Design decisions](docs/design/README.md) · [Rust third-party notices](licenses/THIRD_PARTY_NOTICES.md) - [Original ten-layer plan and acceptance inventory](docs/acceptance/) MIT. No telemetry, model training, automatic upload, push, merge, or deployment diff --git a/STATUS.md b/STATUS.md index ac34661..6392dc8 100644 --- a/STATUS.md +++ b/STATUS.md @@ -2,110 +2,14 @@ This is an expanded local OSS implementation, not completion of every capability in the original ten-layer vision. Working code and independently tested boundaries are -not interchangeable. See the validation report for exact observed tests. - -The Rust port is also in progress. Its workspace provides validated core and -repository identity primitives, versioned SQLite migrations, transactional -record/event/idempotency APIs, project-scoped grant services, and a Unix -state-root/blob/file layer, plus store-level agent and named-resource -reservations, atomic local budget accounting, read-only legacy-state inventory, -and an opt-in Go state importer. The importer retains the source tree, excludes -runtime task/check trees, revokes grants, and sanitizes process state; independent -repeat-import verification checks records, events, request keys, receipt counts, -and content-addressed object digests. See -`docs/design/GO_RUST_STATE_MIGRATION.md` for source-retention and rollback -instructions. `rover-source` now captures committed Git blobs and working-tree -bytes with explicit consistency labels; persistence currently uses the Unix -state-root adapter. The new `rover-execution` crate has a tested Unix -argv-only runner with scrubbed environment, per-stream bounds, live private log -capture, timeout, and process-group cancellation. Its supervisor module now -has transactional task claim, private detached worker launch, a managed -heartbeat loop, cancellation, and conservative stale-process reconciliation; -confirmed `LOST` transitions release that task's leases, while normal task -finalization and client reconnect remain unimplemented. -The new PTY wrapper covers local create/read/write/resize/close on native Unix -and Windows ConPTY backends; native runtime validation currently covers macOS -only. `rover-tui` has a Ratatui workspace renderer, basic tab/pane input, and a -Unix API for attaching a caller-supplied session client. The bridge sends input -and resize events, reads output on a bounded queue, and renders only text from -Rover's terminal emulator. It preserves Ctrl-] detach and Ctrl-B-prefixed -workspace controls. The new `rover-cli` binary provides an initial Unix-only -`rover tui` launch path: it admits a dedicated Rust state root, starts or -reattaches to a project-scoped login shell owned by a detached local process, -persists the workspace layout during attached use, and can display a bounded, -read-only snapshot of project task records from Rover's SQLite store. Ctrl-B -then `t` opens the task board; Enter opens recorded task and process metadata. -Press `r` on the task list or detail to refresh records while preserving the -selected task where possible. Press `c` to request cancellation after an explicit -`y` confirmation; the store rejects terminal tasks and the UI warns that prior -external actions are not undone. Press `o` or `e` in task detail to read the -selected task's stdout or stderr from the blob store; the TUI independently -verifies the full output against the recorded SHA-256, sanitizes controls, and -shows at most a 64 KiB excerpt. Press `i` to inspect the task's linked stored -investigation decision, checks, unknowns, and findings; the view treats these -as recorded evidence and enforces repository and candidate binding. Press `d` -in task detail to inspect a bounded path inventory from verified base and -candidate snapshots; use `rover diff` for the complete applicable patch. -In the task list, `/` starts a case-insensitive search over ID, status, -objective, candidate, and error; Enter applies it and Esc discards it. `s` -cycles Updated, Status, and Objective sorting. Query and order preferences are -stored per repository in Rover's Rust state database. -Ctrl-B then `p` opens a searchable command palette for provider-supported task, -tab, help, and file-browser actions. -If the active pane has a saved Codex or Claude session binding, Ctrl-B then `a` -shows its exact identity. `r` opens a command review and a second `y` confirmation -sends the interactive resume command to the attached shell; `d` removes the -binding after confirmation. Rover leaves permission and sandbox behavior to the -provider. -The CLI supports explicit exact-ID binding to an existing saved workspace pane, -project-scoped list/status, and binding clear with `rover agent session`. -The attached TUI samples the owned PTY's foreground process group and displays -recognized identity while keeping state unknown until manifests or lifecycle -evidence are available. CLI inventory and lifecycle transport remain separate -work; see ADR-0025 and ADR-0026. -The agents crate now contains an independently implemented, bounded Herdr -manifest evaluator and all 22 hash-verified bundled detection TOML files. Its -gate operators, 15 region selectors, rule ordering, OSC fields, visibility -flags, and fallback behavior are covered by Rust tests. The attached TUI feeds -the live viewport, OSC title, and bounded OSC 9;4 progress payloads to the -evaluator for audited direct executable mappings. Lifecycle-authority -integration and wrapper/process-group expansion remain open in -P-062/P-166/P-167; transcript-viewer skip rules preserve the latest same-agent -state in the attached TUI. See ADR-0027. -Ctrl-B then `n` opens a repository-scoped notes list for reusable prompt/context -snippets. `n` creates, Enter edits, and `d` deletes after confirmation; the first -line labels each item. Editing supports cursor movement, bounded UTF-8 text, -explicit Ctrl-S persistence, and an Esc discard prompt. Up to 32 notes (32 KiB -each, 128 KiB total) are stored locally and are not submitted to an agent. -Ctrl-B then `d` opens repository-scoped task briefing drafts with create, edit, -and confirmed delete. Drafts store title, path globs, dependencies, quality-gate -text, and multiline prompt. They save explicitly and are never dispatched or -interpreted; schema v1 records load with defaults and new saves use schema v2. -Limits are 32 drafts, 120 title bytes, 8 KiB paths, 4 KiB dependencies, 1 KiB -gate, and 16 KiB prompt. -Ctrl-B then `k` opens repository-scoped saved shell commands. Create/edit/delete -are explicit; `r` displays the full bounded single-line command and the same-user, -unsandboxed execution warning before `y` sends it once to the attached shell. -Commands are capped at 256 bytes and 32 entries. -Ctrl-B then `f` opens a file tree with Git status labels and colors; -arrows navigate, Enter opens a directory or preview, Backspace goes up, `h` -shows hidden files, and `r` refreshes. Ctrl-B then `.` opens hidden-file quick -open. `.git`, symlinks, and special files are not traversed or previewed; text -previews are sanitized and capped at 256 KiB. Text files up to 8 MiB can be -edited with `e`; Ctrl-S saves atomically after a stale-content check, Ctrl-D -shows a bounded unified diff with three context lines, Ctrl-E opens -`$VISUAL`/`$EDITOR` on a private -working copy, and Esc confirms before discarding dirty edits. External editor -changes return to the buffer and still require Ctrl-S to save. -Session recovery after owner crash/restart and other task actions, -full keyboard/layout interaction, platform parity, and the broader product -CLI remain open; this is an early Rust slice, not a Go runtime replacement. -Non-Unix process execution beyond the PTY wrapper remains unimplemented. Rover -does not yet replace the Go runtime. -Go remains authoritative until the parity tasks and removal gates in -`docs/design/RUST_PARITY_PLAN.md` pass. Python, TypeScript, and Go SDK source -was removed per the user's explicit product-scope decision; dated validation -records below retain the results from when those clients existed. +not interchangeable. See [docs/validation/v0.0.1/REPORT.md](docs/validation/v0.0.1/REPORT.md) +for the hosted CI record of the release. + +The product is the Go `rover` binary. An unreleased Rust port is described +[below](#rust-port-preview-unreleased); it does not replace the Go runtime. +Python, TypeScript, and Go SDK source was removed per the user's explicit +product-scope decision; dated validation records below keep the results from when +those clients existed. | Layer | Implemented | Remaining / not validated | |---|---|---| @@ -120,15 +24,86 @@ records below retain the results from when those clients existed. | L9 evidence | SQLite/event/artifact records, exact identities, diff/local review, exports, online backup/restore, explicit-key Ed25519 signatures | Protected CI check publisher, real external PR/merge/deploy writes, in-toto/SLSA attestations, full retention/erasure and global quota system | | L10 improvement | Historical check-order proposals, explicit labels/datasets, disjoint heldout evaluation, proposal/dataset digests, local promotion/revocation, explicit strategy application | Live empirical optimization validation, RL training, autonomous self-play/RSI, independent evaluator deployment | -## Unreleased platform increments on top of `0.0.1` (pending Linux re-verification and review) - -The older increment records in this section are historical. SDK test results -refer to the removed Python, TypeScript, and Go clients and are not current -product or CI claims. - -These source changes exist in the working tree. They are **not** part of any -validated release until they are re-run on owned Linux/amd64 with a supported -toolchain and reviewed: +## Rust port preview (unreleased) + +The Rust workspace in `crates/` is an unreleased port. It does not replace the Go +runtime, and Go remains authoritative until the parity tasks and removal gates in +`docs/design/RUST_PARITY_PLAN.md` pass. Its executable is `rover-rs`; usage, TUI +keys, and limits are in [docs/RUST_PREVIEW.md](docs/RUST_PREVIEW.md). CI gates it +with `make rust-check` (fmt, clippy with pedantic lints as errors, workspace +tests, dependency license audit) and `make rust-sbom` on Rust 1.88.0. + +Implemented and tested so far: + +- Core and repository identity primitives, versioned SQLite migrations, + transactional record/event/idempotency APIs, project-scoped grant services, a + Unix state-root/blob/file layer, store-level agent and named-resource + reservations, and atomic local budget accounting. +- Read-only legacy-state inventory and an opt-in Go state importer. The importer + keeps the source tree, excludes runtime task/check trees, revokes grants, and + sanitizes process state. Independent repeat-import verification checks records, + events, request keys, receipt counts, and content-addressed object digests. See + `docs/design/GO_RUST_STATE_MIGRATION.md` for source retention and rollback. +- `rover-source` captures committed Git blobs and working-tree bytes with explicit + consistency labels; persistence uses the Unix state-root adapter. +- `rover-execution` has a tested Unix argv-only runner with a scrubbed + environment, per-stream bounds, live private log capture, timeout, and + process-group cancellation. Its supervisor has transactional task claim, private + detached worker launch, a managed heartbeat loop, cancellation, and conservative + stale-process reconciliation. Confirmed `LOST` transitions release that task's + leases. Normal task finalization and client reconnect remain unimplemented. +- The PTY wrapper covers local create/read/write/resize/close on native Unix. + It is built on `portable-pty` 0.9.0. That dependency also contains a Windows + ConPTY backend, but **Rover does not build, test, or support Windows**: no CI + job compiles a Windows target, and a full-workspace Windows compile has never + been completed (ADR-0011). Native runtime validation covers macOS only. +- `rover-tui` has a Ratatui workspace renderer, basic tab/pane input, and a Unix + API for attaching a caller-supplied session client. The bridge sends input and + resize events, reads output on a bounded queue, and renders only text from + Rover's terminal emulator. It keeps Ctrl-] detach and Ctrl-B-prefixed + workspace controls. +- `rover-rs tui` is a Unix-only launch path. It admits a dedicated Rust state + root, starts or reattaches to a project-scoped login shell owned by a detached + local process, and persists the workspace layout during attached use. Attached + views cover: a read-only task board (refresh, confirmed cancellation request, + SHA-256-verified bounded stdout/stderr, linked investigation evidence bound to + the repository and candidate, a bounded base-to-candidate path inventory, and + saved search and sort); a command palette; agent session binding review, resume + and clear; notes; task briefing drafts (schema v1 records load with defaults; + saves use schema v2); saved shell commands; and a file tree with Git status, + quick open, bounded previews, and an inline editor with stale-content-checked + atomic save, diff preview, and external-editor handoff. +- `rover-rs agent session` supports explicit exact-ID binding to an existing saved + workspace pane, project-scoped list/status, and binding clear. The attached TUI + samples the owned PTY's foreground process group and shows the recognized + identity, but keeps state unknown until manifests or lifecycle evidence are + available. CLI inventory and lifecycle transport remain separate work (ADR-0025, + ADR-0026). +- `rover-agents` contains an independently implemented, bounded Herdr manifest + evaluator and all 22 hash-verified bundled detection TOML files. Rust tests + cover its gate operators, 15 region selectors, rule ordering, OSC fields, + visibility flags, and fallback behavior. The attached TUI feeds the live + viewport, OSC title, and bounded OSC 9;4 progress payloads to the evaluator for + audited direct executable mappings. Transcript-viewer skip rules preserve the + latest same-agent state. Lifecycle-authority integration and + wrapper/process-group expansion remain open in P-062/P-166/P-167 (ADR-0027). + +Still open: session recovery after owner crash/restart, other task actions, full +keyboard/layout interaction, the broader product CLI, non-Unix process execution, +native Linux runtime validation of the interactive TUI, and any Windows support. + +## Implementation increment log (historical) + +These increments were recorded while the code carried the former +`0.2.0-alpha.1` label. Increments A–H are part of the `v0.0.1` tag (`158d9d5`); +Increment I (cgo-free darwin terminal/PTY, `cross-build`, the stub store and the +release workflow) landed on `main` afterwards and is unreleased. Hosted CI on +GitHub-hosted ubuntu-24.04 has since run the Linux gates green for both: the tag +run and the `main` run at `2cd2337` are recorded in +[docs/validation/v0.0.1/REPORT.md](docs/validation/v0.0.1/REPORT.md). Independent +human review is still outstanding. Every `make sdk-test` / "SDK 3/3" result below +refers to the removed Python, TypeScript, and Go clients; that make target no +longer exists, so those lines are history, not current product or CI claims. **Increment A — darwin temp-path canonicalization:** @@ -357,15 +332,19 @@ in Go 1.26.5 fixed by 1.26.6; no third-party vulnerabilities — zero external G container** (same machine, not an independent host) — no security certification is claimed, and the restricted executor refuses fallback when the daemon is absent. -- CI configuration is included; **no hosted GitHub workflow or public release was run**. +- Hosted CI (`source-validation` on GitHub-hosted ubuntu-24.04) runs on every + push and pull request; the `v0.0.1` tag run is recorded in + `docs/validation/v0.0.1/REPORT.md`. The public `v0.0.1` GitHub release is a + source-only tag with no binary assets. - Remote control was exercised against a local server; **no separate remote host or distributed worker fleet was validated**. - Learning evaluation uses explicit synthetic fixtures and timing assumptions; no real-world speedup or quality improvement is claimed. -- Current local release checks use Go 1.26.6 + system SQLite/cgo; Linux/amd64 functional validation for this checkout remains pending. - Supported-current-Go, native macOS and Windows validation remain pending; the - unreleased increment above has darwin-only observations that do not replace - Linux validation or the pending review. +- Release checks use Go 1.26.6 + system SQLite/cgo. Linux/amd64 functional + validation (check, race, fuzz, demo, demo-extended) runs in hosted CI. Full + native macOS validation and any Windows support remain pending; darwin results + above are local observations, and cross-built darwin binaries carry a + non-functional stub store. ## Preserved constraints diff --git a/docs/RUST_PREVIEW.md b/docs/RUST_PREVIEW.md new file mode 100644 index 0000000..5d767cc --- /dev/null +++ b/docs/RUST_PREVIEW.md @@ -0,0 +1,123 @@ +# Rust port preview (`rover-rs`) + +> **This is not the Rover product.** The released product is the Go `rover` +> binary described in the [README](../README.md). The Rust workspace under +> `crates/` is an unreleased port. No release ships it, and its behaviour and +> state format may change without notice. Go remains authoritative until the +> removal gates in the [Rust parity plan](design/RUST_PARITY_PLAN.md) pass. +> Progress is tracked in [STATUS.md](../STATUS.md#rust-port-preview-unreleased). + +## Build and run + +Requirements: Linux or macOS, `rustup` with the pinned 1.88.0 toolchain +(`rust-toolchain.toml` selects it), and a C compiler. `rusqlite` builds a bundled +SQLite, so no system SQLite headers are needed for the Rust build. + +```sh +rustup toolchain install 1.88.0 --profile minimal --component clippy --component rustfmt +cargo +1.88.0 build -p rover-cli --locked +target/rust-1.88.0/debug/rover-rs --version +target/rust-1.88.0/debug/rover-rs tui --repo /path/to/repo +``` + +The executable is named `rover-rs` so it can never shadow the Go `rover` on +`PATH`. `rover-rs --version` labels itself as an unreleased preview. The name +changes to `rover` only at the final cutover (parity plan task P-164). + +Platforms: interactive use has been observed on macOS only. The CI `rust` job +runs the Rust test suite on Linux (ubuntu-24.04). Windows is **not supported**: +`rover-rs tui` session hosting needs Unix, and nothing in CI compiles the +workspace for a Windows target. + +Contributor gates are `make rust-check CARGO='cargo +1.88.0'` and +`make rust-sbom` (see [CI reference](CI.md)). + +## State + +The Rust preview keeps its own state, separate from the Go product: +`--state PATH`, else `$ROVER_RUST_HOME`, else `$XDG_STATE_HOME/rover-rust`, +else `~/.local/state/rover-rust`. The Go product uses `--state`, `$ROVER_HOME` +or `~/.config/rover`. An opt-in importer copies Go state into a Rust state root: +it keeps the source tree, requires explicit operator assertions, excludes runtime +task/check trees, and sanitizes grants and execution process state. Repeat +imports verify copied rows, events, request keys, receipt counts, and object +digests. See the [migration and rollback guide](design/GO_RUST_STATE_MIGRATION.md). + +## What the workspace implements so far + +- `rover-core`: model and repository identity primitives, digests, and time. +- `rover-store`: versioned SQLite migrations; transactional record, event and + idempotency APIs; a Unix state-root and blob/file layer; store-level agent and + named-resource reservations; local budget accounting; read-only legacy-state + inventory and the opt-in Go state importer. +- `rover-access`: locally issued bearer grants bound to an exact project and + state root. +- `rover-tasks`: project-bound durable task plans, kept separate from task runs + and never dispatched on their own. +- `rover-source`: captures committed Git blobs and working-tree bytes with + explicit consistency labels (persisted through the Unix state-root adapter). +- `rover-execution`: a tested Unix runner for argv-only commands with scrubbed + environments, bounded live output logs, timeouts, and process-group + cancellation; a task supervisor; PTY sessions; the workspace layout model; a + bounded terminal screen model. +- `rover-agents`: agent profiles, native transcripts, detection evidence, state + rollups, lifecycle authority, saved session bindings, and a bounded evaluator + for the audited Herdr detection manifests. +- `rover-tui`: the Ratatui workspace renderer and attached-session bridge. +- `rover-cli`: the `rover-rs` executable. + +## `rover-rs tui` + +`rover-rs tui [--state PATH] [--repo PATH]` starts or reattaches to a +project-scoped login shell owned by a detached local process. It streams the +shell's output through the bounded screen model and saves the workspace layout. +Ctrl-] detaches. While attached, workspace shortcuts use the Ctrl-B prefix. +Press `?` for help in the standalone workspace, or Ctrl-B then `?` while +attached. The help overlay keeps keys such as `q` from reaching the shell. + +| Keys (attached) | Opens | Keys inside the view and limits | +|---|---|---| +| Ctrl-B `t` | Read-only snapshot of this project's stored tasks | Arrows browse and Enter shows recorded task/process metadata. `r` refreshes and keeps the selection where possible. `c` then `y` requests cancellation; prior external actions are not undone. `o`/`e` show stdout/stderr after checking the full bytes against the recorded SHA-256; output is control-sanitized and clipped to a 64 KiB window (arrows, Page Up/Down, Esc to return). `i` shows the linked investigation's stored decision, checks, unknowns, and findings as recorded evidence, not a re-run; evidence for another repository or candidate is rejected. `d` lists added/changed/deleted paths between the verified base and candidate snapshots; use the Go `rover diff` for the full patch. `/` searches ID, status, objective, candidate, and error text (Enter applies, Esc discards). `s` cycles Updated, Status, and Objective order. The query and order are saved per repository. Esc or `t` closes. | +| Ctrl-B `p` | Searchable command palette | Task, tab, help, and file-browser actions that the current provider supports. | +| Ctrl-B `a` | The exact saved Codex or Claude session bound to the active pane | `r` reviews the native interactive resume command and `y` sends it to the attached shell. `d` removes Rover's binding after confirmation without stopping a running agent. Resume uses the provider's own permissions and sandbox; Rover does not mediate them. | +| Ctrl-B `n` | Repository-scoped notes for reusable prompt/context snippets | `n` creates, Enter edits, `d` deletes after confirmation. The first line labels each note. Editor: Left/Right, Home/End, Backspace, Delete; Ctrl-S saves; Esc asks before discarding. At most 32 notes, 32 KiB each, 128 KiB total. Notes stay local and are never sent to an agent automatically. | +| Ctrl-B `d` | Local task briefing drafts | `n`, Enter, `d` as for notes. A draft holds a title, path globs, dependencies, quality-gate text, and a multiline prompt. Up to 32 drafts per repository (120-byte title, 8 KiB paths, 4 KiB dependencies, 1 KiB gate, 16 KiB prompt). Drafts are not tasks: globs and gate text are not interpreted and nothing is sent to a worker. | +| Ctrl-B `k` | Repository-scoped saved shell commands | `n`, Enter, `d` as above. `r` shows the full command and an unsandboxed-execution warning; `y` sends it once to the attached shell. Single lines of at most 256 bytes, 32 entries. Commands run with Rover's OS-user authority and are not sandboxed. | +| Ctrl-B `f` | Repository file tree with Git status labels and colours | Arrows move, Enter opens a directory or file preview, Backspace goes to the parent, `h` toggles hidden entries, `r` refreshes. | +| Ctrl-B `.` | Quick open | Also searches hidden repository files. | +| Ctrl-B `?` | Keyboard help | | + +File previews refuse symlinks and special files, sanitize terminal controls, and +show at most 256 KiB. `.git` metadata is excluded from both browsers. Press `e` on +a text preview to edit files up to 8 MiB. In the editor, Ctrl-S saves through an +atomic replacement with a stale-content check, Ctrl-D shows the unsaved unified +diff with three context lines, and Esc asks before discarding edits. Ctrl-E opens +a private working copy in the executable named by `VISUAL` or `EDITOR` (default +`vi`). That setting is one executable path, invoked without a shell. Its changes +return to the TUI buffer and still need Ctrl-S to save. The +[security model](SECURITY_MODEL.md) describes these trust boundaries. + +## Other `rover-rs` commands + +```sh +rover-rs agents [--json] +rover-rs agent list +rover-rs agent capabilities +rover-rs agent session bind --repo PATH --pane active|ID --adapter codex-exec|claude-print --id EXACT_ID [--state PATH] +rover-rs agent session list --repo PATH [--state PATH] +rover-rs agent session status --repo PATH --pane ID [--state PATH] +rover-rs agent session clear --repo PATH --pane ID [--state PATH] +``` + +`agent session bind` records a user-selected session identity for a pane in the +saved project workspace (`--pane active` or a saved pane ID). `list` and `status` +distinguish a saved identity from live agent status. Live status stays unknown +until Rover has process or lifecycle evidence. `clear` removes Rover's binding +only. The Go `rover` has none of the `agent session` commands. + +## Known gaps + +Session-owner crash/restart recovery, the broader product CLI (parity tasks P-131 +and P-132), full keyboard/layout interaction, lifecycle-event transport, native +Linux runtime validation of the interactive TUI, and any Windows support remain +open. Architecture decisions are indexed in [design/README.md](design/README.md). diff --git a/docs/validation/v0.0.1/REPORT.md b/docs/validation/v0.0.1/REPORT.md new file mode 100644 index 0000000..0c82919 --- /dev/null +++ b/docs/validation/v0.0.1/REPORT.md @@ -0,0 +1,47 @@ +# Rover 0.0.1 — hosted CI validation record + +`v0.0.1` is an annotated tag on commit +`158d9d5eb79fe2763806c7a5f87021c1919d2d47` (tag object `efd450d6`). The GitHub +release of the same name, published 2026-09-16, is source-only: it has **no +binary assets**, checksums, or attestations. + +This record copies what GitHub Actions reported. It is not a certification of +live coding providers, containers, remote hosts, or production readiness. Earlier +local validation of the same code under its former `0.2.0-alpha.1` label is kept +as history in [../v0.2.0/REPORT.md](../v0.2.0/REPORT.md). + +## Tag run + +- Workflow: `source-validation` (`.github/workflows/ci.yml` at the tag), event + `push` of tag `v0.0.1` +- Run: +- Runner: GitHub-hosted `ubuntu-24.04` (linux/amd64), system `libsqlite3-dev`, + Go `stable` via `actions/setup-go`, which resolved to **go1.27.1** (the run + log prints `go version go1.27.1 linux/amd64`). The tag workflow did not pin a + toolchain; later commits pin Go 1.26.6 as the minimum. +- Job `linux`: started 2026-09-16T12:44:08Z, completed 2026-09-16T12:46:23Z, + conclusion **success** + +| Step at the tag | Command | Result | +|---|---|---| +| Format, vet, tests | `make check` | success | +| Race detector | `make race` | success | +| Short parser fuzz campaigns | `make fuzz` | success | +| Real detached fixture workflow | `make demo` | success | +| Extended terminal, workflow and protocol scenarios | `make demo-extended` | success | +| Python CLI client | `make sdk-test` | success, "Ran 3 tests" (SDK since removed) | +| TypeScript CLI client | `node --test sdk/typescript/test/*.test.ts` | success (SDK since removed) | +| Go CLI client | `go test ./sdk/go` | success (SDK since removed) | + +The version-surface, source-manifest, cross-build and `govulncheck` gates did not +exist in the workflow at the tag, so this run does not cover them. + +## Later runs on `main` (unreleased) + +The tip of `main` before the Rust workspace landed, commit +`2cd2337f685e14fe161a6e7d68296fb84cb594ec`, passed on 2026-09-23 in run + on `ubuntu-24.04` +with pinned go1.26.6. That run also ran `make toolchain-check`, +`make version-check`, `make manifest-check`, `make cross-build` and +`make vulncheck`, all **success**. Those commits are not part of the `0.0.1` +release. From e79819be3ed62aeb5cf2dd4c7df2089c659661b9 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:52:25 +0530 Subject: [PATCH 13/23] docs: state the real publication, support and security intake status SECURITY, SUPPORT and GOVERNANCE still described an unpublished "source bundle" with no public repository, hosted issue queue or intake channel, while GrayCodeAI/rover is public with issues enabled, a v0.0.1 release and green hosted CI. - SECURITY: report privately to security@graycodeai.com; say plainly that GitHub private vulnerability reporting is not enabled yet (the API reports enabled=false) and will become the second channel; no SLA; explain release provenance verification. - SUPPORT: public repo, source-only v0.0.1 tag, no package-manager listing or SLA, GitHub issues for problems, hello@graycodeai.com for anything else, and why `go install` is not a documented path. - GOVERNANCE: maintainers are the GrayCodeAI organization owners. Refs: F065, F275 Co-Authored-By: Claude Opus 5.5 --- GOVERNANCE.md | 8 +++++--- SECURITY.md | 41 +++++++++++++++++++++++++++++------------ SUPPORT.md | 20 ++++++++++++++------ 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 8a5ea3f..11906d1 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -1,8 +1,10 @@ # Governance (bootstrap) -Rover is a user-directed, tool-assisted OSS bootstrap. The intended project owner must -establish maintainer permissions and review procedures in the eventual hosting repository. -No public maintainer roster, legal entity, steering committee, or foundation is asserted. +Rover is a user-directed, tool-assisted open-source project hosted at +. The maintainers are the owners of the +GrayCodeAI GitHub organization, who set repository permissions and review +procedures there. No separate public maintainer roster, legal entity, steering +committee, or foundation is asserted. Initial rule: changes to evidence meaning, required-check interpretation, trust modes, credential authority, release keys, or learning promotion rules require explicit human diff --git a/SECURITY.md b/SECURITY.md index c9c9e4e..0d8b6b0 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,14 +1,31 @@ # Security -This is an experimental source alpha, not hardened multi-tenant or production assurance -software. Read [the complete threat model](docs/SECURITY_MODEL.md) before executing code. -Only owned/trusted local inputs were used in validation. Do not put hostile repositories -or sensitive credentials on an unrestricted alpha worker. - -No public repository, security mailbox, or private vulnerability intake has been provisioned -by this source bundle. Before publication, the actual maintainer must enable a private -reporting channel and document it here. Do not publish secrets or live exploit details in -public issue templates. This document does not claim a monitored address or response SLA. - -No supported production release line exists yet. Maintainers must review security updates, -third-party/toolchain versions, permissions, and release provenance before distributing binaries. +Rover is a pre-1.0 alpha, not hardened multi-tenant or production assurance +software. Read [the complete threat model](docs/SECURITY_MODEL.md) before executing +code. Only owned/trusted local inputs were used in validation. Do not put hostile +repositories or sensitive credentials on an unrestricted alpha worker. + +## Reporting a vulnerability + +Do not open a public GitHub issue for a vulnerability, and do not publish secrets +or working exploit details anywhere public. + +- Email **security@graycodeai.com** with the affected version or commit, platform, + reproduction steps, and impact. +- GitHub private vulnerability reporting is **not yet enabled** on + `GrayCodeAI/rover`. Once the maintainers enable it, the repository's + [Security tab](https://github.com/GrayCodeAI/rover/security) will offer + "Report a vulnerability" as a second private channel. + +No response-time SLA is offered. + +## Supported versions and releases + +There is no supported production release line yet. Fixes land on `main` and in +the next `0.0.x` release. `v0.0.1` is a source-only tag with no binary assets. +Binaries built by the release workflow carry GitHub build-provenance +attestations; verify a download with +`gh attestation verify --repo GrayCodeAI/rover` (see +[docs/CI.md](docs/CI.md)). Maintainers must review security updates, +third-party/toolchain versions, permissions, and release provenance before +distributing binaries. diff --git a/SUPPORT.md b/SUPPORT.md index d6a0441..d0cbeb6 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -1,9 +1,17 @@ # Support and publication status -This source bundle is an experimental implementation. No public service, package-manager -listing, support SLA, hosted issue queue, or release signing identity was created. Use -README and `rover help` for the implemented command surface. Include platform, toolchain, -exact command, and sanitized evidence when reporting a problem to the chosen maintainer. +Rover is published at as a pre-1.0 alpha. +The `v0.0.1` GitHub release is a source-only tag: it has no binary assets, and +there is no package-manager listing and no support SLA. Build from a clone as +described in the [README](README.md). `rover help` and [docs/CLI.md](docs/CLI.md) +describe the implemented command surface. -Do not infer `go install github.com/GrayCodeAI/rover/...@latest` availability from the -module path. GitHub publication is a separate step in the owner's chosen repository. +Report bugs and ask questions in +[GitHub issues](https://github.com/GrayCodeAI/rover/issues). Include platform, +toolchain, exact command, and sanitized evidence. For anything else, email +hello@graycodeai.com. Security problems go through [SECURITY.md](SECURITY.md), +never a public issue. + +`go install github.com/GrayCodeAI/rover/cmd/rover@...` is not a documented or +tested install path. The build needs cgo and system SQLite, and `make build` +injects the commit into the version metadata. From cf462401bcc346501d7ffc4f08d28ceeabda0b90 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:53:03 +0530 Subject: [PATCH 14/23] fix(manifest): name the paths that make SOURCE_MANIFEST.json stale `make manifest-check` only printed "SOURCE_MANIFEST.json is stale", so a contributor had to diff 400+ entries by hand to learn that, say, only CHANGELOG.md drifted. --check now lists up to 20 missing, no-longer- tracked and changed paths plus any version change, and reports a missing or unparsable manifest separately. manifest-check runs the new unit tests first. Refs: F063 Co-Authored-By: Claude Opus 5.5 --- Makefile | 1 + scripts/generate_source_manifest.py | 34 ++++++++++++++++++- scripts/test_generate_source_manifest.py | 43 ++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 scripts/test_generate_source_manifest.py diff --git a/Makefile b/Makefile index d3fa899..afc73ce 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,7 @@ manifest: $(PYTHON) scripts/generate_source_manifest.py manifest-check: + $(PYTHON) -m unittest discover -s scripts -p 'test_generate_source_manifest.py' $(PYTHON) scripts/generate_source_manifest.py --check test: diff --git a/scripts/generate_source_manifest.py b/scripts/generate_source_manifest.py index ab13505..c28510c 100644 --- a/scripts/generate_source_manifest.py +++ b/scripts/generate_source_manifest.py @@ -57,6 +57,32 @@ def manifest_bytes(root): return (json.dumps(manifest, indent=2, ensure_ascii=False) + "\n").encode("utf-8") +def manifest_differences(expected, actual, limit=20): + """Describe how the committed manifest bytes differ from the regenerated ones.""" + try: + want = json.loads(expected) + have = json.loads(actual) + want_files = {row["path"]: row for row in want["files"]} + have_files = {row["path"]: row for row in have["files"]} + except (ValueError, KeyError, TypeError): + return ["SOURCE_MANIFEST.json is not a valid manifest"] + lines = [] + if want.get("version") != have.get("version"): + lines.append(f"version: {have.get('version')!r} -> {want.get('version')!r}") + for path in sorted(want_files.keys() - have_files.keys()): + lines.append(f"missing: {path}") + for path in sorted(have_files.keys() - want_files.keys()): + lines.append(f"no longer tracked: {path}") + for path in sorted(want_files.keys() & have_files.keys()): + if want_files[path] != have_files[path]: + lines.append(f"changed: {path}") + if not lines and expected != actual: + lines.append("formatting differs") + if len(lines) > limit: + lines = lines[:limit] + [f"... and {len(lines) - limit} more"] + return lines + + def write_manifest(root, data): fd, temporary = tempfile.mkstemp(prefix=".SOURCE_MANIFEST.", dir=root) try: @@ -80,8 +106,14 @@ def main(): expected = manifest_bytes(root) target = root / "SOURCE_MANIFEST.json" if args.check: - if not target.is_file() or target.read_bytes() != expected: + if not target.is_file(): + print("SOURCE_MANIFEST.json is missing; run make manifest", file=sys.stderr) + return 1 + actual = target.read_bytes() + if actual != expected: print("SOURCE_MANIFEST.json is stale; run make manifest", file=sys.stderr) + for line in manifest_differences(expected, actual): + print(f" {line}", file=sys.stderr) return 1 return 0 write_manifest(root, expected) diff --git a/scripts/test_generate_source_manifest.py b/scripts/test_generate_source_manifest.py new file mode 100644 index 0000000..eb44ea8 --- /dev/null +++ b/scripts/test_generate_source_manifest.py @@ -0,0 +1,43 @@ +"""Unit tests for the source-manifest staleness report.""" + +import json +import unittest + +from generate_source_manifest import manifest_differences + + +def manifest(version, *files): + rows = [{"path": path, "sha256": digest, "size": size} for path, digest, size in files] + return (json.dumps({"version": version, "scope": "s", "files": rows}, indent=2) + "\n").encode() + + +class ManifestDifferenceTests(unittest.TestCase): + def test_reports_changed_missing_and_untracked_paths(self): + expected = manifest("0.0.1", ("a", "1" * 64, 1), ("b", "2" * 64, 2), ("new", "3" * 64, 3)) + actual = manifest("0.0.1", ("a", "1" * 64, 1), ("b", "9" * 64, 2), ("gone", "4" * 64, 4)) + self.assertEqual( + manifest_differences(expected, actual), + ["missing: new", "no longer tracked: gone", "changed: b"], + ) + + def test_reports_version_change(self): + self.assertEqual( + manifest_differences(manifest("0.0.2"), manifest("0.0.1")), + ["version: '0.0.1' -> '0.0.2'"], + ) + + def test_caps_the_report(self): + expected = manifest("0.0.1", *[(f"p{i:02}", "1" * 64, 1) for i in range(30)]) + lines = manifest_differences(expected, manifest("0.0.1"), limit=5) + self.assertEqual(len(lines), 6) + self.assertEqual(lines[-1], "... and 25 more") + + def test_invalid_committed_manifest(self): + self.assertEqual( + manifest_differences(manifest("0.0.1"), b"{not json"), + ["SOURCE_MANIFEST.json is not a valid manifest"], + ) + + +if __name__ == "__main__": + unittest.main() From 3f369f07042cd7a0d5a2a5739951426bc8225583 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:53:26 +0530 Subject: [PATCH 15/23] docs: document the Rust toolchain and gates for contributors CI now enforces `make rust-check` on Rust 1.88.0 (fmt, clippy pedantic lints as errors, tests, license audit), but CONTRIBUTING, ROADMAP and AGENTS.md never mentioned Rust, cargo or rustup, and CONTRIBUTING still said CI definitions were not evidence of any hosted run. CONTRIBUTING lists the toolchain install command, when to run rust-check and rust-notices, and the `make manifest` step that CI enforces. ROADMAP gains a Rust port section pointing to the parity plan and ADR index. AGENTS.md tells coding agents which gates to run and that the Rust preview is unreleased. Refs: F072, F075 Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 6 ++++-- CONTRIBUTING.md | 28 ++++++++++++++++++++-------- ROADMAP.md | 8 ++++++++ 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 21e2e4d..8fc1c9f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,8 +5,10 @@ agent-neutral full-platform scope. Do not describe the design reference as shipp Before changing code, identify the relevant test and domain owner. Add regression tests for acceptance/security changes. Run make check, make race, and make demo in an appropriate -owned environment. Report exact commands and limitations. Do not invent passing CI runs, -live provider tests, code-coverage metrics, or security certification. +owned environment; for Rust changes also run make rust-check CARGO='cargo +1.88.0'. Run +make manifest after changing tracked files. Report exact commands and limitations. Do not +invent passing CI runs, live provider tests, code-coverage metrics, or security certification. +The Go binary is the product; the Rust preview (crates/, binary rover-rs) is unreleased. Do not weaken tests or required policy to make checks green. Keep schema changes explicit. Never copy model assertions into evidence as observed facts. No automatic publication, diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2dd6ddb..e7171f6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,14 +5,26 @@ terminal-first ten-layer scope, but do not claim unimplemented integrations work ## Local workflow -1. Build with Go, a C compiler, system SQLite development headers, and Git. -2. Run `make check`, `make race`, and `make demo` on owned fixtures. -3. Add a focused regression test before changing acceptance semantics. -4. Update STATUS and the acceptance mapping when a capability's guarantees change. -5. Inspect the diff for source/state/credential leakage and unchecked error paths. - -CI definitions in this bundle are not evidence of a successful hosted run. Provide the -actual command, platform, toolchain, result, and limitations in a contribution. +1. Build the Go product with Go 1.26.6 or newer, a C compiler, system SQLite + development headers, and Git. +2. Run `make check`, `make race`, `make version-check`, and `make demo` on owned + fixtures. +3. Run `make manifest` after adding, removing, or editing tracked files, then + `make manifest-check`. CI fails when `SOURCE_MANIFEST.json` is stale. +4. When a change touches the Rust port (`crates/`, `Cargo.toml`, `Cargo.lock`, + `licenses/`, or `scripts/rust_*`), install the pinned toolchain with + `rustup toolchain install 1.88.0 --profile minimal --component clippy --component rustfmt` + and run `make rust-check CARGO='cargo +1.88.0'`. It runs `cargo fmt`, clippy + with the workspace's pedantic lints as errors, the workspace tests, the script + unit tests, and the locked license/notice audit. After a reviewed dependency + change, `make rust-notices CARGO='cargo +1.88.0'` refreshes the bundled notices. +5. Add a focused regression test before changing acceptance semantics. +6. Update STATUS and the acceptance mapping when a capability's guarantees change. +7. Inspect the diff for source/state/credential leakage and unchecked error paths. + +A green hosted CI run ([docs/CI.md](docs/CI.md)) is necessary but not sufficient. +Provide the actual command, platform, toolchain, result, and limitations in a +contribution. ## Design discipline diff --git a/ROADMAP.md b/ROADMAP.md index 795b18b..329d3e4 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -10,6 +10,14 @@ bounded repair; dependency scheduling and verified source integration; context/n scoped MCP and remote CLI; structured verification/counterfactual/mutation; evidence, backup/signatures; offline check-order experiments with explicit promotion. +## Rust port (in progress, unreleased) + +Rover is being ported to Rust with CLI/TUI as the only product surfaces. The +preview binary is `rover-rs` ([docs/RUST_PREVIEW.md](docs/RUST_PREVIEW.md)). Go +remains the product until every parity task and removal gate in +[the Rust parity plan](docs/design/RUST_PARITY_PLAN.md) passes. Decisions are +recorded in the ADRs indexed by [docs/design/README.md](docs/design/README.md). + ## Required before a production claim 1. Supported-current toolchain and target-OS matrix, live provider compatibility. From 4ddd5a8c39ade5ecc7f9bcb72404dabb67748472 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:54:08 +0530 Subject: [PATCH 16/23] docs(design): index current Rust ADRs apart from the historical plan docs/design/README.md was titled "Historical design reference" and told readers not to treat anything in the folder as current, yet the folder now holds 27 accepted Rust ADRs and the active parity plan. It is now an index: current decisions and plans (with what each covers and a note that ADR commands run as `rover-rs` until cutover) and, separately, the historical ten-layer master plan. The Rust preview page also says the Go state importer is a store API, not yet a CLI command. Refs: F075 Co-Authored-By: Claude Opus 5.5 --- docs/RUST_PREVIEW.md | 3 +- docs/design/README.md | 69 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/docs/RUST_PREVIEW.md b/docs/RUST_PREVIEW.md index 5d767cc..2b8a52d 100644 --- a/docs/RUST_PREVIEW.md +++ b/docs/RUST_PREVIEW.md @@ -37,7 +37,8 @@ Contributor gates are `make rust-check CARGO='cargo +1.88.0'` and The Rust preview keeps its own state, separate from the Go product: `--state PATH`, else `$ROVER_RUST_HOME`, else `$XDG_STATE_HOME/rover-rust`, else `~/.local/state/rover-rust`. The Go product uses `--state`, `$ROVER_HOME` -or `~/.config/rover`. An opt-in importer copies Go state into a Rust state root: +or `~/.config/rover`. An opt-in importer (a store API; there is no CLI command +for it yet) copies Go state into a separate Rust state root: it keeps the source tree, requires explicit operator assertions, excludes runtime task/check trees, and sanitizes grants and execution process state. Repeat imports verify copied rows, events, request keys, receipt counts, and object diff --git a/docs/design/README.md b/docs/design/README.md index 847dc50..61bbe90 100644 --- a/docs/design/README.md +++ b/docs/design/README.md @@ -1,6 +1,65 @@ -# Historical design reference +# Design reference -The master plan is copied from the supplied design draft. It describes the desired -complete ten-layer system, not features implemented in this alpha. STATUS.md and the -actual command/tests are authoritative for current functionality. Do not interpret its -future commands, roadmap, or proposed acceptance specifications as released support. +This folder holds two kinds of material. Check which kind a file is before +relying on it. [STATUS.md](../../STATUS.md), the actual commands, and the tests +are authoritative for what Rover does today. + +## Current decisions and plans (Rust port, in progress) + +These documents govern the unreleased Rust port. Its preview binary is +`rover-rs` ([docs/RUST_PREVIEW.md](../RUST_PREVIEW.md)). Commands the ADRs write as +`rover tui` or `rover agent session ...` run as `rover-rs ...` until the cutover +in parity task P-164. An accepted ADR records a decision; it does not mean the +feature ships in a Rover release. + +- [RUST_PARITY_PLAN.md](RUST_PARITY_PLAN.md): the staged port backlog, + verification ledger, and the gates that must pass before Go is removed. +- [GO_RUST_STATE_MIGRATION.md](GO_RUST_STATE_MIGRATION.md): the opt-in Go-to-Rust + state import (a store API, not yet a CLI command), source retention, and + rollback. +- [RUST_COMPATIBILITY_BASELINE.md](RUST_COMPATIBILITY_BASELINE.md): an index of + the Go command families and behavior the port must match. +- [RUST_DEPENDENCY_AUDIT.md](RUST_DEPENDENCY_AUDIT.md): the dated Rust dependency + review. `make rust-deps-check` enforces the locked licenses and bundled notices. +- [UPSTREAM_FEATURE_MATRIX.md](UPSTREAM_FEATURE_MATRIX.md) and + [UPSTREAM_SOURCE_AUDIT.md](UPSTREAM_SOURCE_AUDIT.md): Herdr, Luvus, and Orca + ADE behavior references, pinned revisions, and exact-file reuse audits. + +| ADR | Decision | +|---|---| +| [ADR-0001](ADR-0001-rust-tui-product-boundary.md) | Rust CLI/TUI product boundary | +| [ADR-0002](ADR-0002-rust-workspace-architecture.md) | Rust workspace and process ownership | +| [ADR-0003](ADR-0003-core-identifiers-digests-and-time.md) | Core identifiers, digests, and timestamps | +| [ADR-0004](ADR-0004-rust-sqlite-adapter.md) | Rust SQLite adapter | +| [ADR-0005](ADR-0005-rust-record-json.md) | JSON record encoding for Rust storage | +| [ADR-0006](ADR-0006-stack-safe-json.md) | Stack-safe deep JSON for Rust storage | +| [ADR-0007](ADR-0007-rust-rooted-file-operations.md) | Rooted file operations for Rust storage | +| [ADR-0008](ADR-0008-rust-project-identity.md) | Canonical repository identity and project scope | +| [ADR-0009](ADR-0009-rust-process-execution.md) | Rust local process execution | +| [ADR-0010](ADR-0010-rust-task-supervisor-state.md) | Rust task supervisor state transitions | +| [ADR-0011](ADR-0011-rust-pty-backend.md) | Rust native PTY backend | +| [ADR-0012](ADR-0012-rust-terminal-emulator.md) | Rust terminal screen core | +| [ADR-0013](ADR-0013-rust-session-broker.md) | Rust local terminal session broker | +| [ADR-0014](ADR-0014-rust-workspace-layout-model.md) | Rust terminal workspace layout model | +| [ADR-0015](ADR-0015-rust-tui-framework.md) | Rust TUI framework and terminal backend | +| [ADR-0016](ADR-0016-rust-task-briefing-drafts.md) | Repository-bound Rust task briefing drafts | +| [ADR-0017](ADR-0017-rust-task-brief-contract.md) | Rust task brief and dependency graph contract | +| [ADR-0018](ADR-0018-rust-task-plan-storage.md) | Rust project-bound task-plan storage | +| [ADR-0019](ADR-0019-rust-agent-profile-registry.md) | Rust agent profile registry | +| [ADR-0020](ADR-0020-native-agent-transcripts.md) | Native agent transcript parsing | +| [ADR-0021](ADR-0021-agent-detection-evidence.md) | Agent detection from explicit process and screen evidence | +| [ADR-0022](ADR-0022-agent-state-rollups.md) | Agent state rollups | +| [ADR-0023](ADR-0023-agent-lifecycle-authority.md) | Sequenced agent lifecycle authority | +| [ADR-0024](ADR-0024-agent-session-identity-and-resume.md) | Exact agent session identity and explicit resume | +| [ADR-0025](ADR-0025-agent-session-inventory-cli.md) | Project-scoped agent session inventory | +| [ADR-0026](ADR-0026-owned-pty-process-sampling.md) | Sampling foreground processes from owned PTYs | +| [ADR-0027](ADR-0027-herdr-compatible-screen-manifests.md) | Herdr-compatible screen manifest evaluation | + +Each ADR's own "Status" line gives the exact scope that was accepted. + +## Historical reference + +- [Rover_Ten_Layer_Master_Plan.md](Rover_Ten_Layer_Master_Plan.md) is copied from + the supplied design draft. It describes the desired complete ten-layer system, + not features implemented in this alpha. Do not read its future commands, + roadmap, or proposed acceptance specifications as released support. From 079eaa3c5196a64065c39d872153a90286145488 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:54:50 +0530 Subject: [PATCH 17/23] docs: mark research notes that cite the removed SDKs as historical Three tracked research notes cited sdk/ paths, rover_client and passing `make sdk-test` runs as current evidence with no marker, so their relative links were dead and they contradicted the SDK removal. Each now opens with a historical-snapshot banner, research_notes/README.md explains how to read all seven dated notes, and the report's command block labels `make sdk-test` historical instead of current. Refs: F068 Co-Authored-By: Claude Opus 5.5 --- reports/Rover OSS comparison 2026.md | 2 +- research_notes/README.md | 16 ++++++++++++++++ .../rover_code_architecture.md | 6 ++++++ .../rover_roadmap_traceability.md | 6 ++++++ .../rover_security_tests.md | 6 ++++++ 5 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 research_notes/README.md diff --git a/reports/Rover OSS comparison 2026.md b/reports/Rover OSS comparison 2026.md index 94eb311..e12bb73 100644 --- a/reports/Rover OSS comparison 2026.md +++ b/reports/Rover OSS comparison 2026.md @@ -352,7 +352,7 @@ make fuzz-all # proposed: all current and added fuzz targets make schema-check # proposed: every schema/example pair make demo # current: 12 owned-fixture scenarios make demo-extended # current: 16 owned-fixture scenarios -make sdk-test # current: Python, TypeScript, Go SDKs +make sdk-test # historical: removed with the SDKs make cross-build # current: must become explicit and functional per target make sbom # current: must emit standard SPDX or CycloneDX make vulncheck # current: must use a pinned scanner and patched toolchain diff --git a/research_notes/README.md b/research_notes/README.md new file mode 100644 index 0000000..a793ce7 --- /dev/null +++ b/research_notes/README.md @@ -0,0 +1,16 @@ +# Research notes (historical) + +These notes are dated research snapshots, not descriptions of current Rover +behavior. The `Rover OSS comparison 2026/` notes were committed on 2026-09-24 and +feed the summary in `reports/Rover OSS comparison 2026.md`. + +- Citations such as `file:line` and relative links point at the tree as it was on + the research date. Many lines have moved since. +- The Python, TypeScript, and Go SDK source trees were removed after the research + (see `CHANGELOG.md`). `sdk/...` links, `rover_client` references, and + `make sdk-test` results describe code and a make target that no longer exist. +- Third-party project facts (stars, features, licenses) were true when retrieved and + may have changed. + +For current status read `STATUS.md`; for the current plan read +`docs/design/RUST_PARITY_PLAN.md`. diff --git a/research_notes/Rover OSS comparison 2026/rover_code_architecture.md b/research_notes/Rover OSS comparison 2026/rover_code_architecture.md index 534b826..8b5ef32 100644 --- a/research_notes/Rover OSS comparison 2026/rover_code_architecture.md +++ b/research_notes/Rover OSS comparison 2026/rover_code_architecture.md @@ -1,5 +1,11 @@ # Rover code architecture audit +> **Historical snapshot; stale references.** This audit describes the tree as it was when +> written (committed 2026-09-24). The Python, TypeScript, and Go SDK source trees were removed afterwards by +> explicit product direction (see `CHANGELOG.md`). Every `sdk/...` link, `rover_client` +> reference, and `make sdk-test` result below therefore describes code and a make target +> that no longer exist. Other line citations refer to the tree at audit time. + ## Scope, evidence, and audit method ### Takeaway diff --git a/research_notes/Rover OSS comparison 2026/rover_roadmap_traceability.md b/research_notes/Rover OSS comparison 2026/rover_roadmap_traceability.md index 06e5057..71bd8c7 100644 --- a/research_notes/Rover OSS comparison 2026/rover_roadmap_traceability.md +++ b/research_notes/Rover OSS comparison 2026/rover_roadmap_traceability.md @@ -1,5 +1,11 @@ # Rover roadmap and traceability audit +> **Historical snapshot; stale references.** This audit describes the tree as it was when +> written (committed 2026-09-24). The Python, TypeScript, and Go SDK source trees were removed afterwards by +> explicit product direction (see `CHANGELOG.md`). Every `sdk/...` link, `rover_client` +> reference, and `make sdk-test` result below therefore describes code and a make target +> that no longer exist. Other line citations refer to the tree at audit time. + ## Claimed-feature traceability ### Takeaway diff --git a/research_notes/Rover OSS comparison 2026/rover_security_tests.md b/research_notes/Rover OSS comparison 2026/rover_security_tests.md index d453c0f..8eeea7b 100644 --- a/research_notes/Rover OSS comparison 2026/rover_security_tests.md +++ b/research_notes/Rover OSS comparison 2026/rover_security_tests.md @@ -1,5 +1,11 @@ # Rover security and verification audit +> **Historical snapshot; stale references.** This audit describes the tree as it was when +> written (committed 2026-09-24). The Python, TypeScript, and Go SDK source trees were removed afterwards by +> explicit product direction (see `CHANGELOG.md`). Every `sdk/...` link, `rover_client` +> reference, and `make sdk-test` result below therefore describes code and a make target +> that no longer exist. Other line citations refer to the tree at audit time. + Audit scope: read-only review of the current tree at the time of inspection, including all Go packages and tests, the Python/TypeScript/Go SDKs and tests, JSON schemas, CI/release workflows, scripts, security/architecture/status/validation documents, and the retained validation artifacts. Local citations use repository-relative links and literal `file:line` references. The findings distinguish observed behavior from code-review hypotheses; no exploit, certification, live-provider, hosted-CI, or independent-host claim is made. ## Security boundaries and concrete findings From 199c958ad61055a54270ed5ce0ed13865e788b88 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:55:07 +0530 Subject: [PATCH 18/23] docs: drop the removed Python client from the architecture diagram docs/ARCHITECTURE.md still listed a "Python CLI client" among Rover's interfaces after the SDK trees were deleted; the diagram now shows the remote CLI client that does exist. docs/SOURCES.md described the tree as an upgrade of 0.1.0-alpha.1 without saying that line was renumbered to the public 0.0.1 release. Refs: F276 Co-Authored-By: Claude Opus 5.5 --- docs/ARCHITECTURE.md | 2 +- docs/SOURCES.md | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index c3fcd7b..cfea028 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -5,7 +5,7 @@ They are not ten independent services. Public commands and protocol tools call t same task, snapshot, assurance and storage implementations. ```text -CLI / keyboard TUI / Python CLI client / MCP clients +CLI / keyboard TUI / remote CLI / MCP clients | local operator or scoped grant | diff --git a/docs/SOURCES.md b/docs/SOURCES.md index 273a9e2..e3c812c 100644 --- a/docs/SOURCES.md +++ b/docs/SOURCES.md @@ -1,7 +1,9 @@ # Implementation references and provenance -This upgrade builds on the delivered Rover 0.1.0-alpha.1 repository. The ten-layer -master plan remains a design target, not a claim of implemented/validated coverage. +These references were consulted while Rover grew from the earlier local +0.1.0-alpha.1 delivery through the 0.2.0-alpha.1 upgrade, which was renumbered as +the public 0.0.1 release. The ten-layer master plan remains a design target, not a +claim of implemented/validated coverage. Primary interface references consulted during implementation: From bbd27e576c3eaba62e8fac0f199956bc33aef77c Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:55:20 +0530 Subject: [PATCH 19/23] docs(changelog): record the Rust preview, CI, provenance and docs changes Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fd1ab9..f97e946 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ ## Unreleased +- Track the Rust port workspace (nine crates, Rust 1.88.0 MSRV) as an + unreleased preview. Its executable is `rover-rs`, not `rover`, so it cannot + shadow the Go product binary, and `rover-rs --version` labels it a preview. + Build and usage notes are in `docs/RUST_PREVIEW.md`. +- CI runs the Go product gates and the Rust preview gates as independent jobs. + The Rust job is cached and keeps its CycloneDX SBOM as a run artifact. +- The release workflow attests build provenance (GitHub artifact attestations, + keyless Sigstore signing) for every checksummed asset; verify with + `gh attestation verify`. +- `make manifest-check` names the paths that make `SOURCE_MANIFEST.json` stale. + The Rust dependency audit runs with the same `CARGO` as the Makefile. + `.gitattributes` keeps the hash-verified license notices byte-exact. +- README, STATUS, SECURITY, SUPPORT, and GOVERNANCE now describe the public + repository, hosted CI, the source-only `v0.0.1` release, and one platform + matrix. `docs/validation/v0.0.1/REPORT.md` records the tag's hosted CI run. - Remove the Python, TypeScript, and Go SDK source trees and their CI/Make targets per product direction. Historical validation records remain dated. - `ProcessGroupIdentity` gains a `conflicting` field listing every equally From f31320b289068ac1d1d43210683e6e638fe3ea30 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 04:55:32 +0530 Subject: [PATCH 20/23] chore(manifest): regenerate SOURCE_MANIFEST.json for the tracked tree `make manifest-check` (run by CI and the release workflow) failed on main because CHANGELOG.md had changed after the last regeneration, and it would have failed again once the Rust workspace, notices and ADRs were tracked. Regenerated with `make manifest`: 628 entries (62 crates/, 320 licenses/, 35 docs/design/, no sdk/), version 0.0.1. Refs: F062, F063 Co-Authored-By: Claude Opus 5.5 --- SOURCE_MANIFEST.json | 2276 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 2173 insertions(+), 103 deletions(-) diff --git a/SOURCE_MANIFEST.json b/SOURCE_MANIFEST.json index aa9342d..b32aaf7 100644 --- a/SOURCE_MANIFEST.json +++ b/SOURCE_MANIFEST.json @@ -2,6 +2,16 @@ "version": "0.0.1", "scope": "Source package inventory; inventory itself excluded", "files": [ + { + "path": ".cargo/config.toml", + "sha256": "9b0f881a9102c49253057fe023459709aaf537c02fdcab0b9b1c7dca144e295f", + "size": 42 + }, + { + "path": ".gitattributes", + "sha256": "e0422a0e58957d6f2b06bb8ef4faf86269691ece7553c93e8b97fabb298ef229", + "size": 271 + }, { "path": ".github/ISSUE_TEMPLATE/bug_report.md", "sha256": "51295bb1d022ed359c39797a90c6b9cebe8296ab97ef1619384ddf7503beb314", @@ -19,18 +29,18 @@ }, { "path": ".github/workflows/ci.yml", - "sha256": "24924a905192c1364e851e28495414eee9f935590defc9877b8d504dc78dab67", - "size": 1759 + "sha256": "0c5861f174da1a427f5dc48018d8ed17525217f8ecfb8b7f698777d3d90fce6e", + "size": 3193 }, { "path": ".github/workflows/release.yml", - "sha256": "cd65ab0caa2c160bb802a72c5f1bb1976e588a8a516ee5287038e9817fd93341", - "size": 2971 + "sha256": "8fa9e51c110ac10d909848932dc42f4f062fbad1deea24f9404ec5607a9e3ca0", + "size": 3497 }, { "path": ".gitignore", - "sha256": "5d06110436f755f88aa3b36502724a08e6063c9889ab179a5c7f16431780b1f2", - "size": 479 + "sha256": "8bfb57623d01ee0942e654eba70e08f8ce39d1b2fdf96a993caf6cfe14fa636a", + "size": 488 }, { "path": ".rover/config.json", @@ -39,13 +49,13 @@ }, { "path": "AGENTS.md", - "sha256": "e9f771d1cd6b9e1b24665ea9d72984cb1302bb25120c81d40e5c18c0dce1922d", - "size": 1013 + "sha256": "ea86c888e87cc2fa6dcef8362cbe74080de0d5e0db6fad057491865591b6462e", + "size": 1215 }, { "path": "CHANGELOG.md", - "sha256": "1b40aa2d89da8c9cf6d386f2498c8ddfc08803f8dac45dd4ca012ab5cd7d1ba9", - "size": 1753 + "sha256": "66777030c875061f02fe990550302106f6993ff7efb51a82f0c43ad7173231b6", + "size": 3919 }, { "path": "CODE_OF_CONDUCT.md", @@ -54,13 +64,23 @@ }, { "path": "CONTRIBUTING.md", - "sha256": "122bbc4ecfea758d5063419d0d43fd2f993b5baefb8591708939a9e1f67402f1", - "size": 1938 + "sha256": "f2da9a0e2620fcad13a34a28292d03931523ca253c9f621baecd7f7b90b21a18", + "size": 2726 + }, + { + "path": "Cargo.lock", + "sha256": "df780e7fc10ff65e62d5408b37ef57085f2c1fedcb78bf7218e6483bd8cf5d87", + "size": 41237 + }, + { + "path": "Cargo.toml", + "sha256": "f18382a28a71fcc43cc41d1bff900fe4c98526e7e0f63666d6c58420cccaf582", + "size": 457 }, { "path": "GOVERNANCE.md", - "sha256": "430f0175a2782d598185272cf6c510b764a7457ebb3b7fe377d462a15010b747", - "size": 808 + "sha256": "1cb213452777b6a211b0e67c70031f7cc04182528619ea25b212c1c60c2db215", + "size": 878 }, { "path": "LICENSE", @@ -69,8 +89,8 @@ }, { "path": "Makefile", - "sha256": "eab7156094aa0c1a8696c249966aaeda3070cbefffa94a94eefe9e81f41754b5", - "size": 3568 + "sha256": "eb854f388b46ffc4b43e00e9c9c09157e046f3cceafb0374bc034509070c533d", + "size": 4377 }, { "path": "NOTICE", @@ -79,28 +99,28 @@ }, { "path": "README.md", - "sha256": "44883678fd90dd0fb9464341f670e9e69a523d9c89523db30854197ed181d981", - "size": 10188 + "sha256": "a8f49586321e94c930b28d7f2a83fb4480ae7ed59e15d9549bcf906e02232e48", + "size": 12166 }, { "path": "ROADMAP.md", - "sha256": "2a60933f842664c2a0cc7c20158280076dd6fb98403a17f94122ec0811e4eceb", - "size": 1571 + "sha256": "ea52ae97b340f2c8cfbfd6b7452d5150a962d4943875d468b755110367060d9e", + "size": 1991 }, { "path": "SECURITY.md", - "sha256": "7b65ddea455138fc0f87f348b6174386697f6739e492aa87a397a68d4e5eb207", - "size": 888 + "sha256": "ff6c8496b488e060ac831ae6db9f9e003c6e54f85bcbcd6b68fd6ea40fdfc931", + "size": 1430 }, { "path": "STATUS.md", - "sha256": "5ec93b9ec343c04096498d13ca49205f3f2afde86741d2f0e71c84e08c8ef579", - "size": 18647 + "sha256": "fff393c52ca30f601b6539d2ce1178eb330862c2c4192cd5373554bf0c45597b", + "size": 24460 }, { "path": "SUPPORT.md", - "sha256": "898da4e1fcff7694a40eae1a1d8b2a63e9c9ff4a5f47b21f9aca9ec0d1c72952", - "size": 562 + "sha256": "652b980c89fa18f5be642275d30ebf7f20d6b757baf150e13abfe8d4e37c1ee2", + "size": 886 }, { "path": "VERSION", @@ -112,6 +132,316 @@ "sha256": "8c1c93e6f2e112e386f7178f56c3a179724a582cc0485b6e1a78ecebb85dc2d6", "size": 358 }, + { + "path": "crates/rover-access/Cargo.toml", + "sha256": "0d7b29f9499872e82a65161c54237749e0b62a397cc34ef4374e6f94ea7739d0", + "size": 722 + }, + { + "path": "crates/rover-access/src/lib.rs", + "sha256": "b70ee8dbcd91e7380eafb0e92d61e34ba6e254b2e544fb798e21c4db388f7ff2", + "size": 15099 + }, + { + "path": "crates/rover-agents/Cargo.toml", + "sha256": "3bb4dfd71a4c0a81e6e38b227a4cd5f8368a213b73b5a8734a84d02cba0ac7fe", + "size": 629 + }, + { + "path": "crates/rover-agents/src/detect.rs", + "sha256": "1616297f730118703c96798cf0bf129681e338cd5524d366451bab343e0a0c2e", + "size": 22389 + }, + { + "path": "crates/rover-agents/src/herdr_manifest.rs", + "sha256": "976678b38e5a07dffcc011704a69e5d13b5464f4ebee38c79dd2c67f262b0cd9", + "size": 37768 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/amp.toml", + "sha256": "b5806b0dd21e2f5e752d0eac7f084d5ce2e3f275638cfd1234c42cc88152dc8c", + "size": 1623 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/antigravity.toml", + "sha256": "11300b853130d037eb2c57d9c4b897893cc1f0a876e12eb54ff1b216177db9d7", + "size": 760 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/claude.toml", + "sha256": "038d0aa23fee3f9b39cb3c9ca117d0f95b0b3a5873cf0f38284ccbac279c9664", + "size": 6726 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/cline.toml", + "sha256": "75fe33ec735c59638da8d62e16bddd257d9959e692edb83b1f11b7f28057866a", + "size": 1527 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/codex.toml", + "sha256": "aba3d44a0f7177d5dd9fd60002a8efd11ef6e1a589bb3992c02563ed3e0de5e0", + "size": 3309 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/cursor.toml", + "sha256": "753b1f7f632d42fa21139c2767ecbb5e1078748aba2e59407ac4932d3ce36ad7", + "size": 1462 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/devin.toml", + "sha256": "250c9cea1d60bdb965dc6056f3066b785e941d60242756fbaca73b57ca6b0f85", + "size": 2016 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/droid.toml", + "sha256": "d37e7c464177c0e8f3edce8d4fabc4bcc7a1874edf2c4c87a2f927888cf69ce9", + "size": 1044 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/gemini.toml", + "sha256": "d7013b5e772852ecc595febf964f00b4f9edcbc6047a2a5421613a154d92520d", + "size": 700 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/github-copilot.toml", + "sha256": "b70c652584326a1a98475a5fcef16207dee9f23b65f8e78300f4fc2bb578cb11", + "size": 1010 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/grok.toml", + "sha256": "4298dd2ea7d124f3ba5d72b649bbb4fb6edc533d7e6e546d523a8214fa31456d", + "size": 5401 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/hermes.toml", + "sha256": "533d21b65dea3a0c60c25d0475c9c900d28a5712b6392669a7788f75de2b6e85", + "size": 2620 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/kilo.toml", + "sha256": "70f0ba4e58bc141fe69d7024013f973cd16e8616393079318914d70afeefef3b", + "size": 694 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/kimi.toml", + "sha256": "ede08c0d2d5024f7606dc0a1b2f7a9c6a0ebb99f3b6c58ca6757049856d06e05", + "size": 1941 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/kiro.toml", + "sha256": "c8990f3c9d4810995be97e8df29836411e37d90f6ac9d9303f505787b504806b", + "size": 2861 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/letta.toml", + "sha256": "205b8c135584c86f9f529aa2d061109b5129085cc5c3124c6dbfe0e78fcdba43", + "size": 2328 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/maki.toml", + "sha256": "3b392170ee3082051266509f575a4640bde8d693b2f37ecf52157a641bc75b28", + "size": 1899 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/muse.toml", + "sha256": "b69c4d87fa9c19e3e6453b706fbe39c98a8b33ffbaa48e8cd5ae6751e9615074", + "size": 3727 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/opencode.toml", + "sha256": "faa82aed2d76ad856528caae04232894594b74cc903aed3e522b3e725c5f2c95", + "size": 1008 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/pi.toml", + "sha256": "57469b82e4239bf93559ed5d400c9d9f86a9e64d5def728b7d4bb398be7659ca", + "size": 456 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/qodercli.toml", + "sha256": "2089f70fc78c6576fd7128a00f4e7eedb85be81bde9ed73301b3b88000048961", + "size": 1110 + }, + { + "path": "crates/rover-agents/src/herdr_manifests/qwen.toml", + "sha256": "b27aa456af228e8a4ceac74f0dd431c33b21473b69314fc672a7fba474e2a7fe", + "size": 3409 + }, + { + "path": "crates/rover-agents/src/lib.rs", + "sha256": "0d025796fb92667ab5d6a2a504e7b912484420d0b0718a48fa16d26457e8938a", + "size": 13758 + }, + { + "path": "crates/rover-agents/src/lifecycle.rs", + "sha256": "2a39130471a6c57a3f27e1254b4851ce728c34a096bd51e7b06130e48ac62b1d", + "size": 9187 + }, + { + "path": "crates/rover-agents/src/process_identity.rs", + "sha256": "ac12c57b3e6a9dd65f30918ad5e79742db88060a09927ea2b78fc7ba73d9b106", + "size": 20545 + }, + { + "path": "crates/rover-agents/src/rollup.rs", + "sha256": "a039088a3a80b685529c6a92f176a36aff67f28abb3424691ac0ec4bbd2ef110", + "size": 11138 + }, + { + "path": "crates/rover-agents/src/sessions.rs", + "sha256": "78696140562adf0513efb9655ac386a9dbd4f133acf9b683a8eff751c7f95b19", + "size": 23890 + }, + { + "path": "crates/rover-agents/src/transcript.rs", + "sha256": "10d25cd5da156fb881384d0e515968284f3a1d3019264f83ee9757ed2c58f263", + "size": 14851 + }, + { + "path": "crates/rover-cli/Cargo.toml", + "sha256": "b98e728031a2cc06eea540eae4e4b440878b303fa90bc9c4ba6b46c8a968c957", + "size": 861 + }, + { + "path": "crates/rover-cli/src/main.rs", + "sha256": "b70e2881cba3dde6e6765b15b1a5fa6373995f101da0f207ff41eb83e512b19b", + "size": 101306 + }, + { + "path": "crates/rover-cli/tests/cli_identity.rs", + "sha256": "01fde959b53bfba1cf57d06bed2139a5128f63a51e49c946d9e0bd00a6a18611", + "size": 1311 + }, + { + "path": "crates/rover-cli/tests/session_daemon.rs", + "sha256": "77bb52b3ce44afada1983ef31b2f0603f6be1d6ef9a7131688e2bcdd6b228ab2", + "size": 3399 + }, + { + "path": "crates/rover-core/Cargo.toml", + "sha256": "134eb516a686a694fef74fbbb2cbe97dfb839d09efd40a982183386525a7e934", + "size": 449 + }, + { + "path": "crates/rover-core/src/lib.rs", + "sha256": "dd54c1ddb494ba13a46b0bf8b1239e9e60663889b4b0ebebb4d17f8a33b0a10b", + "size": 16958 + }, + { + "path": "crates/rover-core/src/repository.rs", + "sha256": "5015f9cf07ade1777b3f8a0f4d6f3afc14937f9af59671d5ae9db89753071674", + "size": 8953 + }, + { + "path": "crates/rover-core/src/task.rs", + "sha256": "0f2c6aec673542c55d11382e48bda65efd5a1cee33f06c30f6c87df05f84fabf", + "size": 13677 + }, + { + "path": "crates/rover-execution/Cargo.toml", + "sha256": "16a87ea67fa0718f7e4c2787976e7731a03ce7dd330490a291d1e617d4788451", + "size": 982 + }, + { + "path": "crates/rover-execution/src/layout.rs", + "sha256": "207d81ce321bc6720831ae60f15dcf09ca642123c153860b33bf2bf08f101f5f", + "size": 37045 + }, + { + "path": "crates/rover-execution/src/lib.rs", + "sha256": "17b399850606bcb04684a58ff0b8f0e7e840356f56e57600251da385dd4f26fa", + "size": 34976 + }, + { + "path": "crates/rover-execution/src/pty.rs", + "sha256": "5e232ae5b8d9ea88d4d79277e254e9ff46d9fbcca98c63b4f7d302b03f642817", + "size": 28648 + }, + { + "path": "crates/rover-execution/src/sessions.rs", + "sha256": "8b2429ed4d77b140f56ae0e65fecb315238106a0b657e760cadaeb95ab206883", + "size": 43375 + }, + { + "path": "crates/rover-execution/src/supervisor.rs", + "sha256": "99f75e688895277ebf92621de874939bad9c398fe8aa86c331a08a672925fa96", + "size": 52843 + }, + { + "path": "crates/rover-execution/src/terminal.rs", + "sha256": "b0c7b8ecfb0e5ec1a90d87f9fdbdb3eb86636d47ef1ac0edcf576f3a36d38259", + "size": 18171 + }, + { + "path": "crates/rover-source/Cargo.toml", + "sha256": "231f87393af97e07361d4b22fdaf96dd2238ae0f769a3760fb22314ec5ec0cce", + "size": 462 + }, + { + "path": "crates/rover-source/src/file_browser.rs", + "sha256": "daf688ac2e2b4f659ae6502f0246951e966ebf86ad7fa52d2e2f1ab38f36b44f", + "size": 20790 + }, + { + "path": "crates/rover-source/src/lib.rs", + "sha256": "17a13651dca68a426ffefea6606e6d7f6fe521f40f7c80e419b6fd783be63033", + "size": 111043 + }, + { + "path": "crates/rover-store/Cargo.toml", + "sha256": "4e3092b08898b8fd1160ab90a42cba255880a19f33bd5a4cc63ad121db130746", + "size": 670 + }, + { + "path": "crates/rover-store/src/budget.rs", + "sha256": "b8b45fe6f93d999070a7899fa8e645182c6d663debcdf9726f47d8c2eb36fb6f", + "size": 15845 + }, + { + "path": "crates/rover-store/src/files.rs", + "sha256": "d822c2d471b6a75c258689a9307bd117cc6e2fa508baa73fca01f12ca8677f3a", + "size": 66960 + }, + { + "path": "crates/rover-store/src/legacy.rs", + "sha256": "f43cc488b2102e644557f171f10241f576043ce21188e0ea332e5cded7790488", + "size": 31728 + }, + { + "path": "crates/rover-store/src/legacy/import.rs", + "sha256": "ce3300dabbe286e6945a32d5992b9bd6f4784e4150cc1c7d93bd10d46591ab56", + "size": 50962 + }, + { + "path": "crates/rover-store/src/lib.rs", + "sha256": "9c16592d280104ed283d1323632a90dc64b6da63dc516a78563b10a0e9e8607e", + "size": 67899 + }, + { + "path": "crates/rover-store/src/resources.rs", + "sha256": "966ddf7606721a3ec369c97b56c61b883884d4e1f31420a4f4de653ebd5ef65a", + "size": 14178 + }, + { + "path": "crates/rover-tasks/Cargo.toml", + "sha256": "8c8038406488ace5eafe9c1ed18428e167eb39170b3432d67d4b75a2d639e18b", + "size": 531 + }, + { + "path": "crates/rover-tasks/src/lib.rs", + "sha256": "db7b3ac038b651baf688d1d692e25b587ed5168ceaedd77683f2a50b5714a52d", + "size": 29986 + }, + { + "path": "crates/rover-tui/Cargo.toml", + "sha256": "a07e6c2ab45a63ff22dd8b8ffc16f76b433edfd7fe2b9c9f278690467bf6d1f0", + "size": 590 + }, + { + "path": "crates/rover-tui/src/lib.rs", + "sha256": "0be35b424a26c833cd45d343ea3103e8a93cd09d9f55285f7ec27b29c7645771", + "size": 322516 + }, { "path": "docs/AGENTS.md", "sha256": "95f65f34b6dbb17263dded65248edf5078166151f431db3be1e01fdde2092593", @@ -124,13 +454,13 @@ }, { "path": "docs/ARCHITECTURE.md", - "sha256": "b6a2f26a64e1ba6a6824c6964dc7a3e20d5036178895447fdbd9d62482bd744e", - "size": 4498 + "sha256": "40e3fa238bbed2b2c828ecfe4abb59f74f88280a6dad5c128f6b9610c4d91e2f", + "size": 4491 }, { "path": "docs/CI.md", - "sha256": "881c21fe3a0125fd13bf112251416d15c557d0d47bd16b4b065685998a5a114d", - "size": 2896 + "sha256": "b232cb39b6a29d1d6f032851c9a53ec430f9393bd3afe1d966f0d39b33184f8c", + "size": 5386 }, { "path": "docs/CLI.md", @@ -144,18 +474,23 @@ }, { "path": "docs/PLAN_ALL.md", - "sha256": "d295733e3d65f3dd5dd0c754c268e12460ae7c0f87af120154fb3032ef329123", - "size": 8619 + "sha256": "6e24e7f95d26306cbf3ce93d393f89e90d5315a400d76eb5b79ff9660302941b", + "size": 8840 + }, + { + "path": "docs/RUST_PREVIEW.md", + "sha256": "d845fc54420a4fb156e1395d0568f8d36d85189764cb374838684c7f1427a676", + "size": 8813 }, { "path": "docs/SECURITY_MODEL.md", - "sha256": "3886aacea071759d8f9f6b516d7d89635ddac518232693488fb9d018686ecd66", - "size": 3652 + "sha256": "08881bc4d905cedd919db042cd9bd508c726c3a6a9174257f153dae66f567974", + "size": 5666 }, { "path": "docs/SOURCES.md", - "sha256": "a12b7c54daf1546e0953a04293b54d0e070ff4707ff2714f3424e179b964087f", - "size": 1727 + "sha256": "9000875cdcc02113a339c0a56ba5398e6afcb007fbecead426303b5a733e6a94", + "size": 1838 }, { "path": "docs/USAGE.md", @@ -202,16 +537,181 @@ "sha256": "15d073de301b7fec114a393756466017a7cf5fd91c2d7a454df6da532ddfe2e4", "size": 1959 }, + { + "path": "docs/design/ADR-0001-rust-tui-product-boundary.md", + "sha256": "c7d6faab7371ad1fe207248a7586d099f7ea07ff792e9e58e75bcfe8e5168d3b", + "size": 3459 + }, + { + "path": "docs/design/ADR-0002-rust-workspace-architecture.md", + "sha256": "e76cb6db20cb26b774375643a1de4028dca7384acc3ecaf18b954213e9d37738", + "size": 3974 + }, + { + "path": "docs/design/ADR-0003-core-identifiers-digests-and-time.md", + "sha256": "802c1c68736ef13de8483038fcbbda4ead0fa8425a60e10acf6805b8afc94507", + "size": 3203 + }, + { + "path": "docs/design/ADR-0004-rust-sqlite-adapter.md", + "sha256": "1807cc4ec5fbe9e5ab956c582b47b2ad5c516c507ed2ae6e8d2d70f2a561c306", + "size": 4215 + }, + { + "path": "docs/design/ADR-0005-rust-record-json.md", + "sha256": "c45efecead2a7ce0b86819a2d98ffb64601534419ad6c019eb02fb6cc3a3015c", + "size": 3058 + }, + { + "path": "docs/design/ADR-0006-stack-safe-json.md", + "sha256": "b0e866855f13752b1e9323a7a67132940aec0c7760ec1ffe3b2d8e6a841242e7", + "size": 3937 + }, + { + "path": "docs/design/ADR-0007-rust-rooted-file-operations.md", + "sha256": "344498db416086f19d337c024b5b051661830dbf8814803066d9155b710b1098", + "size": 5095 + }, + { + "path": "docs/design/ADR-0008-rust-project-identity.md", + "sha256": "bd17c6fa34891f92e7bd0c95cbadb205f95447e134376002767870797a571030", + "size": 2726 + }, + { + "path": "docs/design/ADR-0009-rust-process-execution.md", + "sha256": "8de552c1ba9cbb6af9f5967cbd620e9a13462fe48b118cb9bc21f83ee06b56ec", + "size": 3472 + }, + { + "path": "docs/design/ADR-0010-rust-task-supervisor-state.md", + "sha256": "36746f65921687aca9b9096559e8482e604e2106849714e922de1366735cce6b", + "size": 4527 + }, + { + "path": "docs/design/ADR-0011-rust-pty-backend.md", + "sha256": "b10147c3e3c4b9fcac08df53ff019ce1cbeccaabd824601bdae7dba89b1c8156", + "size": 2767 + }, + { + "path": "docs/design/ADR-0012-rust-terminal-emulator.md", + "sha256": "e8edb15429a42e946d0c9a64c8e81d0db51dd8e45137adec5aca769ddec7c86a", + "size": 2633 + }, + { + "path": "docs/design/ADR-0013-rust-session-broker.md", + "sha256": "c9e5dfa2f9378898be77a7883eb90c500a7630072ff59558e77c9c0f8e429f8c", + "size": 3762 + }, + { + "path": "docs/design/ADR-0014-rust-workspace-layout-model.md", + "sha256": "198068cf99769f16b754ebcda8649c20cd84ec5ef009c44d3360307974069605", + "size": 2841 + }, + { + "path": "docs/design/ADR-0015-rust-tui-framework.md", + "sha256": "7eebb0f8e159990e1cb38d8d61b14fd7af32bde4aaaf0368b601b489b71f6e05", + "size": 3166 + }, + { + "path": "docs/design/ADR-0016-rust-task-briefing-drafts.md", + "sha256": "419d0e5098d0d042064617f2b9887345141305eed2d9b1878c7d02b074979df6", + "size": 2683 + }, + { + "path": "docs/design/ADR-0017-rust-task-brief-contract.md", + "sha256": "b2860dfb42cd8bf55e0d84f825c360a2bffc8d3e3b63982f8a637cbb8361c29b", + "size": 3429 + }, + { + "path": "docs/design/ADR-0018-rust-task-plan-storage.md", + "sha256": "48e4ac0cc2ed222c4b7d70aaa1d7fec46eb2b620c15bf97a3f8c7f863d38b1a1", + "size": 2873 + }, + { + "path": "docs/design/ADR-0019-rust-agent-profile-registry.md", + "sha256": "05e094cca6850bbb5f7b15b6c7a90b4712afbabc629c2b825834c727df026cf9", + "size": 2166 + }, + { + "path": "docs/design/ADR-0020-native-agent-transcripts.md", + "sha256": "624088b1401455b19e43768aca361730b14d97ea3a3215bd6360cf3b0b81a70d", + "size": 2232 + }, + { + "path": "docs/design/ADR-0021-agent-detection-evidence.md", + "sha256": "f1c40437b608cdd7429beea4b37bedac89a1d3e73919d45f9c5a7ef1836c8dff", + "size": 3612 + }, + { + "path": "docs/design/ADR-0022-agent-state-rollups.md", + "sha256": "629573b90b38503ee31aee3a48bff04c0fa5243d2d4b8c48a4aa1075390bd27c", + "size": 1924 + }, + { + "path": "docs/design/ADR-0023-agent-lifecycle-authority.md", + "sha256": "af6723c9553a5aba996a99d8f2a99cce69994349094e6be9e54b2a6f79219821", + "size": 1839 + }, + { + "path": "docs/design/ADR-0024-agent-session-identity-and-resume.md", + "sha256": "ad72271ed82f21f99209d2d6112e503601c9566609d7b964a7c901342165dd24", + "size": 2727 + }, + { + "path": "docs/design/ADR-0025-agent-session-inventory-cli.md", + "sha256": "8ba7d6a9ad71adb23ed4b2cf99c1137cb76b760c592ca919f211d9c30995a098", + "size": 2591 + }, + { + "path": "docs/design/ADR-0026-owned-pty-process-sampling.md", + "sha256": "5c39e393db2ba0fe943de225ce9c3f603870b3fee10dec07855b83ea36daae31", + "size": 2959 + }, + { + "path": "docs/design/ADR-0027-herdr-compatible-screen-manifests.md", + "sha256": "62532df736a0bb980a69aed93aad77559fc3af2c82c9908a19f267f0aa4c18cf", + "size": 3444 + }, + { + "path": "docs/design/GO_RUST_STATE_MIGRATION.md", + "sha256": "34a916d135b5642cd0d13d516ceaad9581f1cbcd241865a0cf0081a2fdcbcde6", + "size": 2969 + }, { "path": "docs/design/README.md", - "sha256": "2181b8aad7f6d23fd9bfa1bffe2308f91d3c7b9eee5b62a0b65a7c5eec45720b", - "size": 371 + "sha256": "6c61cae33a2055d1436bf910f878cdf4deead3105cb938d63632d123f37071f8", + "size": 4378 + }, + { + "path": "docs/design/RUST_COMPATIBILITY_BASELINE.md", + "sha256": "e0dd28db40aaede3905fd3c1c5c66e92ff9beabe6a721ffcd48304ce895af1c9", + "size": 8796 + }, + { + "path": "docs/design/RUST_DEPENDENCY_AUDIT.md", + "sha256": "3c3696da29b680e493f4f6aa6b9a9b98772d6a214043949f810e30b981d8fa6d", + "size": 13076 + }, + { + "path": "docs/design/RUST_PARITY_PLAN.md", + "sha256": "106d81a9efcba602e9ae00212765327734f18131718c57bd7407147f2b8a6c10", + "size": 67130 }, { "path": "docs/design/Rover_Ten_Layer_Master_Plan.md", "sha256": "83783cfc11070422a95d5ae008a08a010bb3ef8c8eee585292cbf2b7eed2953d", "size": 57447 }, + { + "path": "docs/design/UPSTREAM_FEATURE_MATRIX.md", + "sha256": "48e33e319215f2cef3f733a267d647d4dd2f2835b506ffebfb5b87ef784bb9fe", + "size": 30219 + }, + { + "path": "docs/design/UPSTREAM_SOURCE_AUDIT.md", + "sha256": "8806e3d094843f737fec2de2c1226d725e3d03ccc729801e13784cdd884791f7", + "size": 10863 + }, { "path": "docs/validation/REPORT.md", "sha256": "2cedb0cc3e0688bcd106d04428bb476dbd91d3ff2d1830a450e843ea0dcc62e7", @@ -267,6 +767,11 @@ "sha256": "a6222a5bcacf88cd92c6a98aedf36eb4614d2b6eb41da275ce6fd76cefc316af", "size": 90728 }, + { + "path": "docs/validation/v0.0.1/REPORT.md", + "sha256": "01a2823426892512ecd7f124557eebfa27f0d59e6b01c4f3d050de5f3cf7e623", + "size": 2391 + }, { "path": "docs/validation/v0.2.0/REPORT.md", "sha256": "d1887ab525cbb3bc4041a5fafd195647fd21c03fc92e795761cef345c44d84bf", @@ -809,8 +1314,8 @@ }, { "path": "internal/model/version_test.go", - "sha256": "23efee2ff81d74c2b40c7d19c494caf61435c4ca9ede6d1c696197ffef82837e", - "size": 5128 + "sha256": "d0812c2892f4c17ef2d5a38f41951bae250a896a756f05450335c3362ce1851e", + "size": 5614 }, { "path": "internal/publish/publish.go", @@ -968,109 +1473,1674 @@ "size": 4408 }, { - "path": "schemas/README.md", - "sha256": "5454bbc208a50e33b6c47fd6da5435f6f3fd63f102aee76d0402f763ccfae3cc", - "size": 434 + "path": "licenses/THIRD_PARTY_NOTICES.md", + "sha256": "92ef17c23302589d95c6921b65d40bf3284f040bd0d8a3e478539c7c24f588ab", + "size": 50807 }, { - "path": "schemas/config.schema.json", - "sha256": "562d52c46f5fd3879ab9350b8b080a22495c0afd8d3b283c88b43d1f437c79fe", - "size": 4912 + "path": "licenses/third-party/aho-corasick-1.1.5/COPYING", + "sha256": "01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f", + "size": 126 }, { - "path": "schemas/mutation.schema.json", - "sha256": "f72cbc49e251f11a1a1cf282b900ffd78733ccdd2cfc60c621b58a163e4260a7", - "size": 979 + "path": "licenses/third-party/aho-corasick-1.1.5/LICENSE-MIT", + "sha256": "0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f", + "size": 1081 }, { - "path": "schemas/regression.schema.json", - "sha256": "c8fa67fd748f9ceacc7c5e30905bbe0cc5f93e95a2112fc9fc811c3ff805089a", - "size": 617 + "path": "licenses/third-party/aho-corasick-1.1.5/UNLICENSE", + "sha256": "7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c", + "size": 1211 }, { - "path": "schemas/task.schema.json", - "sha256": "843a2ea1c7e6f5b69f5c93025aa3073125e98b0d47d989f3da74f6438f2973b8", - "size": 3357 + "path": "licenses/third-party/alacritty_terminal-0.26.0/LICENSE-APACHE", + "sha256": "8884289e3969c089cd556c53fa1d3f51d86b29e52a8c1d660a55ce8292986910", + "size": 10843 }, { - "path": "schemas/workflow.schema.json", - "sha256": "94ede18dcc05df1bb81abf7ca87c17baa121a37c00f8eb25a90a59610dbc088c", - "size": 6213 + "path": "licenses/third-party/allocator-api2-0.2.21/LICENSE-APACHE", + "sha256": "20fe7b00e904ed690e3b9fd6073784d3fc428141dbd10b81c01fd143d0797f58", + "size": 9899 }, { - "path": "scripts/demo.py", - "sha256": "0ac21dfe245ac8ed07c0a462699d1880cb04cf258b0553256b6503946fe61644", - "size": 8578 + "path": "licenses/third-party/allocator-api2-0.2.21/LICENSE-MIT", + "sha256": "36516aefdc84c5d5a1e7485425913a22dbda69eb1930c5e84d6ae4972b5194b9", + "size": 1046 }, { - "path": "scripts/demo_extended.py", - "sha256": "e4c8e52237c26a6083150bc1292a7adf72a5aeb3175944c87904ec3eb3701f22", - "size": 14396 + "path": "licenses/third-party/anyhow-1.0.104/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 }, { - "path": "scripts/generate_source_manifest.py", - "sha256": "4102cfa77e92b3cb5eec69ff465c75da6652561fa26fdee19c2d419bba4b7e7b", - "size": 2893 + "path": "licenses/third-party/anyhow-1.0.104/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 }, { - "path": "sdk/README.md", - "sha256": "da0265ab167ad8295effbae09d18e73c5c76fe5c1ed182181ea429e065aae1b0", - "size": 861 + "path": "licenses/third-party/approx-0.5.1/LICENSE", + "sha256": "3ddf9be5c28fe27dad143a5dc76eea25222ad1dd68934a047064e56ed2fa40c5", + "size": 11560 }, { - "path": "sdk/go/README.md", - "sha256": "664376a4a34f06ca1c1a73c6f30611088ed642317cc9dccfbede5a017f7adf09", - "size": 877 + "path": "licenses/third-party/ar_archive_writer-0.5.3/LICENSE.txt", + "sha256": "f72b120d1385408e9e380acc020756eb6ba1b461d66c328ea67327ba08d7dcfd", + "size": 12263 }, { - "path": "sdk/go/rover_client.go", - "sha256": "e33e5ac4bd4b4bc9ff36a9ca2b2511419e9febccb798c1275e2b1db342449616", - "size": 5574 + "path": "licenses/third-party/arrayvec-0.7.8/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 }, { - "path": "sdk/go/rover_client_test.go", - "sha256": "8760d54bb4ebe137dd8b71e85f5d32326e8d6fc82b7e513cb3707075cbdcb6c4", - "size": 1391 + "path": "licenses/third-party/arrayvec-0.7.8/LICENSE-MIT", + "sha256": "4da95ec4ecb65b738d470b7d762894ad9c97da93e6cbfb18b570fc2c96f4b871", + "size": 1071 }, { - "path": "sdk/python/README.md", - "sha256": "48b25e79bf4c6980c77265a44e07c2c9b648ebead0112572251b9e61ad9fd051", - "size": 760 + "path": "licenses/third-party/atomic-waker-1.1.2/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/atomic-waker-1.1.2/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/autocfg-1.5.1/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/autocfg-1.5.1/LICENSE-MIT", + "sha256": "27995d58ad5c1145c1a8cd86244ce844886958a35eb2b78c6b772748669999ac", + "size": 1054 + }, + { + "path": "licenses/third-party/base64-0.22.1/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/base64-0.22.1/LICENSE-MIT", + "sha256": "0dd882e53de11566d50f8e8e2d5a651bcf3fabee4987d70f306233cf39094ba7", + "size": 1076 + }, + { + "path": "licenses/third-party/bitflags-1.3.2/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/bitflags-1.3.2/LICENSE-MIT", + "sha256": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "size": 1071 + }, + { + "path": "licenses/third-party/bitflags-2.13.2/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/bitflags-2.13.2/LICENSE-MIT", + "sha256": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "size": 1071 + }, + { + "path": "licenses/third-party/block-buffer-0.12.1/LICENSE-APACHE", + "sha256": "a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5", + "size": 10849 + }, + { + "path": "licenses/third-party/block-buffer-0.12.1/LICENSE-MIT", + "sha256": "98181e7249d0c01737645ec982499ce99a0f07eb8f7d625b8840d799d10dbc01", + "size": 1082 }, { - "path": "sdk/python/pyproject.toml", - "sha256": "1d911005a9b689c9c0eb42d2f8f883e9101dce25bb47459b0f9370434cdd65cf", - "size": 667 + "path": "licenses/third-party/by_address-1.2.1/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 }, { - "path": "sdk/python/rover_client.py", - "sha256": "33c33f2fb85393b7f26018f12b1b28cc0540fdebd399c6e5a19a4d8bdcd07997", - "size": 3566 + "path": "licenses/third-party/by_address-1.2.1/LICENSE-MIT", + "sha256": "daa94322de7eab889e055932396160395bd8e3af82f56ae8c419d3049111da72", + "size": 1056 }, { - "path": "sdk/python/test_client.py", - "sha256": "494280de192150404bcbc99be6aea90a5280032667d49a7586560a5bd49d15d2", - "size": 1183 + "path": "licenses/third-party/castaway-0.2.4/LICENSE", + "sha256": "f98e09091d5ae02b2f2ec1ead4f7f28c4c44d1cb98b078739ba67091637e170c", + "size": 1075 + }, + { + "path": "licenses/third-party/cc-1.4.7/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/cc-1.4.7/LICENSE-MIT", + "sha256": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397", + "size": 1057 + }, + { + "path": "licenses/third-party/cfg-if-1.0.5/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/cfg-if-1.0.5/LICENSE-MIT", + "sha256": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397", + "size": 1057 + }, + { + "path": "licenses/third-party/cfg_aliases-0.1.1/LICENSE", + "sha256": "31b94860253d8ec7b4529f51901044d3b459d6292d996504a36b1bae3a36a812", + "size": 1076 + }, + { + "path": "licenses/third-party/compact_str-0.9.1/LICENSE", + "sha256": "0d52feec79589df30138dad3800323fe5686b764b73347330a2dfef8ac83efe8", + "size": 1073 + }, + { + "path": "licenses/third-party/concurrent-queue-2.5.0/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/concurrent-queue-2.5.0/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/const-oid-0.10.2/LICENSE-APACHE", + "sha256": "a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5", + "size": 10849 + }, + { + "path": "licenses/third-party/const-oid-0.10.2/LICENSE-MIT", + "sha256": "73b9dc2e79c7308998dd30296e073aefaefb944a68fb89aa412c23c0edcabcaa", + "size": 1082 + }, + { + "path": "licenses/third-party/convert_case-0.10.0/LICENSE", + "sha256": "aed7b1758e35afa0cd0fde059d61950747ca11cd0e5e169cb21c11608daed772", + "size": 1062 + }, + { + "path": "licenses/third-party/cpufeatures-0.3.1/LICENSE-APACHE", + "sha256": "a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5", + "size": 10849 + }, + { + "path": "licenses/third-party/cpufeatures-0.3.1/LICENSE-MIT", + "sha256": "73b9dc2e79c7308998dd30296e073aefaefb944a68fb89aa412c23c0edcabcaa", + "size": 1082 + }, + { + "path": "licenses/third-party/crossbeam-utils-0.8.23/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/crossbeam-utils-0.8.23/LICENSE-MIT", + "sha256": "5734ed989dfca1f625b40281ee9f4530f91b2411ec01cb748223e7eb87e201ab", + "size": 1099 + }, + { + "path": "licenses/third-party/crossterm-0.29.0/LICENSE", + "sha256": "aca4760f2a5eba9ce9d4448fe7cd3fcf98245a8315ede59ebd0c6085dd542e61", + "size": 1083 + }, + { + "path": "licenses/third-party/crossterm_winapi-0.9.1/LICENSE", + "sha256": "aca4760f2a5eba9ce9d4448fe7cd3fcf98245a8315ede59ebd0c6085dd542e61", + "size": 1083 + }, + { + "path": "licenses/third-party/crypto-common-0.2.2/LICENSE-APACHE", + "sha256": "a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5", + "size": 10849 + }, + { + "path": "licenses/third-party/crypto-common-0.2.2/LICENSE-MIT", + "sha256": "d2e7ec5355c96eeade56b09187ceb48a6a30299da3ce7531a66d3d11405ab963", + "size": 1070 + }, + { + "path": "licenses/third-party/cursor-icon-1.2.0/LICENSE-APACHE", + "sha256": "b59affd015e517ed998dd735757f9a3693643cd389609947ed54074234a10784", + "size": 11346 + }, + { + "path": "licenses/third-party/cursor-icon-1.2.0/LICENSE-MIT", + "sha256": "c138726a726ac37bae09d0f8a48e74e8e3aa8a8952fcd10fae0fb224cc1c05f1", + "size": 1059 + }, + { + "path": "licenses/third-party/darling-0.24.1/LICENSE", + "sha256": "8ea93490d74a5a1b1af3ff71d786271b3f1e5f0bea79ac16e02ec533cef040d6", + "size": 1067 + }, + { + "path": "licenses/third-party/darling_core-0.24.1/LICENSE", + "sha256": "8ea93490d74a5a1b1af3ff71d786271b3f1e5f0bea79ac16e02ec533cef040d6", + "size": 1067 + }, + { + "path": "licenses/third-party/darling_macro-0.24.1/LICENSE", + "sha256": "8ea93490d74a5a1b1af3ff71d786271b3f1e5f0bea79ac16e02ec533cef040d6", + "size": 1067 + }, + { + "path": "licenses/third-party/deranged-0.5.8/LICENSE-APACHE", + "sha256": "edd65bdd88957a205c47d53fa499eed8865a70320f0f03f6391668cb304ea376", + "size": 11349 + }, + { + "path": "licenses/third-party/deranged-0.5.8/LICENSE-MIT", + "sha256": "231c837c45eb53f108fb48929e488965bc4fcc14e9ea21d35f50e6b99d98685b", + "size": 1062 + }, + { + "path": "licenses/third-party/derive_more-2.1.1/LICENSE", + "sha256": "8a35369f3ca263b3c62fbb5032947e53b6bfebc6c8a4d1bb982de1c069f6fba5", + "size": 1080 + }, + { + "path": "licenses/third-party/derive_more-impl-2.1.1/LICENSE", + "sha256": "8a35369f3ca263b3c62fbb5032947e53b6bfebc6c8a4d1bb982de1c069f6fba5", + "size": 1080 + }, + { + "path": "licenses/third-party/digest-0.11.3/LICENSE-APACHE", + "sha256": "a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5", + "size": 10849 + }, + { + "path": "licenses/third-party/digest-0.11.3/LICENSE-MIT", + "sha256": "af59cea35d7f5e2777a713b8d155d65efa2c339eb43f3c14e868c6ac8506edad", + "size": 1103 + }, + { + "path": "licenses/third-party/document-features-0.2.12/LICENSE-APACHE", + "sha256": "074e6e32c86a4c0ef8b3ed25b721ca23aca83df277cd88106ef7177c354615ff", + "size": 10280 + }, + { + "path": "licenses/third-party/document-features-0.2.12/LICENSE-MIT", + "sha256": "aa893340d14b9844625be6a50ac644169a01b52f0211cbf81b09e1874c8cd81d", + "size": 1082 + }, + { + "path": "licenses/third-party/downcast-rs-1.2.1/LICENSE-APACHE", + "sha256": "8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90", + "size": 10850 + }, + { + "path": "licenses/third-party/downcast-rs-1.2.1/LICENSE-MIT", + "sha256": "fb252fecf98d9af8f1d9201c5860319a514f5a8a24700dc84fc6ea287a7a4958", + "size": 1073 + }, + { + "path": "licenses/third-party/either-1.18.0/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/either-1.18.0/LICENSE-MIT", + "sha256": "7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545", + "size": 1043 + }, + { + "path": "licenses/third-party/equivalent-1.0.2/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/equivalent-1.0.2/LICENSE-MIT", + "sha256": "7365cc8878a1d7ce155a58c4ca09c3d7a6be413efa5334a80ea842912b669349", + "size": 1049 + }, + { + "path": "licenses/third-party/errno-0.3.14/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/errno-0.3.14/LICENSE-MIT", + "sha256": "8764a597675778ddfd4e25f81b08a05dbcf089ac05662df7613fe67f150e3aa2", + "size": 1054 + }, + { + "path": "licenses/third-party/fallible-iterator-0.3.0/LICENSE-APACHE", + "sha256": "c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08", + "size": 11358 + }, + { + "path": "licenses/third-party/fallible-iterator-0.3.0/LICENSE-MIT", + "sha256": "0816e154b159ba255c563f7c8c7df5bbb8cc5fc96f5ab8cf9f4743b4f41fe7eb", + "size": 1078 + }, + { + "path": "licenses/third-party/fallible-streaming-iterator-0.1.9/LICENSE-APACHE", + "sha256": "c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08", + "size": 11358 + }, + { + "path": "licenses/third-party/fallible-streaming-iterator-0.1.9/LICENSE-MIT", + "sha256": "8dcec5569a9be5b0e086c80faed6f1aefa670af0ec29cecc2f714303096887e0", + "size": 1086 + }, + { + "path": "licenses/third-party/fast-srgb8-1.0.0/LICENSE-APACHE", + "sha256": "a8a76b5fd1f1e53246d8d12dac4e6bed981f93b1f94d63f4edbf9a367b83cfc4", + "size": 10842 + }, + { + "path": "licenses/third-party/fast-srgb8-1.0.0/LICENSE-CC0", + "sha256": "a2010f343487d3f7618affe54f789f5487602331c0a8d03f49e9a7c547cf0499", + "size": 7048 + }, + { + "path": "licenses/third-party/fast-srgb8-1.0.0/LICENSE-MIT", + "sha256": "0b32b8546bef7ce9338df62d68b8f1b1a2de678df10e97ee108218ae921d4845", + "size": 1058 + }, + { + "path": "licenses/third-party/fastrand-2.5.0/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/fastrand-2.5.0/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/filedescriptor-0.8.3/LICENSE.md", + "sha256": "191c46fcf52061382b1c51a70311eb9081381cc158e5899f3739473a9432185b", + "size": 1068 + }, + { + "path": "licenses/third-party/find-msvc-tools-0.1.13/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/find-msvc-tools-0.1.13/LICENSE-MIT", + "sha256": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397", + "size": 1057 + }, + { + "path": "licenses/third-party/foldhash-0.2.0/LICENSE", + "sha256": "b1181a40b2a7b25cf66fd01481713bc1005df082c53ef73e851e55071b102744", + "size": 856 + }, + { + "path": "licenses/third-party/futures-io-0.3.34/LICENSE-APACHE", + "sha256": "275c491d6d1160553c32fd6127061d7f9606c3ea25abfad6ca3f6ed088785427", + "size": 10874 + }, + { + "path": "licenses/third-party/futures-io-0.3.34/LICENSE-MIT", + "sha256": "6652c868f35dfe5e8ef636810a4e576b9d663f3a17fb0f5613ad73583e1b88fd", + "size": 1094 + }, + { + "path": "licenses/third-party/getrandom-0.4.3/LICENSE-APACHE", + "sha256": "aaff376532ea30a0cd5330b9502ad4a4c8bf769c539c87ffe78819d188a18ebf", + "size": 10849 + }, + { + "path": "licenses/third-party/getrandom-0.4.3/LICENSE-MIT", + "sha256": "523a42c25d245dde9c015f882cec7f4555aad883382a6cf19b4b7d9b2cd5419b", + "size": 1130 + }, + { + "path": "licenses/third-party/hashbrown-0.16.1/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/hashbrown-0.16.1/LICENSE-MIT", + "sha256": "ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2", + "size": 1060 + }, + { + "path": "licenses/third-party/hashbrown-0.17.1/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/hashbrown-0.17.1/LICENSE-MIT", + "sha256": "ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2", + "size": 1060 + }, + { + "path": "licenses/third-party/heck-0.5.0/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/heck-0.5.0/LICENSE-MIT", + "sha256": "7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0", + "size": 1071 + }, + { + "path": "licenses/third-party/hermit-abi-0.5.3/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/hermit-abi-0.5.3/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/home-0.5.12/LICENSE-APACHE", + "sha256": "8ada45cd9f843acf64e4722ae262c622a2b3b3007c7310ef36ac1061a30f6adb", + "size": 10860 + }, + { + "path": "licenses/third-party/home-0.5.12/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/hybrid-array-0.4.15/LICENSE-APACHE", + "sha256": "a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5", + "size": 10849 + }, + { + "path": "licenses/third-party/hybrid-array-0.4.15/LICENSE-MIT", + "sha256": "70c9d40f1f9545c3f133b8a67206e89da850f6468eed072281bb3701514114a9", + "size": 1082 + }, + { + "path": "licenses/third-party/ident_case-1.0.1/LICENSE", + "sha256": "508a77d2e7b51d98adeed32648ad124b7b30241a8e70b2e72c99f92d8e5874d1", + "size": 1036 + }, + { + "path": "licenses/third-party/indexmap-2.14.2/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/indexmap-2.14.2/LICENSE-MIT", + "sha256": "ecc269ef87fd38a1d98e30bfac9ba964a9dbd9315c3770fed98d4d7cb5882055", + "size": 1049 + }, + { + "path": "licenses/third-party/indoc-2.0.7/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/indoc-2.0.7/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/instability-0.3.13/LICENSE.md", + "sha256": "0352320d9929e862c1f07b59caacc3cd1bbacfec0ad4136bf4c89d4ca8939781", + "size": 1114 + }, + { + "path": "licenses/third-party/itertools-0.14.0/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/itertools-0.14.0/LICENSE-MIT", + "sha256": "7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545", + "size": 1043 + }, + { + "path": "licenses/third-party/itoa-1.0.18/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/itoa-1.0.18/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/kasuari-0.4.12/LICENSE-APACHE", + "sha256": "000b4962e6b27176a0ff89cce4be555b16472cafb5671eb2804a8fdac6854793", + "size": 11357 + }, + { + "path": "licenses/third-party/kasuari-0.4.12/LICENSE-MIT", + "sha256": "74a7056189235b49336669da4e67ad1b315de4ed17e93df751f48c3fab403812", + "size": 1109 + }, + { + "path": "licenses/third-party/lazy_static-1.5.0/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/lazy_static-1.5.0/LICENSE-MIT", + "sha256": "0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720", + "size": 1071 + }, + { + "path": "licenses/third-party/libc-0.2.189/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/libc-0.2.189/LICENSE-MIT", + "sha256": "123a331b5dbf04c30097fa43b8f858bc85df671fe776de498d01f3d6b7c1f69e", + "size": 1066 + }, + { + "path": "licenses/third-party/libm-0.2.16/LICENSE.txt", + "sha256": "3823dda7cf046602f4b4e77ec8e227863dc4736037cc85bb33d9f19febe16bb7", + "size": 14088 + }, + { + "path": "licenses/third-party/libsqlite3-sys-0.38.2/LICENSE", + "sha256": "c10c1f27337546471e5f7e4e97fdd398b35b9d4e126115dcd22de8d8e65abf6f", + "size": 1067 + }, + { + "path": "licenses/third-party/libsqlite3-sys-0.38.2/SQLITE_BLESSING.txt", + "sha256": "7ba39c32af28155248da75313e83f17a429d9a4cf2ef691b092527d6b2d37b77", + "size": 274 + }, + { + "path": "licenses/third-party/line-clipping-0.3.7/LICENSE-APACHE", + "sha256": "86f2767527a034d6b9cb8bd98676925e58ed75bf805d234dc2a71a54150cbad1", + "size": 11343 + }, + { + "path": "licenses/third-party/line-clipping-0.3.7/LICENSE-MIT", + "sha256": "520a93f9b172b4c8d2a7ab6f0da1f5f987eba7270ab64a02972095cd87df7fc4", + "size": 1070 + }, + { + "path": "licenses/third-party/linux-raw-sys-0.12.1/COPYRIGHT", + "sha256": "3290ae0fbc9ddb77d2239121d710f0bb9d31b3b4744e6d97fe01e652b4c1870b", + "size": 881 + }, + { + "path": "licenses/third-party/linux-raw-sys-0.12.1/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/linux-raw-sys-0.12.1/LICENSE-APACHE-2.0_WITH_LLVM-exception", + "sha256": "268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5", + "size": 12243 + }, + { + "path": "licenses/third-party/linux-raw-sys-0.12.1/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/litrs-1.0.0/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/litrs-1.0.0/LICENSE-MIT", + "sha256": "7dc1552e88f49132cb358b1b962fc5e79fa42d70bcbb88c526d33e45b8e98036", + "size": 1062 + }, + { + "path": "licenses/third-party/lock_api-0.4.14/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/lock_api-0.4.14/LICENSE-MIT", + "sha256": "c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5", + "size": 1071 + }, + { + "path": "licenses/third-party/log-0.4.34/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/log-0.4.34/LICENSE-MIT", + "sha256": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "size": 1071 + }, + { + "path": "licenses/third-party/lru-0.18.0/LICENSE", + "sha256": "061dc50af2cd9340703daf61978af3200cf681b12ea67a323c33ba109a23a45e", + "size": 1071 + }, + { + "path": "licenses/third-party/memchr-2.8.3/COPYING", + "sha256": "01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f", + "size": 126 + }, + { + "path": "licenses/third-party/memchr-2.8.3/LICENSE-MIT", + "sha256": "0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f", + "size": 1081 + }, + { + "path": "licenses/third-party/memchr-2.8.3/UNLICENSE", + "sha256": "7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c", + "size": 1211 + }, + { + "path": "licenses/third-party/mio-1.2.3/LICENSE", + "sha256": "07919255c7e04793d8ea760d6c2ce32d19f9ff02bdbdde3ce90b1e1880929a9b", + "size": 1082 + }, + { + "path": "licenses/third-party/miow-0.6.1/LICENSE-APACHE", + "sha256": "7cfd738c53d61c79f07e348f622bf7707c9084237054d37fbe07788a75f5881c", + "size": 11048 + }, + { + "path": "licenses/third-party/miow-0.6.1/LICENSE-MIT", + "sha256": "29c9533fc9a32ee529e928bcb12308f0745d237fdea80d83c0258f23243d65d1", + "size": 1082 + }, + { + "path": "licenses/third-party/nix-0.28.0/LICENSE", + "sha256": "66e3ee1fa7f909ad3c612d556f2a0cdabcd809ad6e66f3b0605015ac64841b70", + "size": 1097 + }, + { + "path": "licenses/third-party/num-conv-0.2.2/LICENSE-APACHE", + "sha256": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "size": 10174 + }, + { + "path": "licenses/third-party/num-conv-0.2.2/LICENSE-MIT", + "sha256": "e2e245f2b566d0bfafb0f6a04c16b82d710c4e6e7d799e39bb4ef6e541b85b98", + "size": 1050 + }, + { + "path": "licenses/third-party/num-traits-0.2.19/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/num-traits-0.2.19/LICENSE-MIT", + "sha256": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "size": 1071 + }, + { + "path": "licenses/third-party/num_threads-0.1.7/LICENSE-APACHE", + "sha256": "c69b72788ec261765e460532016b4bb78372f238e449b5e70e3268ec1e18fd15", + "size": 11342 + }, + { + "path": "licenses/third-party/num_threads-0.1.7/LICENSE-MIT", + "sha256": "b4bf94a9fceb8846320fda938ee53fc16b506572609da5cf1d2d289a5597a8f8", + "size": 1055 + }, + { + "path": "licenses/third-party/object-0.39.1/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/object-0.39.1/LICENSE-MIT", + "sha256": "0b74dfa0bcee5c420c6b7f67b4b2658f9ab8388c97b8e733975f2cecbdd668a6", + "size": 1064 + }, + { + "path": "licenses/third-party/palette-0.7.6/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/palette-0.7.6/LICENSE-MIT", + "sha256": "be1313a4b07ab509c80083966cdd209a7d81d75cd57fcea24441fc9041ccf184", + "size": 1080 + }, + { + "path": "licenses/third-party/palette_derive-0.7.6/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/palette_derive-0.7.6/LICENSE-MIT", + "sha256": "be1313a4b07ab509c80083966cdd209a7d81d75cd57fcea24441fc9041ccf184", + "size": 1080 + }, + { + "path": "licenses/third-party/parking_lot-0.12.5/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/parking_lot-0.12.5/LICENSE-MIT", + "sha256": "c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5", + "size": 1071 + }, + { + "path": "licenses/third-party/parking_lot_core-0.9.12/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/parking_lot_core-0.9.12/LICENSE-MIT", + "sha256": "c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5", + "size": 1071 + }, + { + "path": "licenses/third-party/pin-project-lite-0.2.17/LICENSE-APACHE", + "sha256": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "size": 10174 + }, + { + "path": "licenses/third-party/pin-project-lite-0.2.17/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/piper-0.2.5/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/piper-0.2.5/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/pkg-config-0.3.34/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/pkg-config-0.3.34/LICENSE-MIT", + "sha256": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397", + "size": 1057 + }, + { + "path": "licenses/third-party/polling-3.11.0/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/polling-3.11.0/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/portable-atomic-1.13.1/LICENSE-APACHE", + "sha256": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "size": 10174 + }, + { + "path": "licenses/third-party/portable-atomic-1.13.1/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/portable-pty-0.9.0/LICENSE.md", + "sha256": "191c46fcf52061382b1c51a70311eb9081381cc158e5899f3739473a9432185b", + "size": 1068 + }, + { + "path": "licenses/third-party/powerfmt-0.2.0/LICENSE-APACHE", + "sha256": "155420c6403d4e0fca34105e3c03fdd6939b64c393c7ec6f95f5b72c5474eab0", + "size": 11349 + }, + { + "path": "licenses/third-party/powerfmt-0.2.0/LICENSE-MIT", + "sha256": "070dbc7dda03a29296f2d58bdb9b7331af90f2abc9f31df22875d1eabaf29852", + "size": 1062 + }, + { + "path": "licenses/third-party/proc-macro2-1.0.107/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/proc-macro2-1.0.107/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/psm-0.1.32/LICENSE-APACHE", + "sha256": "965a63a81d9a2fbeb5f9096954dabb49690f9dffcdac9825f675b25c807252a2", + "size": 10702 + }, + { + "path": "licenses/third-party/psm-0.1.32/LICENSE-MIT", + "sha256": "3e3714aa69bd874601741fd7d7ad5298740cece37778e279fc1ab4451c5a11af", + "size": 1062 + }, + { + "path": "licenses/third-party/quote-1.0.47/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/quote-1.0.47/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/r-efi-6.0.0/AUTHORS", + "sha256": "d027e91dbc9cdbb2f1190068e498bd6b61cff022b6a032b191021ba658d96111", + "size": 3733 + }, + { + "path": "licenses/third-party/ratatui-0.30.2/LICENSE", + "sha256": "50eb43e8d742c9c61a9391e42b2184fce54dbd1893a1bb1c85b8c9ee217ab1f5", + "size": 1132 + }, + { + "path": "licenses/third-party/ratatui-core-0.1.2/LICENSE", + "sha256": "50eb43e8d742c9c61a9391e42b2184fce54dbd1893a1bb1c85b8c9ee217ab1f5", + "size": 1132 + }, + { + "path": "licenses/third-party/ratatui-crossterm-0.1.2/LICENSE", + "sha256": "50eb43e8d742c9c61a9391e42b2184fce54dbd1893a1bb1c85b8c9ee217ab1f5", + "size": 1132 + }, + { + "path": "licenses/third-party/ratatui-widgets-0.3.2/LICENSE", + "sha256": "50eb43e8d742c9c61a9391e42b2184fce54dbd1893a1bb1c85b8c9ee217ab1f5", + "size": 1132 + }, + { + "path": "licenses/third-party/redox_syscall-0.5.18/LICENSE", + "sha256": "efcfee7981ff72431fffb06925cad00a23dce079ed4354f61030ad5abdb78829", + "size": 1076 + }, + { + "path": "licenses/third-party/regex-1.12.4/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/regex-1.12.4/LICENSE-MIT", + "sha256": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "size": 1071 + }, + { + "path": "licenses/third-party/regex-automata-0.4.18/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/regex-automata-0.4.18/LICENSE-MIT", + "sha256": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "size": 1071 + }, + { + "path": "licenses/third-party/regex-syntax-0.8.11/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/regex-syntax-0.8.11/LICENSE-MIT", + "sha256": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "size": 1071 + }, + { + "path": "licenses/third-party/rusqlite-0.40.2/LICENSE", + "sha256": "c10c1f27337546471e5f7e4e97fdd398b35b9d4e126115dcd22de8d8e65abf6f", + "size": 1067 + }, + { + "path": "licenses/third-party/rustc_version-0.4.1/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/rustc_version-0.4.1/LICENSE-MIT", + "sha256": "c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5", + "size": 1071 + }, + { + "path": "licenses/third-party/rustix-1.1.4/COPYRIGHT", + "sha256": "377c2e7c53250cc5905c0b0532d35973392af16ffb9596a41d99d202cf3617c9", + "size": 853 + }, + { + "path": "licenses/third-party/rustix-1.1.4/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/rustix-1.1.4/LICENSE-APACHE-2.0_WITH_LLVM-exception", + "sha256": "268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5", + "size": 12243 + }, + { + "path": "licenses/third-party/rustix-1.1.4/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/rustix-openpty-0.2.0/COPYRIGHT", + "sha256": "27292f8969032a60051bbb7501e6114428b8f4a566f2e9b6bb7dc771afc9352b", + "size": 885 + }, + { + "path": "licenses/third-party/rustix-openpty-0.2.0/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/rustix-openpty-0.2.0/LICENSE-APACHE-2.0_WITH_LLVM-exception", + "sha256": "268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5", + "size": 12243 + }, + { + "path": "licenses/third-party/rustix-openpty-0.2.0/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/rustversion-1.0.23/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/rustversion-1.0.23/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/ryu-1.0.23/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/ryu-1.0.23/LICENSE-BOOST", + "sha256": "c9bff75738922193e67fa726fa225535870d2aa1059f91452c411736284ad566", + "size": 1338 + }, + { + "path": "licenses/third-party/scopeguard-1.2.0/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/scopeguard-1.2.0/LICENSE-MIT", + "sha256": "fb77f0a9c53e473abe5103c8632ef9f0f2874d4fb3f17cb2d8c661aab9cee9d7", + "size": 1097 + }, + { + "path": "licenses/third-party/semver-1.0.28/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/semver-1.0.28/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/serde-1.0.229/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/serde-1.0.229/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/serde_core-1.0.229/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/serde_core-1.0.229/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/serde_derive-1.0.229/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/serde_derive-1.0.229/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/serde_json-1.0.151/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/serde_json-1.0.151/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/serde_spanned-0.6.9/LICENSE-APACHE", + "sha256": "c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08", + "size": 11358 + }, + { + "path": "licenses/third-party/serde_spanned-0.6.9/LICENSE-MIT", + "sha256": "6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6", + "size": 1062 + }, + { + "path": "licenses/third-party/serde_stacker-0.1.14/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/serde_stacker-0.1.14/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/serial2-0.2.38/LICENSE-APACHE", + "sha256": "da4d60bfcfd15870aa69e0d0891796d7075a860c54e7b38ec5ccd353c66049ed", + "size": 575 + }, + { + "path": "licenses/third-party/sha2-0.11.0/LICENSE-APACHE", + "sha256": "a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5", + "size": 10849 + }, + { + "path": "licenses/third-party/sha2-0.11.0/LICENSE-MIT", + "sha256": "831e0f43ad0bf014c1c4fec5767aae470434c1d66d6e671be2d823e729063e25", + "size": 1196 + }, + { + "path": "licenses/third-party/shared_library-0.1.9/LICENSE-APACHE", + "sha256": "c144680885b29e4719e2a51f0aab5439a1e02d980692b5aaf086cae12727f28b", + "size": 10849 + }, + { + "path": "licenses/third-party/shared_library-0.1.9/LICENSE-MIT", + "sha256": "1c07d19ccbe2578665ab7d8c63f71559f890eb8d2a82fa39d0206b7a3414064f", + "size": 1057 + }, + { + "path": "licenses/third-party/shell-words-1.1.1/LICENSE-APACHE", + "sha256": "848d7bf14c618c39f1e29fcac2318a2fcc4254045ba7e2f6f5fa799e057225cb", + "size": 10708 + }, + { + "path": "licenses/third-party/shell-words-1.1.1/LICENSE-MIT", + "sha256": "f76b7831ee8f42d3ac38919101e4ceb9f56aeb7796cdfe8a5ef52e26acedcbbf", + "size": 1058 + }, + { + "path": "licenses/third-party/shlex-2.0.1/LICENSE-APACHE", + "sha256": "553fffcd9b1cb158bc3e9edc35da85ca5c3b3d7d2e61c883ebcfa8a65814b583", + "size": 566 + }, + { + "path": "licenses/third-party/shlex-2.0.1/LICENSE-MIT", + "sha256": "4455bf75a91154108304cb283e0fea9948c14f13e20d60887cf2552449dea3b1", + "size": 1092 + }, + { + "path": "licenses/third-party/signal-hook-0.3.18/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/signal-hook-0.3.18/LICENSE-MIT", + "sha256": "503558bfefe66ca15e4e3f7955b3cb0ec87fd52f29bf24b336af7bd00e946d5c", + "size": 1068 + }, + { + "path": "licenses/third-party/signal-hook-0.4.4/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/signal-hook-0.4.4/LICENSE-MIT", + "sha256": "503558bfefe66ca15e4e3f7955b3cb0ec87fd52f29bf24b336af7bd00e946d5c", + "size": 1068 + }, + { + "path": "licenses/third-party/signal-hook-mio-0.2.5/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/signal-hook-mio-0.2.5/LICENSE-MIT", + "sha256": "503558bfefe66ca15e4e3f7955b3cb0ec87fd52f29bf24b336af7bd00e946d5c", + "size": 1068 + }, + { + "path": "licenses/third-party/signal-hook-registry-1.4.8/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/signal-hook-registry-1.4.8/LICENSE-MIT", + "sha256": "503558bfefe66ca15e4e3f7955b3cb0ec87fd52f29bf24b336af7bd00e946d5c", + "size": 1068 + }, + { + "path": "licenses/third-party/smallvec-1.16.1/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/smallvec-1.16.1/LICENSE-MIT", + "sha256": "0b28172679e0009b655da42797c03fd163a3379d5cfa67ba1f1655e974a2a1a9", + "size": 1072 + }, + { + "path": "licenses/third-party/stacker-0.1.25/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/stacker-0.1.25/LICENSE-MIT", + "sha256": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397", + "size": 1057 + }, + { + "path": "licenses/third-party/static_assertions-1.1.0/LICENSE-APACHE", + "sha256": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "size": 11358 + }, + { + "path": "licenses/third-party/static_assertions-1.1.0/LICENSE-MIT", + "sha256": "ea084a2373ebc1f0902c09266e7bf25a05ab3814c1805bb017ffa7308f90c061", + "size": 1072 + }, + { + "path": "licenses/third-party/strsim-0.11.1/LICENSE", + "sha256": "1e697ce8d21401fbf1bddd9b5c3fd4c4c79ae1e3bdf51f81761c85e11d5a89cd", + "size": 1166 + }, + { + "path": "licenses/third-party/strum-0.28.0/LICENSE", + "sha256": "8bce3b45e49ecd1461f223b46de133d8f62cd39f745cfdaf81bee554b908bd42", + "size": 1072 + }, + { + "path": "licenses/third-party/strum_macros-0.28.0/LICENSE", + "sha256": "8bce3b45e49ecd1461f223b46de133d8f62cd39f745cfdaf81bee554b908bd42", + "size": 1072 + }, + { + "path": "licenses/third-party/syn-2.0.119/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/syn-2.0.119/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/syn-3.0.6/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/syn-3.0.6/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/thiserror-1.0.69/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/thiserror-1.0.69/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/thiserror-2.0.20/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/thiserror-2.0.20/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/thiserror-impl-1.0.69/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/thiserror-impl-1.0.69/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/thiserror-impl-2.0.20/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/thiserror-impl-2.0.20/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/time-0.3.55/LICENSE-APACHE", + "sha256": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "size": 10174 + }, + { + "path": "licenses/third-party/time-0.3.55/LICENSE-MIT", + "sha256": "2537228d9a1b44a5dc595241349cae7090b326c8de165aaf89bfddef4a00d0fc", + "size": 1057 + }, + { + "path": "licenses/third-party/time-core-0.1.9/LICENSE-APACHE", + "sha256": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "size": 10174 + }, + { + "path": "licenses/third-party/time-core-0.1.9/LICENSE-MIT", + "sha256": "2537228d9a1b44a5dc595241349cae7090b326c8de165aaf89bfddef4a00d0fc", + "size": 1057 + }, + { + "path": "licenses/third-party/time-macros-0.2.32/LICENSE-APACHE", + "sha256": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "size": 10174 + }, + { + "path": "licenses/third-party/time-macros-0.2.32/LICENSE-MIT", + "sha256": "2537228d9a1b44a5dc595241349cae7090b326c8de165aaf89bfddef4a00d0fc", + "size": 1057 + }, + { + "path": "licenses/third-party/toml-0.8.23/LICENSE-APACHE", + "sha256": "c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08", + "size": 11358 + }, + { + "path": "licenses/third-party/toml-0.8.23/LICENSE-MIT", + "sha256": "6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6", + "size": 1062 + }, + { + "path": "licenses/third-party/toml_datetime-0.6.11/LICENSE-APACHE", + "sha256": "c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08", + "size": 11358 + }, + { + "path": "licenses/third-party/toml_datetime-0.6.11/LICENSE-MIT", + "sha256": "6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6", + "size": 1062 + }, + { + "path": "licenses/third-party/toml_edit-0.22.27/LICENSE-APACHE", + "sha256": "c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08", + "size": 11358 + }, + { + "path": "licenses/third-party/toml_edit-0.22.27/LICENSE-MIT", + "sha256": "6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6", + "size": 1062 + }, + { + "path": "licenses/third-party/typenum-1.20.1/LICENSE", + "sha256": "db11fec9946737df39ca3898d9cd8c10ec6f6c3a884a6802b0ad0b81b4e8f23a", + "size": 17 + }, + { + "path": "licenses/third-party/typenum-1.20.1/LICENSE-APACHE", + "sha256": "516b24e051bf5630880ebbd55c40a25ce9552ebaf8970a53e8976eb70e522406", + "size": 10835 + }, + { + "path": "licenses/third-party/typenum-1.20.1/LICENSE-MIT", + "sha256": "a825bd853ab71619a4923d7b4311221427848070ff44d990da39b0b274c1683f", + "size": 1083 + }, + { + "path": "licenses/third-party/unicode-ident-1.0.26/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/unicode-ident-1.0.26/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/unicode-ident-1.0.26/LICENSE-UNICODE", + "sha256": "f7db81051789b729fea528a63ec4c938fdcb93d9d61d97dc8cc2e9df6d47f2a1", + "size": 1995 + }, + { + "path": "licenses/third-party/unicode-segmentation-1.13.3/COPYRIGHT", + "sha256": "23860c2a7b5d96b21569afedf033469bab9fe14a1b24a35068b8641c578ce24d", + "size": 321 + }, + { + "path": "licenses/third-party/unicode-segmentation-1.13.3/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/unicode-segmentation-1.13.3/LICENSE-MIT", + "sha256": "7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0", + "size": 1071 + }, + { + "path": "licenses/third-party/unicode-truncate-2.0.1/COPYRIGHT", + "sha256": "23860c2a7b5d96b21569afedf033469bab9fe14a1b24a35068b8641c578ce24d", + "size": 321 + }, + { + "path": "licenses/third-party/unicode-truncate-2.0.1/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/unicode-truncate-2.0.1/LICENSE-MIT", + "sha256": "fa226a5c235dafc28fc9303b03f6bf5f7483c31d8355a031ebd6963ffeb65b95", + "size": 1085 + }, + { + "path": "licenses/third-party/unicode-width-0.2.2/COPYRIGHT", + "sha256": "23860c2a7b5d96b21569afedf033469bab9fe14a1b24a35068b8641c578ce24d", + "size": 321 + }, + { + "path": "licenses/third-party/unicode-width-0.2.2/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/unicode-width-0.2.2/LICENSE-MIT", + "sha256": "7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0", + "size": 1071 + }, + { + "path": "licenses/third-party/vcpkg-0.2.15/LICENSE-APACHE", + "sha256": "60c93a31f490375aadf64098b75f10715010379541021e00261045d7800611d3", + "size": 11050 + }, + { + "path": "licenses/third-party/vcpkg-0.2.15/LICENSE-MIT", + "sha256": "016d20f335060a70e79d9fcf8dfaa6201114d65d592211f4bdfb8ae9ca2bc1dc", + "size": 1082 + }, + { + "path": "licenses/third-party/vte-0.15.0/LICENSE-APACHE", + "sha256": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "size": 9723 + }, + { + "path": "licenses/third-party/vte-0.15.0/LICENSE-MIT", + "sha256": "e4c9b06fa850cb9b540a5e400e9f6394cf15efcf4098144de477d1d3dae10150", + "size": 1052 + }, + { + "path": "licenses/third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-APACHE", + "sha256": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "size": 10847 + }, + { + "path": "licenses/third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-APACHE-2.0_WITH_LLVM-exception", + "sha256": "268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5", + "size": 12243 + }, + { + "path": "licenses/third-party/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/third-party/winapi-0.3.9/LICENSE-APACHE", + "sha256": "b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1", + "size": 11357 + }, + { + "path": "licenses/third-party/winapi-0.3.9/LICENSE-MIT", + "sha256": "ce7bc3499fee93d5022ef430d5e4201e79a6d9154f3974e42f41349f0569e09b", + "size": 1073 + }, + { + "path": "licenses/third-party/winapi-i686-pc-windows-gnu-0.4.0/LICENSE-APACHE", + "sha256": "b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1", + "size": 11357 + }, + { + "path": "licenses/third-party/winapi-i686-pc-windows-gnu-0.4.0/LICENSE-MIT", + "sha256": "ce7bc3499fee93d5022ef430d5e4201e79a6d9154f3974e42f41349f0569e09b", + "size": 1073 + }, + { + "path": "licenses/third-party/winapi-x86_64-pc-windows-gnu-0.4.0/LICENSE-APACHE", + "sha256": "b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1", + "size": 11357 + }, + { + "path": "licenses/third-party/winapi-x86_64-pc-windows-gnu-0.4.0/LICENSE-MIT", + "sha256": "ce7bc3499fee93d5022ef430d5e4201e79a6d9154f3974e42f41349f0569e09b", + "size": 1073 + }, + { + "path": "licenses/third-party/windows-link-0.2.1/LICENSE-APACHE-2.0", + "sha256": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "size": 11351 + }, + { + "path": "licenses/third-party/windows-link-0.2.1/LICENSE-MIT", + "sha256": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "size": 1141 + }, + { + "path": "licenses/third-party/windows-sys-0.59.0/LICENSE-APACHE-2.0", + "sha256": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "size": 11351 + }, + { + "path": "licenses/third-party/windows-sys-0.59.0/LICENSE-MIT", + "sha256": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "size": 1141 + }, + { + "path": "licenses/third-party/windows-sys-0.61.2/LICENSE-APACHE-2.0", + "sha256": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "size": 11351 + }, + { + "path": "licenses/third-party/windows-sys-0.61.2/LICENSE-MIT", + "sha256": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "size": 1141 + }, + { + "path": "licenses/third-party/windows-targets-0.52.6/LICENSE-APACHE-2.0", + "sha256": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "size": 11351 + }, + { + "path": "licenses/third-party/windows-targets-0.52.6/LICENSE-MIT", + "sha256": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "size": 1141 + }, + { + "path": "licenses/third-party/windows_aarch64_gnullvm-0.52.6/LICENSE-APACHE-2.0", + "sha256": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "size": 11351 + }, + { + "path": "licenses/third-party/windows_aarch64_gnullvm-0.52.6/LICENSE-MIT", + "sha256": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "size": 1141 + }, + { + "path": "licenses/third-party/windows_aarch64_msvc-0.52.6/LICENSE-APACHE-2.0", + "sha256": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "size": 11351 + }, + { + "path": "licenses/third-party/windows_aarch64_msvc-0.52.6/LICENSE-MIT", + "sha256": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "size": 1141 + }, + { + "path": "licenses/third-party/windows_i686_gnu-0.52.6/LICENSE-APACHE-2.0", + "sha256": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "size": 11351 + }, + { + "path": "licenses/third-party/windows_i686_gnu-0.52.6/LICENSE-MIT", + "sha256": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "size": 1141 + }, + { + "path": "licenses/third-party/windows_i686_gnullvm-0.52.6/LICENSE-APACHE-2.0", + "sha256": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "size": 11351 + }, + { + "path": "licenses/third-party/windows_i686_gnullvm-0.52.6/LICENSE-MIT", + "sha256": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "size": 1141 + }, + { + "path": "licenses/third-party/windows_i686_msvc-0.52.6/LICENSE-APACHE-2.0", + "sha256": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "size": 11351 + }, + { + "path": "licenses/third-party/windows_i686_msvc-0.52.6/LICENSE-MIT", + "sha256": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "size": 1141 + }, + { + "path": "licenses/third-party/windows_x86_64_gnu-0.52.6/LICENSE-APACHE-2.0", + "sha256": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "size": 11351 + }, + { + "path": "licenses/third-party/windows_x86_64_gnu-0.52.6/LICENSE-MIT", + "sha256": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "size": 1141 + }, + { + "path": "licenses/third-party/windows_x86_64_gnullvm-0.52.6/LICENSE-APACHE-2.0", + "sha256": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "size": 11351 + }, + { + "path": "licenses/third-party/windows_x86_64_gnullvm-0.52.6/LICENSE-MIT", + "sha256": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "size": 1141 + }, + { + "path": "licenses/third-party/windows_x86_64_msvc-0.52.6/LICENSE-APACHE-2.0", + "sha256": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "size": 11351 + }, + { + "path": "licenses/third-party/windows_x86_64_msvc-0.52.6/LICENSE-MIT", + "sha256": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "size": 1141 + }, + { + "path": "licenses/third-party/winnow-0.7.15/LICENSE-MIT", + "sha256": "cb5aedb296c5246d1f22e9099f925a65146f9f0d6b4eebba97fd27a6cdbbab2d", + "size": 1023 + }, + { + "path": "licenses/third-party/winreg-0.10.1/LICENSE", + "sha256": "ea021d8c6383c84d19c15ce90d3b1602cf448fdcc0bf3772c98e43c7bf1bf1d1", + "size": 1055 + }, + { + "path": "licenses/third-party/zmij-1.0.23/LICENSE-MIT", + "sha256": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "size": 1023 + }, + { + "path": "licenses/upstream/herdr/LICENSE", + "sha256": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "size": 11357 + }, + { + "path": "rust-toolchain.toml", + "sha256": "980c9752cb40dca3265ce4e3e31de1c369b469c9837be059b806ea93fe76799d", + "size": 86 + }, + { + "path": "schemas/README.md", + "sha256": "5454bbc208a50e33b6c47fd6da5435f6f3fd63f102aee76d0402f763ccfae3cc", + "size": 434 + }, + { + "path": "schemas/config.schema.json", + "sha256": "562d52c46f5fd3879ab9350b8b080a22495c0afd8d3b283c88b43d1f437c79fe", + "size": 4912 + }, + { + "path": "schemas/mutation.schema.json", + "sha256": "f72cbc49e251f11a1a1cf282b900ffd78733ccdd2cfc60c621b58a163e4260a7", + "size": 979 + }, + { + "path": "schemas/regression.schema.json", + "sha256": "c8fa67fd748f9ceacc7c5e30905bbe0cc5f93e95a2112fc9fc811c3ff805089a", + "size": 617 + }, + { + "path": "schemas/task.schema.json", + "sha256": "843a2ea1c7e6f5b69f5c93025aa3073125e98b0d47d989f3da74f6438f2973b8", + "size": 3357 + }, + { + "path": "schemas/workflow.schema.json", + "sha256": "94ede18dcc05df1bb81abf7ca87c17baa121a37c00f8eb25a90a59610dbc088c", + "size": 6213 + }, + { + "path": "scripts/demo.py", + "sha256": "0ac21dfe245ac8ed07c0a462699d1880cb04cf258b0553256b6503946fe61644", + "size": 8578 + }, + { + "path": "scripts/demo_extended.py", + "sha256": "e4c8e52237c26a6083150bc1292a7adf72a5aeb3175944c87904ec3eb3701f22", + "size": 14396 + }, + { + "path": "scripts/generate_source_manifest.py", + "sha256": "fa7328a9d0d0031133c1a517e0ba0d98b45288ce4092a367bc5fb16e4eb9a8f2", + "size": 4362 }, { - "path": "sdk/typescript/README.md", - "sha256": "d615b66de12f36310f48ff492d82f4f2272a6e49244aa9e7d1330bc2f89da20c", - "size": 875 + "path": "scripts/rust_dependency_audit.py", + "sha256": "e51c4b28189e3cb7d08eaef96e370da7b42641a51dc4315339304926bcd62ca7", + "size": 26810 }, { - "path": "sdk/typescript/package.json", - "sha256": "d710424cd2366dbedaf00cc8a13f68692c3a3b0ab9f7817fb9ae5c14919a9990", - "size": 565 + "path": "scripts/test_generate_source_manifest.py", + "sha256": "cc1ffb604dc9b216102cc20aedf8ba89c217973bbdb8a47a0b1e5ef3a949f7a8", + "size": 1597 }, { - "path": "sdk/typescript/src/rover_client.ts", - "sha256": "842eb93b08debc7ac2df5b5d0d0a5199badf99f16c8f22f12cdd66790c5b81cf", - "size": 4070 + "path": "scripts/test_herdr_manifest_audit.py", + "sha256": "99167fe76f68b67855489d49ba269d60241468dc98f14869143b49d9e4d94e9b", + "size": 1694 }, { - "path": "sdk/typescript/test/client.test.ts", - "sha256": "f732299a74f3fbc83906c77eca41a8a28d15922407c1022cfcc5ae3cdc946ad3", - "size": 1398 + "path": "scripts/test_rust_dependency_audit.py", + "sha256": "cfb669f3b5bf949f02a50c16203eaf38c071644acb2bdc8977fba0619b62861e", + "size": 9200 } ] } From 9a7b8ac540350e12b3678409cd0c51e9cea077a2 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 05:03:36 +0530 Subject: [PATCH 21/23] fix(rust): clear clippy pedantic lints in the Linux-only /proc reader The first hosted `rust` job failed `make rust-clippy` on ubuntu-24.04: three clippy pedantic lints (similar_names, question_mark, inefficient_to_string) sit in `cfg(target_os = "linux")` code in rover-execution's PTY foreground-process sampler, which macOS clippy never compiles. Rename the `pgid` parameter to `foreground_group`, replace a `let ... else { return None }` with `?` (same semantics in an Option-returning function), and build the start-time String from the dereferenced &str. Behaviour is unchanged. Verified locally with `cargo +1.88.0 clippy --workspace --all-targets --locked --offline --target x86_64-unknown-linux-gnu -- -D warnings`, which reproduces the three CI errors on the old code and passes on the new code. Co-Authored-By: Claude Opus 5.5 --- crates/rover-execution/src/pty.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/rover-execution/src/pty.rs b/crates/rover-execution/src/pty.rs index b8e1d75..18a77bc 100644 --- a/crates/rover-execution/src/pty.rs +++ b/crates/rover-execution/src/pty.rs @@ -266,8 +266,10 @@ impl PtySession { } #[cfg(target_os = "linux")] -fn linux_foreground_process_group_details(pgid: i32) -> Option> { - if pgid <= 0 { +fn linux_foreground_process_group_details( + foreground_group: i32, +) -> Option> { + if foreground_group <= 0 { return None; } let entries = std::fs::read_dir("/proc").ok()?; @@ -292,7 +294,7 @@ fn linux_foreground_process_group_details(pgid: i32) -> Option Option Option Option<(i32, String)> { let fields = fields.split_ascii_whitespace().collect::>(); // The first field after the command is state (field 3); pgrp is field 5 // and starttime is field 22 in procfs' stat format. - Some((fields.get(2)?.parse().ok()?, fields.get(19)?.to_string())) + Some((fields.get(2)?.parse().ok()?, String::from(*fields.get(19)?))) } #[cfg(target_os = "macos")] From 43eea4affa0d219770c6d74b00d75e0eaf5143c3 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 05:03:37 +0530 Subject: [PATCH 22/23] chore(manifest): regenerate SOURCE_MANIFEST.json after the pty.rs lint fix Co-Authored-By: Claude Opus 5.5 --- SOURCE_MANIFEST.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SOURCE_MANIFEST.json b/SOURCE_MANIFEST.json index b32aaf7..5612aec 100644 --- a/SOURCE_MANIFEST.json +++ b/SOURCE_MANIFEST.json @@ -354,8 +354,8 @@ }, { "path": "crates/rover-execution/src/pty.rs", - "sha256": "5e232ae5b8d9ea88d4d79277e254e9ff46d9fbcca98c63b4f7d302b03f642817", - "size": 28648 + "sha256": "7a929120ef0d3e628a0f4862df8dad20d10ec60fa5e9ea99742ba9b56a3f6fac", + "size": 28671 }, { "path": "crates/rover-execution/src/sessions.rs", From 39026d4a2f82a6901237e34cbc33b4761db33700 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sun, 27 Sep 2026 05:07:28 +0530 Subject: [PATCH 23/23] docs(ci): record the measured cold-cache duration of both CI jobs The first green hosted run of the split workflow (run 36279833786) finished the rust job in 1 min 22 s with no cache (clippy build 27.6 s, test build 31.4 s, 319 Linux tests) and the go job in 2 min 40 s. SOURCE_MANIFEST.json regenerated for the doc change. Refs: F069 Co-Authored-By: Claude Opus 5.5 --- SOURCE_MANIFEST.json | 4 ++-- docs/CI.md | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/SOURCE_MANIFEST.json b/SOURCE_MANIFEST.json index 5612aec..15847f1 100644 --- a/SOURCE_MANIFEST.json +++ b/SOURCE_MANIFEST.json @@ -459,8 +459,8 @@ }, { "path": "docs/CI.md", - "sha256": "b232cb39b6a29d1d6f032851c9a53ec430f9393bd3afe1d966f0d39b33184f8c", - "size": 5386 + "sha256": "6deec3fb4bdf2f1402b0f971e25cd602e1892a4d2688e6284659366e572a8b51", + "size": 5594 }, { "path": "docs/CLI.md", diff --git a/docs/CI.md b/docs/CI.md index 07bd075..aa51b8e 100644 --- a/docs/CI.md +++ b/docs/CI.md @@ -39,8 +39,13 @@ The Rust workspace under `crates/` is a preview gated by All Rust steps take `CARGO='cargo +1.88.0'`; the audit script honours the same `CARGO` value. The Rust SBOM is not attached to releases because releases ship -only the Go binaries. Cold-cache duration on the hosted runner has not been measured -yet; record it here after the first run on `main`. +only the Go binaries. + +Measured duration: the first cold-cache run +([run 36279833786](https://github.com/GrayCodeAI/rover/actions/runs/36279833786), +2026-09-26) took 1 min 22 s for the whole `rust` job: clippy build 27.6 s, +test build 31.4 s, 319 tests on Linux. The `go` job took 2 min 40 s in the same +run. Both are far below their timeouts. ### Cross-compilation matrix