diff --git a/AGENTS.md b/AGENTS.md index 49def6b4..618bd079 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,22 +2,30 @@ ## Project Purpose -Term Challenge is a WASM evaluation module for AI agents on the Bittensor network via platform-v2. Miners submit Python agent packages (as zip files) that solve SWE-bench tasks. The WASM module runs inside platform-v2 validators to validate submissions, evaluate task results, and compute scores. +Term Challenge is a WASM evaluation module for AI agents on the Bittensor network via platform-v2. Miners submit Python agent packages (as zip files) that solve SWE-bench tasks. The WASM module runs inside platform-v2 validators to validate submissions, evaluate task results, and compute scores. A companion native CLI (`term-cli`) provides a TUI for monitoring leaderboards, evaluation progress, and network health. ## Architecture Overview ``` term-challenge/ -├── Cargo.toml # workspace with members = ["wasm"] +├── Cargo.toml # workspace with members = ["wasm", "cli"] ├── wasm/ │ ├── Cargo.toml # cdylib, depends on platform-challenge-sdk-wasm │ └── src/ -│ ├── lib.rs # Challenge impl + register_challenge! -│ ├── types.rs # Submission, TaskDefinition, DecayParams, etc. -│ ├── scoring.rs # Aggregate scoring, decay, weight calculation -│ ├── tasks.rs # Active dataset storage (SWE-bench tasks) -│ ├── dataset.rs # Dataset selection and consensus logic -│ └── routes.rs # Challenge route definitions for RPC +│ ├── lib.rs # Challenge impl + register_challenge! +│ ├── types.rs # Submission, TaskDefinition, AgentLogs, etc. +│ ├── scoring.rs # Aggregate scoring, decay, weight calculation +│ ├── tasks.rs # Active dataset storage (SWE-bench tasks) +│ ├── dataset.rs # Dataset selection and consensus logic +│ ├── routes.rs # Challenge route definitions for RPC +│ └── agent_storage.rs # Agent code & log storage functions +├── cli/ +│ ├── Cargo.toml # native binary, ratatui TUI +│ └── src/ +│ ├── main.rs # Entry point, event loop +│ ├── app.rs # Application state +│ ├── ui.rs # Ratatui UI rendering +│ └── rpc.rs # JSON-RPC 2.0 client ├── AGENTS.md ├── README.md ├── LICENSE @@ -32,7 +40,9 @@ term-challenge/ 3. **Validators** run WASM `validate()` — checks signature, epoch rate limit, Basilica metadata 4. **50% validator approval** → submission stored in blockchain 5. **Validators** run WASM `evaluate()` — scores task results, applies LLM judge -6. **Consensus** aggregates scores, applies decay, submits weights to Bittensor +6. **Agent code & logs** stored on-chain for auditability (code ≤ 1MB, logs ≤ 256KB) +7. **Log consensus** — validators propose logs, >50% hash agreement required +8. **Consensus** aggregates scores, applies decay, submits weights to Bittensor ### Key Concepts @@ -42,9 +52,63 @@ term-challenge/ - **Epoch rate limiting**: 1 submission per 3 epochs per miner - **Top agent decay**: 72h grace period, then 50% daily decay to 0 weight +## Agent Code Storage + +Agent submissions are stored on-chain for auditability and retrieval. The `agent_storage` module manages three storage categories: + +| Storage Key Format | Content | Max Size | +|---|---|---| +| `agent_code::` | Raw zip package bytes | 1 MB (1,048,576 bytes) | +| `agent_hash::` | Hash of the agent package | — | +| `agent_logs::` | Serialized `AgentLogs` struct | 256 KB (262,144 bytes) | + +- **Package size limit**: Submissions with `package_zip` exceeding 1 MB are rejected at the storage layer. +- **Log size limit**: Serialized logs exceeding 256 KB are rejected. Individual task output previews are truncated to 4 KB (4,096 bytes) before storage. +- **Key format**: Keys are constructed as `:` using little-endian encoding for the epoch. + +## CLI + +The `term-cli` crate is a **native binary** (NOT `no_std`) that provides a terminal user interface for monitoring the term-challenge network. + +### Design + +- **Framework**: Built with [ratatui](https://ratatui.rs/) for TUI rendering +- **Transport**: Connects to validators via JSON-RPC 2.0 over HTTP +- **Target**: Standard `x86_64` / `aarch64` native targets (not WASM) + +### Available Tabs + +| Tab | Description | +|---|---| +| Leaderboard | Current scores, ranks, and miner hotkeys | +| Evaluation | Live evaluation progress for pending submissions | +| Submission | Recent submission history and status | +| Network | Validator count, epoch info, system health | + +### Keyboard Shortcuts + +| Key | Action | +|---|---| +| `Tab` / `Shift+Tab` | Switch between tabs | +| `↑` / `↓` / `j` / `k` | Navigate rows | +| `r` | Refresh data | +| `q` / `Esc` | Quit | + +### RPC Methods Used + +- `epoch_current` — Current epoch number +- `challenge_call /leaderboard` — Leaderboard data +- `evaluation_getProgress` — Evaluation progress for a submission +- `agent_getLogs` — Validated evaluation logs +- `system_health` — Node health status +- `validator_count` — Number of active validators + ## Build Commands ```bash +# Build CLI (native) +cargo build --release -p term-cli + # Build WASM module cargo build --release --target wasm32-unknown-unknown -p term-challenge-wasm @@ -70,17 +134,20 @@ Git hooks live in `.githooks/` and are activated with `git config core.hooksPath 5. **Host functions are the ONLY external interface.** No direct HTTP, no filesystem, no std::net. 6. **Do NOT add `#[allow(dead_code)]` broadly.** Fix unused code or remove it. +> **Note:** The `cli/` crate is exempt from the `no_std` rule (rule 1) and the host-functions-only rule (rule 5) since it is a native binary that runs outside the WASM sandbox. Rules 2, 3, 4, and 6 still apply to CLI code. + ## DO / DO NOT ### DO -- Use `alloc::string::String`, `alloc::vec::Vec`, `alloc::collections::BTreeMap` -- Use `serde` with `default-features = false, features = ["derive", "alloc"]` -- Use `bincode` with `default-features = false` for serialization -- Use host functions for all I/O: `host_storage_get/set`, `host_http_post`, `host_consensus_get_epoch` +- Use `alloc::string::String`, `alloc::vec::Vec`, `alloc::collections::BTreeMap` (WASM code) +- Use `serde` with `default-features = false, features = ["derive", "alloc"]` (WASM code) +- Use `bincode` with `default-features = false` for serialization (WASM code) +- Use host functions for all I/O: `host_storage_get/set`, `host_http_post`, `host_consensus_get_epoch` (WASM code) - Keep the `register_challenge!` macro ABI contract intact +- Use standard `std` library features in the `cli/` crate (it is a native binary) ### DO NOT -- Do NOT use `std::`, `println!`, `std::collections::HashMap` +- Do NOT use `std::`, `println!`, `std::collections::HashMap` in WASM code - Do NOT add heavy dependencies — the WASM module must stay minimal - Do NOT break the WASM ABI (evaluate, validate, get_name, get_version, get_tasks, configure, alloc) - Do NOT store sensitive data in plain text in blockchain storage diff --git a/Cargo.lock b/Cargo.lock index b22566b5..71bc5197 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,104 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bincode" version = "1.3.3" @@ -12,84 +110,2071 @@ dependencies = [ ] [[package]] -name = "platform-challenge-sdk-wasm" -version = "0.1.0" -source = "git+https://github.com/PlatformNetwork/platform-v2?branch=main#ae9b049a3abbaf9808aca38fdc8a782f813ce734" +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" dependencies = [ - "bincode", + "rustversion", +] + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", "serde", + "wasm-bindgen", + "windows-link", ] [[package]] -name = "proc-macro2" -version = "1.0.106" +name = "clap" +version = "4.5.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "c5caf74d17c3aec5495110c34cc3f78644bfa89af6c8993ed4de2790e49b6499" dependencies = [ - "unicode-ident", + "clap_builder", + "clap_derive", ] [[package]] -name = "quote" -version = "1.0.44" +name = "clap_builder" +version = "4.5.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "370daa45065b80218950227371916a1633217ae42b2715b2287b606dcd618e24" dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +dependencies = [ + "heck", "proc-macro2", + "quote", + "syn", ] [[package]] -name = "serde" -version = "1.0.228" +name = "clap_lex" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "compact_str" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" dependencies = [ - "serde_core", - "serde_derive", + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", ] [[package]] -name = "serde_core" -version = "1.0.228" +name = "core-foundation-sys" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "serde_derive", + "bitflags", + "crossterm_winapi", + "mio", + "parking_lot", + "rustix", + "signal-hook", + "signal-hook-mio", + "winapi", ] [[package]] -name = "serde_derive" -version = "1.0.228" +name = "crossterm_winapi" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" dependencies = [ + "ident_case", "proc-macro2", "quote", + "strsim", "syn", ] [[package]] -name = "syn" -version = "2.0.116" +name = "darling_macro" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "syn", ] [[package]] -name = "term-challenge-wasm" -version = "0.1.0" +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[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 = [ - "bincode", - "platform-challenge-sdk-wasm", - "serde", + "libc", + "windows-sys 0.61.2", ] [[package]] -name = "unicode-ident" -version = "1.0.24" +name = "find-msvc-tools" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +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 = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[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.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" +dependencies = [ + "darling", + "indoc", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "js-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[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.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[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.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[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 = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "platform-challenge-sdk-wasm" +version = "0.1.0" +source = "git+https://github.com/PlatformNetwork/platform-v2?branch=main#ae9b049a3abbaf9808aca38fdc8a782f813ce734" +dependencies = [ + "bincode", + "serde", +] + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "ratatui" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" +dependencies = [ + "bitflags", + "cassowary", + "compact_str", + "crossterm", + "indoc", + "instability", + "itertools", + "lru", + "paste", + "strum", + "unicode-segmentation", + "unicode-truncate", + "unicode-width 0.2.0", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[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 = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[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-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[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 = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[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.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "term-challenge-wasm" +version = "0.1.0" +dependencies = [ + "bincode", + "platform-challenge-sdk-wasm", + "serde", +] + +[[package]] +name = "term-cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "clap", + "crossterm", + "ratatui", + "reqwest", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-truncate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools", + "unicode-segmentation", + "unicode-width 0.1.14", +] + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +dependencies = [ + "rustls-pki-types", +] + +[[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-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +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 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[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_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[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_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[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_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[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_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[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_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index f8971519..f34f0296 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] resolver = "2" -members = ["wasm"] +members = ["wasm", "cli"] +default-members = ["wasm"] [workspace.package] version = "0.1.0" diff --git a/README.md b/README.md index 4f82c6b6..8c583c30 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,94 @@ Term Challenge is a WASM evaluation module for AI agents on the Bittensor network. It runs inside [platform-v2](https://github.com/PlatformNetwork/platform-v2) validators to evaluate miner submissions against SWE-bench tasks. +--- + +## System Architecture + +```mermaid +flowchart LR + Miner[Miner] -->|Submit Agent ZIP| RPC[Validator RPC] + RPC --> Validators[Validator Network] + Validators --> WASM[term-challenge WASM] + Validators --> Executor[term-executor] + Executor -->|Task Results| Validators + Validators -->|Scores + Weights| BT[Bittensor Chain] + CLI[term-cli TUI] -->|JSON-RPC| RPC + CLI -->|Display| Monitor[Leaderboard / Progress / Logs] +``` + +--- + +## Evaluation Flow + +```mermaid +sequenceDiagram + participant M as Miner + participant V as Validators + participant W as WASM Module + participant E as term-executor + participant BT as Bittensor + + M->>V: Submit agent zip + metadata + V->>W: validate(submission) + W-->>V: approved (>50% consensus) + V->>E: Execute agent on SWE-bench tasks + E-->>V: Task results + scores + V->>W: evaluate(results) + W-->>V: Aggregate score + weight + V->>V: Store agent code & logs + V->>V: Log consensus (>50% agreement) + V->>BT: Submit weights at epoch boundary +``` + +--- + +## CLI Data Flow + +```mermaid +flowchart TB + CLI[term-cli] -->|epoch_current| RPC[Validator RPC] + CLI -->|challenge_call /leaderboard| RPC + CLI -->|evaluation_getProgress| RPC + CLI -->|agent_getLogs| RPC + CLI -->|system_health| RPC + CLI -->|validator_count| RPC + RPC --> State[Chain State] + State --> LB[Leaderboard Data] + State --> Eval[Evaluation Progress] + State --> Logs[Validated Logs] +``` + +--- + +## Agent Log Consensus + +```mermaid +flowchart LR + V1[Validator 1] -->|Log Proposal| P2P[(P2P Network)] + V2[Validator 2] -->|Log Proposal| P2P + V3[Validator 3] -->|Log Proposal| P2P + P2P --> Consensus{Hash Match >50%?} + Consensus -->|Yes| Store[Validated Logs] + Consensus -->|No| Reject[Rejected] +``` + +--- + +## Agent Code Storage + +```mermaid +flowchart TB + Submit[Agent Submission] --> Validate{package_zip ≤ 1MB?} + Validate -->|Yes| Store[Blockchain Storage] + Validate -->|No| Reject[Rejected] + Store --> Code[agent_code:hotkey:epoch] + Store --> Hash[agent_hash:hotkey:epoch] + Store --> Logs[agent_logs:hotkey:epoch ≤ 256KB] +``` + +--- + ## Features - **WASM Module**: Compiles to `wasm32-unknown-unknown`, loaded by platform-v2 validators @@ -20,6 +108,11 @@ Term Challenge is a WASM evaluation module for AI agents on the Bittensor networ - **Top Agent Decay**: 72h grace period, 50% daily decay to 0 weight - **P2P Dataset Consensus**: Validators collectively select 50 evaluation tasks - **Zip Package Submissions**: Agents submitted as zip packages (no compilation step) +- **Agent Code Storage**: Submitted agent packages (≤ 1MB) stored on-chain with hash verification +- **Log Consensus**: Evaluation logs validated across validators with >50% hash agreement +- **CLI (term-cli)**: Native TUI for monitoring leaderboards, evaluation progress, submissions, and network health + +--- ## Building @@ -29,33 +122,71 @@ cargo build --release --target wasm32-unknown-unknown -p term-challenge-wasm # The output .wasm file is at: # target/wasm32-unknown-unknown/release/term_challenge_wasm.wasm + +# Build CLI (native) +cargo build --release -p term-cli ``` +--- + ## Architecture -This repository contains ONLY the WASM evaluation module. All infrastructure (P2P networking, RPC server, blockchain storage, validator coordination) is provided by [platform-v2](https://github.com/PlatformNetwork/platform-v2). +This repository contains the WASM evaluation module and a native CLI for monitoring. All infrastructure (P2P networking, RPC server, blockchain storage, validator coordination) is provided by [platform-v2](https://github.com/PlatformNetwork/platform-v2). ``` term-challenge/ -├── wasm/ # WASM evaluation module +├── wasm/ # WASM evaluation module +│ └── src/ +│ ├── lib.rs # Challenge trait implementation +│ ├── types.rs # Submission, task, and config types +│ ├── scoring.rs # Score aggregation and decay +│ ├── tasks.rs # Active dataset management +│ ├── dataset.rs # Dataset selection consensus +│ ├── routes.rs # RPC route definitions +│ └── agent_storage.rs # Agent code & log storage functions +├── cli/ # Native TUI monitoring tool │ └── src/ -│ ├── lib.rs # Challenge trait implementation -│ ├── types.rs # Submission, task, and config types -│ ├── scoring.rs # Score aggregation and decay -│ ├── tasks.rs # Active dataset management -│ ├── dataset.rs # Dataset selection consensus -│ └── routes.rs # RPC route definitions -├── AGENTS.md # Development guide +│ ├── main.rs # Entry point, event loop +│ ├── app.rs # Application state +│ ├── ui.rs # Ratatui UI rendering +│ └── rpc.rs # JSON-RPC 2.0 client +├── AGENTS.md # Development guide └── README.md ``` +--- + ## How It Works 1. Miners submit zip packages with agent code and SWE-bench task results 2. Platform-v2 validators load this WASM module 3. `validate()` checks signatures, epoch rate limits, and Basilica metadata 4. `evaluate()` scores task results and applies LLM judge scoring -5. Scores are aggregated via P2P consensus and submitted to Bittensor +5. Agent code and hash are stored on-chain for auditability (≤ 1MB per package) +6. Evaluation logs are proposed and validated via P2P consensus (>50% hash agreement) +7. Scores are aggregated via P2P consensus and submitted to Bittensor + +--- + +## CLI Usage + +```bash +# Install via platform CLI +platform download term-challenge + +# Or build from source +cargo build --release -p term-cli + +# Run the TUI +term-cli --rpc-url http://chain.platform.network:9944 + +# With miner hotkey filter +term-cli --hotkey 5GrwvaEF... --tab leaderboard + +# Available tabs: leaderboard, evaluation, submission, network +``` + +--- ## License diff --git a/cli/Cargo.toml b/cli/Cargo.toml new file mode 100644 index 00000000..16798f40 --- /dev/null +++ b/cli/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "term-cli" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +description = "Terminal Benchmark Challenge CLI — ratatui TUI for monitoring" + +[[bin]] +name = "term-cli" +path = "src/main.rs" + +[dependencies] +ratatui = "0.29" +crossterm = "0.28" +tokio = { version = "1.40", features = ["full"] } +reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +clap = { version = "4.5", features = ["derive"] } +anyhow = "1.0" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +chrono = { version = "0.4", features = ["serde"] } diff --git a/cli/src/app.rs b/cli/src/app.rs new file mode 100644 index 00000000..00c5a449 --- /dev/null +++ b/cli/src/app.rs @@ -0,0 +1,206 @@ +use crate::rpc::RpcClient; +use chrono::{DateTime, Utc}; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum Tab { + Leaderboard, + Evaluation, + Submission, + Network, +} + +impl Tab { + pub const ALL: [Tab; 4] = [ + Tab::Leaderboard, + Tab::Evaluation, + Tab::Submission, + Tab::Network, + ]; + + pub fn label(self) -> &'static str { + match self { + Tab::Leaderboard => "Leaderboard", + Tab::Evaluation => "Evaluation", + Tab::Submission => "Submission", + Tab::Network => "Network", + } + } + + pub fn index(self) -> usize { + match self { + Tab::Leaderboard => 0, + Tab::Evaluation => 1, + Tab::Submission => 2, + Tab::Network => 3, + } + } +} + +pub struct LeaderboardRow { + pub rank: u32, + pub miner_hotkey: String, + pub score: f64, + pub pass_rate: f64, + pub submissions: u32, + pub last_submission: String, +} + +pub struct EvalTaskRow { + pub task_id: String, + pub status: String, + pub score: f64, + pub duration_ms: u64, + pub error: Option, +} + +pub struct NetworkStatus { + pub epoch: u64, + pub phase: String, + pub block_height: u64, + pub validators: usize, + pub connected: bool, +} + +impl Default for NetworkStatus { + fn default() -> Self { + Self { + epoch: 0, + phase: "unknown".to_string(), + block_height: 0, + validators: 0, + connected: false, + } + } +} + +pub struct App { + pub tab: Tab, + pub rpc_url: String, + pub hotkey: Option, + pub challenge_id: Option, + pub leaderboard: Vec, + pub evaluation_progress: Vec, + pub network_status: NetworkStatus, + pub scroll_offset: usize, + pub last_refresh: Option>, + pub error_message: Option, + pub should_quit: bool, +} + +impl App { + pub fn new(rpc_url: String, hotkey: Option, challenge_id: Option) -> Self { + Self { + tab: Tab::Leaderboard, + rpc_url, + hotkey, + challenge_id, + leaderboard: Vec::new(), + evaluation_progress: Vec::new(), + network_status: NetworkStatus::default(), + scroll_offset: 0, + last_refresh: None, + error_message: None, + should_quit: false, + } + } + + pub fn set_tab_from_str(&mut self, s: &str) { + self.tab = match s.to_lowercase().as_str() { + "leaderboard" => Tab::Leaderboard, + "evaluation" => Tab::Evaluation, + "submission" => Tab::Submission, + "network" => Tab::Network, + _ => Tab::Leaderboard, + }; + self.scroll_offset = 0; + } + + pub fn next_tab(&mut self) { + let idx = self.tab.index(); + let next = (idx + 1) % Tab::ALL.len(); + self.tab = Tab::ALL[next]; + self.scroll_offset = 0; + } + + pub fn prev_tab(&mut self) { + let idx = self.tab.index(); + let prev = if idx == 0 { + Tab::ALL.len() - 1 + } else { + idx - 1 + }; + self.tab = Tab::ALL[prev]; + self.scroll_offset = 0; + } + + pub fn scroll_up(&mut self) { + self.scroll_offset = self.scroll_offset.saturating_sub(1); + } + + pub fn scroll_down(&mut self) { + self.scroll_offset = self.scroll_offset.saturating_add(1); + } + + pub async fn refresh(&mut self, rpc: &RpcClient) { + self.error_message = None; + + if let Err(e) = self.refresh_network(rpc).await { + self.error_message = Some(format!("Network: {e}")); + self.network_status.connected = false; + return; + } + self.network_status.connected = true; + + if self.challenge_id.is_none() { + match rpc.fetch_challenge_list().await { + Ok(challenges) if challenges.len() == 1 => { + self.challenge_id = Some(challenges[0].id.clone()); + } + Ok(_) => {} + Err(e) => { + self.error_message = Some(format!("Challenges: {e}")); + } + } + } + + if let Some(cid) = &self.challenge_id { + let cid = cid.clone(); + match rpc.fetch_leaderboard(&cid).await { + Ok(rows) => self.leaderboard = rows, + Err(e) => { + self.error_message = Some(format!("Leaderboard: {e}")); + } + } + } + + if let Some(hotkey) = &self.hotkey { + let hotkey = hotkey.clone(); + match rpc.fetch_evaluation_progress(&hotkey).await { + Ok(tasks) => self.evaluation_progress = tasks, + Err(e) => { + tracing::debug!("Evaluation progress: {e}"); + } + } + } + + self.last_refresh = Some(Utc::now()); + } + + async fn refresh_network(&mut self, rpc: &RpcClient) -> anyhow::Result<()> { + let _ = rpc.fetch_system_health().await?; + + let epoch_info = rpc.fetch_epoch_info().await?; + self.network_status.epoch = epoch_info.epoch; + self.network_status.phase = epoch_info.phase; + self.network_status.block_height = epoch_info.block_height; + + match rpc.fetch_validator_count().await { + Ok(count) => self.network_status.validators = count, + Err(e) => { + tracing::warn!("Failed to fetch validator count: {e}"); + } + } + + Ok(()) + } +} diff --git a/cli/src/main.rs b/cli/src/main.rs new file mode 100644 index 00000000..cc419a42 --- /dev/null +++ b/cli/src/main.rs @@ -0,0 +1,105 @@ +mod app; +mod rpc; +mod ui; + +use std::time::{Duration, Instant}; + +use anyhow::Result; +use clap::Parser; +use crossterm::event::{self, Event, KeyCode, KeyEventKind}; +use tracing_subscriber::EnvFilter; + +use crate::app::App; +use crate::rpc::RpcClient; + +#[derive(Parser)] +#[command(name = "term-cli", about = "Terminal Benchmark Challenge Monitor")] +struct Cli { + /// RPC endpoint URL + #[arg(long, default_value = "http://chain.platform.network:9944")] + rpc_url: String, + + /// Your miner hotkey for filtered views + #[arg(long)] + hotkey: Option, + + /// Challenge ID (auto-detected if single challenge) + #[arg(long)] + challenge_id: Option, + + /// Initial tab to display + #[arg(long, default_value = "leaderboard")] + tab: String, +} + +#[tokio::main] +async fn main() -> Result<()> { + let cli = Cli::parse(); + + tracing_subscriber::fmt() + .with_env_filter( + EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("warn")), + ) + .with_writer(std::io::stderr) + .init(); + + let mut terminal = ratatui::try_init()?; + + let result = run(&mut terminal, cli).await; + + ratatui::try_restore()?; + + result +} + +async fn run(terminal: &mut ratatui::DefaultTerminal, cli: Cli) -> Result<()> { + let mut app = App::new(cli.rpc_url.clone(), cli.hotkey, cli.challenge_id); + app.set_tab_from_str(&cli.tab); + + let rpc = RpcClient::new(&cli.rpc_url); + + app.refresh(&rpc).await; + + let tick_rate = Duration::from_secs(10); + let mut last_tick = Instant::now(); + + loop { + terminal.draw(|f| ui::draw(f, &app))?; + + let timeout = tick_rate + .checked_sub(last_tick.elapsed()) + .unwrap_or_default(); + + if event::poll(timeout)? { + if let Event::Key(key) = event::read()? { + if key.kind == KeyEventKind::Press { + match key.code { + KeyCode::Char('q') => { + app.should_quit = true; + } + KeyCode::Tab => app.next_tab(), + KeyCode::BackTab => app.prev_tab(), + KeyCode::Up => app.scroll_up(), + KeyCode::Down => app.scroll_down(), + KeyCode::Char('r') => { + app.refresh(&rpc).await; + last_tick = Instant::now(); + } + _ => {} + } + } + } + } + + if app.should_quit { + break; + } + + if last_tick.elapsed() >= tick_rate { + app.refresh(&rpc).await; + last_tick = Instant::now(); + } + } + + Ok(()) +} diff --git a/cli/src/rpc.rs b/cli/src/rpc.rs new file mode 100644 index 00000000..350cd15c --- /dev/null +++ b/cli/src/rpc.rs @@ -0,0 +1,217 @@ +use std::sync::atomic::{AtomicU64, Ordering}; + +use anyhow::{anyhow, Context}; +use serde::{Deserialize, Serialize}; + +use crate::app::{EvalTaskRow, LeaderboardRow}; + +pub struct RpcClient { + url: String, + client: reqwest::Client, + request_id: AtomicU64, +} + +#[derive(Serialize)] +struct JsonRpcRequest<'a> { + jsonrpc: &'a str, + id: u64, + method: &'a str, + params: serde_json::Value, +} + +#[derive(Deserialize)] +struct JsonRpcResponse { + result: Option, + error: Option, + #[allow(dead_code)] + id: Option, +} + +#[derive(Deserialize)] +struct JsonRpcError { + code: i64, + message: String, +} + +pub struct EpochInfo { + pub epoch: u64, + pub phase: String, + pub block_height: u64, +} + +#[derive(Deserialize)] +struct EpochInfoRaw { + #[serde(default)] + epoch: u64, + #[serde(default)] + phase: String, + #[serde(default)] + block_height: u64, +} + +pub struct ChallengeInfo { + pub id: String, +} + +#[derive(Deserialize)] +struct ChallengeInfoRaw { + #[serde(default)] + id: String, +} + +#[derive(Deserialize)] +struct LeaderboardRowRaw { + #[serde(default)] + rank: u32, + #[serde(default)] + miner_hotkey: String, + #[serde(default)] + score: f64, + #[serde(default)] + pass_rate: f64, + #[serde(default)] + submissions: u32, + #[serde(default)] + last_submission: String, +} + +#[derive(Deserialize)] +struct EvalTaskRowRaw { + #[serde(default)] + task_id: String, + #[serde(default)] + status: String, + #[serde(default)] + score: f64, + #[serde(default)] + duration_ms: u64, + #[serde(default)] + error: Option, +} + +impl RpcClient { + pub fn new(url: &str) -> Self { + Self { + url: url.to_string(), + client: reqwest::Client::new(), + request_id: AtomicU64::new(1), + } + } + + pub async fn call( + &self, + method: &str, + params: serde_json::Value, + ) -> anyhow::Result { + let id = self.request_id.fetch_add(1, Ordering::Relaxed); + let request = JsonRpcRequest { + jsonrpc: "2.0", + id, + method, + params, + }; + + let response = self + .client + .post(&self.url) + .json(&request) + .send() + .await + .context("Failed to send RPC request")?; + + let status = response.status(); + if !status.is_success() { + return Err(anyhow!("RPC HTTP error: {status}")); + } + + let rpc_response: JsonRpcResponse = response + .json() + .await + .context("Failed to parse RPC response")?; + + if let Some(err) = rpc_response.error { + return Err(anyhow!("RPC error {}: {}", err.code, err.message)); + } + + rpc_response + .result + .ok_or_else(|| anyhow!("RPC response missing result")) + } + + pub async fn fetch_leaderboard( + &self, + challenge_id: &str, + ) -> anyhow::Result> { + let params = serde_json::json!({ + "challenge_id": challenge_id, + "path": "/leaderboard" + }); + let result = self.call("challenge_call", params).await?; + let raw: Vec = + serde_json::from_value(result).context("Failed to parse leaderboard data")?; + Ok(raw + .into_iter() + .map(|r| LeaderboardRow { + rank: r.rank, + miner_hotkey: r.miner_hotkey, + score: r.score, + pass_rate: r.pass_rate, + submissions: r.submissions, + last_submission: r.last_submission, + }) + .collect()) + } + + pub async fn fetch_epoch_info(&self) -> anyhow::Result { + let result = self.call("epoch_current", serde_json::json!({})).await?; + let raw: EpochInfoRaw = + serde_json::from_value(result).context("Failed to parse epoch info")?; + Ok(EpochInfo { + epoch: raw.epoch, + phase: raw.phase, + block_height: raw.block_height, + }) + } + + pub async fn fetch_system_health(&self) -> anyhow::Result { + self.call("system_health", serde_json::json!({})).await + } + + pub async fn fetch_validator_count(&self) -> anyhow::Result { + let result = self.call("validator_count", serde_json::json!({})).await?; + let count = result.as_u64().unwrap_or_default() as usize; + Ok(count) + } + + pub async fn fetch_evaluation_progress( + &self, + submission_id: &str, + ) -> anyhow::Result> { + let params = serde_json::json!({ + "submission_id": submission_id + }); + let result = self.call("evaluation_getProgress", params).await?; + let raw: Vec = + serde_json::from_value(result).context("Failed to parse evaluation progress")?; + Ok(raw + .into_iter() + .map(|r| EvalTaskRow { + task_id: r.task_id, + status: r.status, + score: r.score, + duration_ms: r.duration_ms, + error: r.error, + }) + .collect()) + } + + pub async fn fetch_challenge_list(&self) -> anyhow::Result> { + let result = self.call("challenge_list", serde_json::json!({})).await?; + let raw: Vec = + serde_json::from_value(result).context("Failed to parse challenge list")?; + Ok(raw + .into_iter() + .map(|r| ChallengeInfo { id: r.id }) + .collect()) + } +} diff --git a/cli/src/ui.rs b/cli/src/ui.rs new file mode 100644 index 00000000..54fea87c --- /dev/null +++ b/cli/src/ui.rs @@ -0,0 +1,384 @@ +use ratatui::{ + layout::{Constraint, Direction, Layout, Rect}, + style::{Color, Modifier, Style, Stylize}, + text::{Line, Span}, + widgets::{Block, Borders, Cell, Paragraph, Row, Table, Tabs}, + Frame, +}; + +use crate::app::{App, Tab}; + +pub fn draw(frame: &mut Frame, app: &App) { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Length(3), + Constraint::Min(0), + Constraint::Length(3), + ]) + .split(frame.area()); + + draw_tabs(frame, chunks[0], app); + + match app.tab { + Tab::Leaderboard => draw_leaderboard(frame, chunks[1], app), + Tab::Evaluation => draw_evaluation(frame, chunks[1], app), + Tab::Submission => draw_submission(frame, chunks[1], app), + Tab::Network => draw_network(frame, chunks[1], app), + } + + draw_status_bar(frame, chunks[2], app); +} + +fn draw_tabs(frame: &mut Frame, area: Rect, app: &App) { + let titles: Vec<&str> = Tab::ALL.iter().map(|t| t.label()).collect(); + let tabs = Tabs::new(titles) + .block(Block::default().borders(Borders::ALL).title("Term CLI")) + .select(app.tab.index()) + .style(Style::default().fg(Color::Gray)) + .highlight_style( + Style::default() + .fg(Color::Yellow) + .add_modifier(Modifier::BOLD), + ); + frame.render_widget(tabs, area); +} + +fn draw_leaderboard(frame: &mut Frame, area: Rect, app: &App) { + let header = Row::new(vec![ + Cell::from("Rank"), + Cell::from("Miner"), + Cell::from("Score"), + Cell::from("Pass Rate"), + Cell::from("Submissions"), + Cell::from("Last Submission"), + ]) + .style( + Style::default() + .fg(Color::Yellow) + .add_modifier(Modifier::BOLD), + ); + + let visible_rows = visible_row_count(area); + let rows: Vec = app + .leaderboard + .iter() + .skip(app.scroll_offset) + .take(visible_rows) + .map(|entry| { + let hotkey_display = truncate_hotkey(&entry.miner_hotkey, 8); + Row::new(vec![ + Cell::from(entry.rank.to_string()), + Cell::from(hotkey_display), + Cell::from(format!("{:.4}", entry.score)), + Cell::from(format!("{:.1}%", entry.pass_rate * 100.0)), + Cell::from(entry.submissions.to_string()), + Cell::from(entry.last_submission.clone()), + ]) + }) + .collect(); + + let widths = [ + Constraint::Length(6), + Constraint::Length(14), + Constraint::Length(10), + Constraint::Length(12), + Constraint::Length(12), + Constraint::Min(20), + ]; + + let table = Table::new(rows, widths) + .header(header) + .block(Block::default().borders(Borders::ALL).title("Leaderboard")) + .row_highlight_style(Style::default().add_modifier(Modifier::BOLD)); + + frame.render_widget(table, area); + + if app.leaderboard.is_empty() { + draw_empty_message(frame, area, "No leaderboard data available"); + } +} + +fn draw_evaluation(frame: &mut Frame, area: Rect, app: &App) { + let inner_chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Length(3), Constraint::Min(0)]) + .split(area); + + let total = app.evaluation_progress.len(); + let completed = app + .evaluation_progress + .iter() + .filter(|t| t.status == "completed") + .count(); + let progress_text = if total > 0 { + format!( + "Progress: {completed}/{total} ({:.0}%)", + (completed as f64 / total as f64) * 100.0 + ) + } else { + "No evaluation tasks".to_string() + }; + let progress_bar = Paragraph::new(progress_text).block( + Block::default() + .borders(Borders::ALL) + .title("Overall Progress"), + ); + frame.render_widget(progress_bar, inner_chunks[0]); + + let header = Row::new(vec![ + Cell::from("Task ID"), + Cell::from("Status"), + Cell::from("Score"), + Cell::from("Duration (ms)"), + Cell::from("Error"), + ]) + .style( + Style::default() + .fg(Color::Yellow) + .add_modifier(Modifier::BOLD), + ); + + let visible_rows = visible_row_count(inner_chunks[1]); + let rows: Vec = app + .evaluation_progress + .iter() + .skip(app.scroll_offset) + .take(visible_rows) + .map(|task| { + let status_style = match task.status.as_str() { + "completed" => Style::default().fg(Color::Green), + "failed" => Style::default().fg(Color::Red), + "running" => Style::default().fg(Color::Cyan), + _ => Style::default().fg(Color::Gray), + }; + Row::new(vec![ + Cell::from(task.task_id.clone()), + Cell::from(Span::styled(task.status.clone(), status_style)), + Cell::from(format!("{:.4}", task.score)), + Cell::from(task.duration_ms.to_string()), + Cell::from(task.error.clone().unwrap_or_default()), + ]) + }) + .collect(); + + let widths = [ + Constraint::Length(20), + Constraint::Length(12), + Constraint::Length(10), + Constraint::Length(14), + Constraint::Min(20), + ]; + + let table = Table::new(rows, widths).header(header).block( + Block::default() + .borders(Borders::ALL) + .title("Evaluation Tasks"), + ); + + frame.render_widget(table, inner_chunks[1]); + + if app.evaluation_progress.is_empty() { + draw_empty_message(frame, inner_chunks[1], "No evaluation data available"); + } +} + +fn draw_submission(frame: &mut Frame, area: Rect, app: &App) { + let block = Block::default().borders(Borders::ALL).title("Submissions"); + + match &app.hotkey { + Some(hotkey) => { + let filtered: Vec<&crate::app::LeaderboardRow> = app + .leaderboard + .iter() + .filter(|r| r.miner_hotkey == *hotkey) + .collect(); + + if filtered.is_empty() { + let text = Paragraph::new(format!( + "No submissions found for hotkey: {}", + truncate_hotkey(hotkey, 16) + )) + .block(block); + frame.render_widget(text, area); + return; + } + + let mut lines = Vec::new(); + for entry in &filtered { + lines.push(Line::from(vec![ + Span::styled("Rank: ", Style::default().fg(Color::Yellow)), + Span::raw(entry.rank.to_string()), + ])); + lines.push(Line::from(vec![ + Span::styled("Hotkey: ", Style::default().fg(Color::Yellow)), + Span::raw(entry.miner_hotkey.clone()), + ])); + lines.push(Line::from(vec![ + Span::styled("Score: ", Style::default().fg(Color::Yellow)), + Span::raw(format!("{:.4}", entry.score)), + ])); + lines.push(Line::from(vec![ + Span::styled("Pass Rate: ", Style::default().fg(Color::Yellow)), + Span::raw(format!("{:.1}%", entry.pass_rate * 100.0)), + ])); + lines.push(Line::from(vec![ + Span::styled("Submissions: ", Style::default().fg(Color::Yellow)), + Span::raw(entry.submissions.to_string()), + ])); + lines.push(Line::from(vec![ + Span::styled("Last Submission: ", Style::default().fg(Color::Yellow)), + Span::raw(entry.last_submission.clone()), + ])); + lines.push(Line::from("")); + } + + let paragraph = Paragraph::new(lines).block(block); + frame.render_widget(paragraph, area); + } + None => { + let text = Paragraph::new("No hotkey specified. Use --hotkey to filter submissions.") + .block(block); + frame.render_widget(text, area); + } + } +} + +fn draw_network(frame: &mut Frame, area: Rect, app: &App) { + let ns = &app.network_status; + let connected_style = if ns.connected { + Style::default().fg(Color::Green) + } else { + Style::default().fg(Color::Red) + }; + let connected_text = if ns.connected { + "Connected" + } else { + "Disconnected" + }; + + let lines = vec![ + Line::from(vec![ + Span::styled("Status: ", Style::default().fg(Color::Yellow).bold()), + Span::styled(connected_text, connected_style), + ]), + Line::from(""), + Line::from(vec![ + Span::styled("Epoch: ", Style::default().fg(Color::Yellow).bold()), + Span::raw(ns.epoch.to_string()), + ]), + Line::from(vec![ + Span::styled("Phase: ", Style::default().fg(Color::Yellow).bold()), + Span::raw(ns.phase.clone()), + ]), + Line::from(vec![ + Span::styled("Block Height:", Style::default().fg(Color::Yellow).bold()), + Span::raw(format!(" {}", ns.block_height)), + ]), + Line::from(vec![ + Span::styled("Validators: ", Style::default().fg(Color::Yellow).bold()), + Span::raw(ns.validators.to_string()), + ]), + Line::from(""), + Line::from(vec![ + Span::styled("RPC URL: ", Style::default().fg(Color::Yellow).bold()), + Span::raw(app.rpc_url.clone()), + ]), + ]; + + if let Some(cid) = &app.challenge_id { + let mut all_lines = lines; + all_lines.push(Line::from(vec![ + Span::styled("Challenge: ", Style::default().fg(Color::Yellow).bold()), + Span::raw(cid.clone()), + ])); + let paragraph = Paragraph::new(all_lines).block( + Block::default() + .borders(Borders::ALL) + .title("Network Status"), + ); + frame.render_widget(paragraph, area); + } else { + let paragraph = Paragraph::new(lines).block( + Block::default() + .borders(Borders::ALL) + .title("Network Status"), + ); + frame.render_widget(paragraph, area); + } +} + +fn draw_status_bar(frame: &mut Frame, area: Rect, app: &App) { + let ns = &app.network_status; + let refresh_str = app + .last_refresh + .map(|t| t.format("%H:%M:%S UTC").to_string()) + .unwrap_or_else(|| "never".to_string()); + + let mut spans = vec![ + Span::styled(" Epoch: ", Style::default().fg(Color::Yellow)), + Span::raw(ns.epoch.to_string()), + Span::raw(" | "), + Span::styled("Phase: ", Style::default().fg(Color::Yellow)), + Span::raw(ns.phase.clone()), + Span::raw(" | "), + Span::styled("Block: ", Style::default().fg(Color::Yellow)), + Span::raw(ns.block_height.to_string()), + Span::raw(" | "), + Span::styled("Validators: ", Style::default().fg(Color::Yellow)), + Span::raw(ns.validators.to_string()), + Span::raw(" | "), + Span::styled("Refresh: ", Style::default().fg(Color::Yellow)), + Span::raw(refresh_str), + ]; + + if let Some(err) = &app.error_message { + spans.push(Span::raw(" | ")); + spans.push(Span::styled( + format!("Error: {err}"), + Style::default().fg(Color::Red), + )); + } + + let status = Paragraph::new(Line::from(spans)) + .block(Block::default().borders(Borders::ALL).title("Status")); + frame.render_widget(status, area); +} + +fn truncate_hotkey(hotkey: &str, max_len: usize) -> String { + if hotkey.len() > max_len { + format!("{}...", &hotkey[..max_len]) + } else { + hotkey.to_string() + } +} + +fn visible_row_count(area: Rect) -> usize { + area.height.saturating_sub(4) as usize +} + +fn draw_empty_message(frame: &mut Frame, area: Rect, message: &str) { + let inner = centered_rect(60, 20, area); + let text = Paragraph::new(message).style(Style::default().fg(Color::DarkGray)); + frame.render_widget(text, inner); +} + +fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { + let popup_layout = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Percentage((100 - percent_y) / 2), + Constraint::Percentage(percent_y), + Constraint::Percentage((100 - percent_y) / 2), + ]) + .split(r); + + Layout::default() + .direction(Direction::Horizontal) + .constraints([ + Constraint::Percentage((100 - percent_x) / 2), + Constraint::Percentage(percent_x), + Constraint::Percentage((100 - percent_x) / 2), + ]) + .split(popup_layout[1])[1] +} diff --git a/wasm/src/agent_storage.rs b/wasm/src/agent_storage.rs new file mode 100644 index 00000000..59fd4e1a --- /dev/null +++ b/wasm/src/agent_storage.rs @@ -0,0 +1,74 @@ +use alloc::string::String; +use alloc::vec::Vec; +use platform_challenge_sdk_wasm::host_functions::{host_storage_get, host_storage_set}; + +use crate::types::AgentLogs; + +pub const MAX_AGENT_PACKAGE_SIZE: usize = 1_048_576; +const MAX_LOG_SIZE: usize = 262_144; +pub const MAX_TASK_OUTPUT_PREVIEW: usize = 4_096; + +fn make_key(prefix: &[u8], miner_hotkey: &str, epoch: u64) -> Vec { + let mut key = Vec::from(prefix); + key.extend_from_slice(miner_hotkey.as_bytes()); + key.push(b':'); + key.extend_from_slice(&epoch.to_le_bytes()); + key +} + +pub fn store_agent_code(miner_hotkey: &str, epoch: u64, package_zip: &[u8]) -> bool { + if package_zip.len() > MAX_AGENT_PACKAGE_SIZE { + return false; + } + let key = make_key(b"agent_code:", miner_hotkey, epoch); + host_storage_set(&key, package_zip).is_ok() +} + +pub fn store_agent_hash(miner_hotkey: &str, epoch: u64, agent_hash: &str) -> bool { + let key = make_key(b"agent_hash:", miner_hotkey, epoch); + host_storage_set(&key, agent_hash.as_bytes()).is_ok() +} + +pub fn store_agent_logs(miner_hotkey: &str, epoch: u64, logs: &AgentLogs) -> bool { + let data = match bincode::serialize(logs) { + Ok(d) => d, + Err(_) => return false, + }; + if data.len() > MAX_LOG_SIZE { + return false; + } + let key = make_key(b"agent_logs:", miner_hotkey, epoch); + host_storage_set(&key, &data).is_ok() +} + +/// Retrieve stored agent code for a miner/epoch. +/// Called by platform-v2's challenge route handler for `/agent/:hotkey/code` requests. +#[allow(dead_code)] +pub fn get_agent_code(miner_hotkey: &str, epoch: u64) -> Option> { + let key = make_key(b"agent_code:", miner_hotkey, epoch); + let data = host_storage_get(&key).ok()?; + if data.is_empty() { + return None; + } + Some(data) +} + +/// Retrieve stored agent logs for a miner/epoch. +/// Called by platform-v2's challenge route handler for `/agent/:hotkey/logs` requests. +#[allow(dead_code)] +pub fn get_agent_logs(miner_hotkey: &str, epoch: u64) -> Option { + let key = make_key(b"agent_logs:", miner_hotkey, epoch); + let data = host_storage_get(&key).ok()?; + if data.is_empty() { + return None; + } + bincode::deserialize(&data).ok() +} + +pub fn truncate_output(output: &str, max_len: usize) -> String { + if output.len() <= max_len { + return String::from(output); + } + let truncated = &output[..max_len]; + String::from(truncated) +} diff --git a/wasm/src/dataset.rs b/wasm/src/dataset.rs index df4fb5ea..0f741485 100644 --- a/wasm/src/dataset.rs +++ b/wasm/src/dataset.rs @@ -1,101 +1,10 @@ -use alloc::string::String; -use alloc::vec::Vec; -use core::fmt::Write as _; -use platform_challenge_sdk_wasm::host_functions::{ - host_consensus_get_epoch, host_random_seed, host_storage_set, -}; +//! Dataset selection and consensus logic (reserved for future P2P consensus implementation) -use crate::types::{DatasetSelection, TaskDefinition}; +// Note: Dataset consensus functions removed as the feature is not yet integrated. +// The active dataset is currently managed via the ChallengeParams passed to evaluate(). -const DATASET_SELECTION_PREFIX: &[u8] = b"dataset_selection:"; -const TOTAL_SWE_BENCH_TASKS: usize = 2294; -const TASKS_TO_SELECT: usize = 100; -const CONSENSUS_DATASET_SIZE: usize = 50; - -pub fn select_random_task_indices() -> Vec { - let mut seed = [0u8; 32]; - if host_random_seed(&mut seed).is_err() { - return Vec::new(); - } - - let mut indices = Vec::with_capacity(TASKS_TO_SELECT); - let mut state: u64 = u64::from_le_bytes([ - seed[0], seed[1], seed[2], seed[3], seed[4], seed[5], seed[6], seed[7], - ]); - - while indices.len() < TASKS_TO_SELECT { - state = state - .wrapping_mul(6364136223846793005) - .wrapping_add(1442695040888963407); - let idx = (state >> 33) as usize % TOTAL_SWE_BENCH_TASKS; - if !indices.contains(&idx) { - indices.push(idx); - } - } - - indices -} - -pub fn store_my_selection(indices: &[usize]) -> bool { - let epoch = host_consensus_get_epoch(); - if epoch < 0 { - return false; - } - - let mut key = Vec::with_capacity(DATASET_SELECTION_PREFIX.len() + 8); - key.extend_from_slice(DATASET_SELECTION_PREFIX); - key.extend_from_slice(&(epoch as u64).to_le_bytes()); - - let data = match bincode::serialize(indices) { - Ok(d) => d, - Err(_) => return false, - }; - host_storage_set(&key, &data).is_ok() -} - -pub fn build_consensus_dataset( - all_tasks: &[TaskDefinition], - validator_selections: &[Vec], -) -> Vec { - if validator_selections.is_empty() || all_tasks.is_empty() { - return Vec::new(); - } - - let threshold = validator_selections.len().div_ceil(2); - let mut counts = alloc::collections::BTreeMap::new(); - - for selection in validator_selections { - for &idx in selection { - *counts.entry(idx).or_insert(0usize) += 1; - } - } - - let mut consensus_indices: Vec = counts - .into_iter() - .filter(|(_, count)| *count >= threshold) - .map(|(idx, _)| idx) - .collect(); - - consensus_indices.sort_unstable(); - consensus_indices.truncate(CONSENSUS_DATASET_SIZE); - - consensus_indices - .iter() - .filter_map(|&idx| all_tasks.get(idx).cloned()) - .collect() -} - -pub fn create_dataset_selection(tasks: Vec) -> DatasetSelection { - let epoch = host_consensus_get_epoch(); - - let mut hash_input = String::new(); - for task in &tasks { - let _ = write!(hash_input, "{}:{};", task.id, task.name); - } - - DatasetSelection { - tasks, - selected_at_epoch: if epoch >= 0 { epoch as u64 } else { 0 }, - dataset_hash: hash_input, - } -} +// When P2P dataset consensus is implemented, this module will contain: +// - Random task index selection using host_random_seed +// - Validator selection storage via host_storage_set +// - Consensus building (>50% agreement) on task indices +// - Dataset selection serialization with hash verification diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 12c4309f..0f4fb615 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -2,6 +2,7 @@ extern crate alloc; +mod agent_storage; mod dataset; mod routes; mod scoring; @@ -18,7 +19,8 @@ use platform_challenge_sdk_wasm::{Challenge, EvaluationInput, EvaluationOutput}; use crate::scoring::{calculate_aggregate, format_summary, to_weight}; use crate::types::{ - ChallengeParams, DatasetSelection, LlmJudgeRequest, LlmJudgeResponse, Submission, TaskResult, + AgentLogEntry, AgentLogs, ChallengeParams, DatasetSelection, LlmJudgeRequest, LlmJudgeResponse, + Submission, TaskResult, }; const MAX_SUBMISSION_SIZE: u64 = 64 * 1024 * 1024; @@ -112,11 +114,11 @@ impl TermChallengeWasm { Err(_) => return None, }; - let judge_resp: LlmJudgeResponse = - match bincode_options_llm().deserialize(&response_bytes) { - Ok(r) => r, - Err(_) => return None, - }; + let judge_resp: LlmJudgeResponse = match bincode_options_llm().deserialize(&response_bytes) + { + Ok(r) => r, + Err(_) => return None, + }; if !judge_resp.score.is_finite() { return None; @@ -167,6 +169,10 @@ impl Challenge for TermChallengeWasm { } } + let miner_hotkey = submission.miner_hotkey; + let epoch = submission.epoch; + let agent_hash = submission.agent_hash; + let package_zip = submission.package_zip; let mut results: Vec = submission.task_results; if let Some(ref url) = params.llm_judge_url { @@ -188,7 +194,37 @@ impl Challenge for TermChallengeWasm { let score = (weight * 10_000.0) as i64; let message = format_summary(&aggregate); - set_last_submission_epoch(&submission.miner_hotkey, submission.epoch); + let _ = agent_storage::store_agent_code(&miner_hotkey, epoch, &package_zip); + let _ = agent_storage::store_agent_hash(&miner_hotkey, epoch, &agent_hash); + + let mut entries = Vec::with_capacity(results.len()); + let mut total_size_bytes: u64 = 0; + for r in &results { + let output_preview = agent_storage::truncate_output( + &r.agent_output, + agent_storage::MAX_TASK_OUTPUT_PREVIEW, + ); + total_size_bytes = total_size_bytes.saturating_add(output_preview.len() as u64); + entries.push(AgentLogEntry { + task_id: r.task_id.clone(), + passed: r.passed, + score: r.score, + execution_time_ms: r.execution_time_ms, + output_preview, + error: r.error.clone(), + }); + } + + let logs = AgentLogs { + miner_hotkey: miner_hotkey.clone(), + epoch, + agent_hash: agent_hash.clone(), + entries, + total_size_bytes, + }; + let _ = agent_storage::store_agent_logs(&miner_hotkey, epoch, &logs); + + set_last_submission_epoch(&miner_hotkey, epoch); EvaluationOutput::success(score, &message) } @@ -217,6 +253,10 @@ impl Challenge for TermChallengeWasm { return false; } + if submission.package_zip.len() > 1_048_576 { + return false; + } + if submission.basilica_instance.is_empty() || submission.executor_url.is_empty() || submission.executor_token.is_empty() diff --git a/wasm/src/routes.rs b/wasm/src/routes.rs index 33b2fa24..ab548ed8 100644 --- a/wasm/src/routes.rs +++ b/wasm/src/routes.rs @@ -1,9 +1,21 @@ +//! Route definitions for the term-challenge module +//! +//! These routes are designed to be queried via platform-v2's `challenge_call` RPC method. +//! When the challenge SDK's route integration is complete, these routes will be registered +//! automatically. Until then, validators can access this data via direct storage queries. + use alloc::string::String; use alloc::vec; use alloc::vec::Vec; use crate::types::RouteDefinition; +/// Returns route definitions for the term-challenge module. +/// +/// Note: This function is currently unused pending integration with platform-v2's +/// challenge route registration system. The routes are defined here for documentation +/// and future automatic registration. +#[allow(dead_code)] pub fn get_route_definitions() -> Vec { vec![ RouteDefinition { @@ -48,5 +60,15 @@ pub fn get_route_definitions() -> Vec { path: String::from("/stats"), description: String::from("Challenge statistics: total submissions, active miners"), }, + RouteDefinition { + method: String::from("GET"), + path: String::from("/agent/:hotkey/code"), + description: String::from("Returns stored agent code package for a miner"), + }, + RouteDefinition { + method: String::from("GET"), + path: String::from("/agent/:hotkey/logs"), + description: String::from("Returns evaluation logs for a miner"), + }, ] } diff --git a/wasm/src/scoring.rs b/wasm/src/scoring.rs index 989fb1df..515efad0 100644 --- a/wasm/src/scoring.rs +++ b/wasm/src/scoring.rs @@ -74,10 +74,16 @@ pub fn calculate_aggregate(tasks: &[TaskDefinition], results: &[TaskResult]) -> } } +/// Convert aggregate score to weight (normalized 0.0-1.0). pub fn to_weight(score: &AggregateScore) -> f64 { score.pass_rate.clamp(0.0, 1.0) } +/// Apply decay to weight based on hours since top score. +/// +/// Note: This function is reserved for future use when decay mechanics are +/// integrated with the scoring system via challenge configuration. +#[allow(dead_code)] pub fn apply_decay(weight: f64, hours_since_top: f64, params: &DecayParams) -> f64 { let grace = params.grace_period_hours as f64; if hours_since_top <= grace { diff --git a/wasm/src/tasks.rs b/wasm/src/tasks.rs index f03e5e99..5552268b 100644 --- a/wasm/src/tasks.rs +++ b/wasm/src/tasks.rs @@ -50,16 +50,3 @@ fn append_dataset_history(selection: &DatasetSelection) -> bool { }; host_storage_set(DATASET_HISTORY_KEY, &data).is_ok() } - -pub fn get_dataset_history() -> Vec { - host_storage_get(DATASET_HISTORY_KEY) - .ok() - .and_then(|d| { - if d.is_empty() { - None - } else { - bincode::deserialize(&d).ok() - } - }) - .unwrap_or_default() -} diff --git a/wasm/src/types.rs b/wasm/src/types.rs index e1547326..3c4e0520 100644 --- a/wasm/src/types.rs +++ b/wasm/src/types.rs @@ -95,6 +95,25 @@ pub struct DatasetSelection { pub dataset_hash: String, } +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct AgentLogEntry { + pub task_id: String, + pub passed: bool, + pub score: f64, + pub execution_time_ms: u64, + pub output_preview: String, + pub error: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct AgentLogs { + pub miner_hotkey: String, + pub epoch: u64, + pub agent_hash: String, + pub entries: Vec, + pub total_size_bytes: u64, +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct RouteDefinition { pub method: String,