├── .gitignore ├── Cargo.toml ├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── src ├── terminal │ ├── types.rs │ ├── buffer.rs │ ├── mod.rs │ └── handlers.rs ├── utils │ └── mod.rs ├── updates.rs ├── main.rs └── lsp.rs ├── install.sh ├── readme.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "axs" 3 | version = "0.2.10" 4 | edition = "2021" 5 | 6 | 7 | [dependencies] 8 | axum = { version = "0.8.1", features = ["ws"] } 9 | clap = { version = "4.4.18", features = ["derive"] } 10 | colored = "3.0.0" 11 | futures = "0.3.31" 12 | pnet = "0.35.0" 13 | portable-pty = { git = "https://github.com/wez/wezterm", package = "portable-pty", rev = "1e941fca5c17155b5f64a74221a396ef12ef71d0" } 14 | regex = "1.11.1" 15 | reqwest = { version = "0.12.12", default-features = false, features = [ 16 | "json", 17 | "rustls-tls", 18 | ] } 19 | semver = "1.0.25" 20 | serde = { version = "1.0.215", features = ["derive"] } 21 | serde_json = "1.0.133" 22 | tokio = { version = "1.42.0", features = ["full"] } 23 | tokio-util = { version = "0.7", features = ["codec"] } 24 | bytes = "1.5" 25 | nom = "7.1" 26 | tower-http = { version = "0.6.2", features = ["cors", "trace"] } 27 | tracing = "0.1.41" 28 | tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } 29 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: ["*"] 6 | pull_request: 7 | branches: ["*"] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | lint: 12 | name: Lint & Format 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v3 17 | 18 | - name: Install Rust toolchain 19 | uses: dtolnay/rust-toolchain@stable 20 | with: 21 | components: clippy, rustfmt 22 | 23 | - name: Run Clippy 24 | run: cargo clippy --all-targets --all-features -- -D warnings 25 | 26 | - name: Check formatting 27 | run: cargo fmt --all -- --check 28 | 29 | test: 30 | name: Test 31 | runs-on: ubuntu-latest 32 | steps: 33 | - name: Checkout repository 34 | uses: actions/checkout@v3 35 | 36 | - name: Install Rust toolchain 37 | uses: dtolnay/rust-toolchain@stable 38 | 39 | - name: Run tests 40 | run: cargo test 41 | -------------------------------------------------------------------------------- /src/terminal/types.rs: -------------------------------------------------------------------------------- 1 | use super::handlers::TerminalSession; 2 | use serde::{Deserialize, Serialize}; 3 | use std::{collections::HashMap, sync::Arc}; 4 | use tokio::sync::Mutex; 5 | 6 | pub const MAX_BUFFER_SIZE: usize = 1_000_000; 7 | 8 | #[derive(Deserialize)] 9 | pub struct TerminalOptions { 10 | pub cols: serde_json::Value, 11 | pub rows: serde_json::Value, 12 | } 13 | 14 | #[derive(Deserialize)] 15 | pub struct ExecuteCommandOption { 16 | pub command: String, 17 | pub cwd: Option, 18 | pub u_cwd: Option, 19 | } 20 | 21 | #[derive(Serialize)] 22 | pub struct CommandResponse { 23 | pub output: String, 24 | pub error: Option, 25 | } 26 | 27 | #[derive(Debug, Serialize)] 28 | pub struct ErrorResponse { 29 | pub error: String, 30 | } 31 | 32 | #[derive(Debug, Serialize)] 33 | pub struct ProcessExitMessage { 34 | pub exit_code: Option, 35 | pub signal: Option, 36 | pub message: String, 37 | } 38 | 39 | pub type Sessions = Arc>>; 40 | -------------------------------------------------------------------------------- /src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | use pnet::datalink; 2 | use std::net::Ipv4Addr; 3 | 4 | pub fn get_ip_address() -> Option { 5 | for iface in datalink::interfaces() { 6 | for ip in iface.ips { 7 | if let pnet::ipnetwork::IpNetwork::V4(network) = ip { 8 | if !network.ip().is_loopback() { 9 | return Some(network.ip()); 10 | } 11 | } 12 | } 13 | } 14 | None 15 | } 16 | 17 | pub fn parse_u16(value: &serde_json::Value, field_name: &str) -> Result { 18 | match value { 19 | serde_json::Value::Number(n) if n.is_u64() => n 20 | .as_u64() 21 | .and_then(|n| u16::try_from(n).ok()) 22 | .ok_or_else(|| format!("{field_name} must be a valid u16.")), 23 | serde_json::Value::String(s) => s 24 | .parse::() 25 | .map_err(|_| format!("{field_name} must be a valid u16 string.")), 26 | _ => Err(format!("{field_name} must be a number or a valid string.",)), 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/terminal/buffer.rs: -------------------------------------------------------------------------------- 1 | use axum::body::Bytes; 2 | 3 | pub struct CircularBuffer { 4 | pub data: Vec, 5 | position: usize, 6 | max_size: usize, 7 | } 8 | 9 | impl CircularBuffer { 10 | pub fn new(max_size: usize) -> Self { 11 | Self { 12 | data: Vec::with_capacity(max_size), 13 | position: 0, 14 | max_size, 15 | } 16 | } 17 | 18 | pub fn write(&mut self, new_data: &[u8]) { 19 | for &byte in new_data { 20 | if self.data.len() < self.max_size { 21 | self.data.push(byte); 22 | } else { 23 | self.data[self.position] = byte; 24 | self.position = (self.position + 1) % self.max_size; 25 | } 26 | } 27 | } 28 | 29 | pub fn get_contents(&self) -> Bytes { 30 | if self.data.len() < self.max_size { 31 | Bytes::from(self.data.clone()) 32 | } else { 33 | let mut result = Vec::with_capacity(self.max_size); 34 | result.extend_from_slice(&self.data[self.position..]); 35 | result.extend_from_slice(&self.data[..self.position]); 36 | Bytes::from(result) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # detect architecture 4 | detect_arch() { 5 | case $(uname -m) in 6 | armv7l | armv8l) 7 | echo "android-armv7" 8 | ;; 9 | aarch64) 10 | echo "android-arm64" 11 | ;; 12 | x86_64) 13 | echo "android-x86_64" 14 | ;; 15 | *) 16 | echo "Unsupported architecture. Please create an issue on GitHub, and we will consider providing a binary for your architecture." 17 | exit 1 18 | ;; 19 | esac 20 | } 21 | 22 | # download the appropriate binary 23 | download_binary() { 24 | ARCH=$(detect_arch) 25 | BASE_URL="https://github.com/bajrangCoder/acodex_server/releases/latest/download" 26 | 27 | FILE_NAME="axs-$ARCH" 28 | DOWNLOAD_URL="$BASE_URL/$FILE_NAME" 29 | 30 | # Download the binary 31 | echo "Downloading $FILE_NAME for $ARCH architecture..." 32 | if ! curl --progress-bar --fail -L "$DOWNLOAD_URL" -o "$FILE_NAME"; then 33 | echo "Failed to download the binary! Please check the URL and your connection: $DOWNLOAD_URL" 34 | exit 1 35 | fi 36 | 37 | # Move the binary to the PREFIX directory and rename it to 'axs' 38 | echo "Installing axs binary to $PREFIX..." 39 | mv "$FILE_NAME" "$PREFIX/bin/axs" 40 | chmod +x "$PREFIX/bin/axs" 41 | 42 | # Create a symlink acodeX-server pointing to axs 43 | ln -sf "$PREFIX/bin/axs" "$PREFIX/bin/acodeX-server" 44 | 45 | echo "Binary downloaded and installed as 'axs'. You can now use the 'axs' command!" 46 | echo "Make sure '$PREFIX/bin' is in your PATH." 47 | } 48 | 49 | download_binary 50 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # acodex_server 2 | 3 | `acodex_server` is a Rust-based backend/server for serving shell over socket. It provides a **lightweight**, **independent**, **secure**, and **fast** solution. 4 | 5 | ## Features 6 | 7 | - Lightweight 8 | - Secure 9 | - fast 10 | - uses system pty 11 | - automatic update checking 12 | 13 | ## Installation 14 | 15 | To install `axs` on your system, simply use the following command: 16 | 17 | ```bash 18 | curl -L https://raw.githubusercontent.com/bajrangCoder/acodex_server/main/install.sh | bash 19 | ``` 20 | 21 | ## Update 22 | 23 | `axs` will automatically notify you whenever a new update is available. With a simple command: 24 | 25 | ```sh 26 | axs update 27 | ``` 28 | 29 | you can easily update it without any hassle. 30 | 31 | > [!NOTE] 32 | > This feature is available from `v0.2.0` onwards. For older versions, please use the installation script to update. 33 | 34 | ### Example Usage 35 | 36 | ```bash 37 | $ axs --help 38 | CLI/Server backend to serve pty over socket 39 | 40 | Usage: axs [OPTIONS] [COMMAND] 41 | 42 | Commands: 43 | update Update axs server 44 | help Print this message or the help of the given subcommand(s) 45 | 46 | Options: 47 | -p, --port Port to start the server [default: 8767] 48 | -i, --ip Start the server on local network (ip) 49 | -c, --command Custom command or shell for interactive PTY (e.g. "/usr/bin/bash") 50 | --allow-any-origin Allow all origins for CORS (dangerous). By default only https://localhost is allowed 51 | -h, --help Print help 52 | -V, --version Print version 53 | ``` 54 | 55 | > [!NOTE] 56 | > If you encounter any issues, please [create an issue on GitHub](https://github.com/bajrangCoder/acodex_server/issues). 57 | 58 | ## Building from Source 59 | 60 | To build acodex_server from source, follow these steps: 61 | 62 | 1. Clone the repository: 63 | ```bash 64 | git clone https://github.com/your-username/acodex_server.git 65 | ``` 66 | 67 | 2. Ensure that Rust is installed on your system. 68 | 69 | 3. Navigate to the project directory: 70 | ```bash 71 | cd acodex_server 72 | ``` 73 | 74 | 4. Build the project: 75 | ```bash 76 | cargo build --release 77 | ``` 78 | 79 | 5. Use the generated binary located at `/target/release/axs`. 80 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | tags: 7 | - "v*" 8 | 9 | permissions: 10 | contents: write 11 | 12 | jobs: 13 | build: 14 | name: Build ${{ matrix.platform.os_name }} 15 | runs-on: ubuntu-latest 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | platform: 20 | - os_name: Android-aarch64 21 | target: aarch64-linux-android 22 | binary_name: axs-android-arm64 23 | - os_name: Android-armv7 24 | target: armv7-linux-androideabi 25 | binary_name: axs-android-armv7 26 | - os_name: Android-x86_64 27 | target: x86_64-linux-android 28 | binary_name: axs-android-x86_64 29 | - os_name: MuslAndroid-aarch64 30 | target: aarch64-unknown-linux-musl 31 | binary_name: axs-musl-android-arm64 32 | - os_name: MuslAndroid-armv7 33 | target: armv7-unknown-linux-musleabihf 34 | binary_name: axs-musl-android-armv7 35 | - os_name: MuslAndroid-x86_64 36 | target: x86_64-unknown-linux-musl 37 | binary_name: axs-musl-android-x86_64 38 | steps: 39 | - name: Checkout repository 40 | uses: actions/checkout@v3 41 | 42 | - name: Install cross 43 | run: cargo install cross --git https://github.com/cross-rs/cross.git --branch main 44 | 45 | - name: Add Rust target 46 | run: rustup target add ${{ matrix.platform.target }} 47 | 48 | - name: Build binary with cross 49 | run: cross build --release --target ${{ matrix.platform.target }} 50 | 51 | - name: Rename binary 52 | run: mv target/${{ matrix.platform.target }}/release/axs target/${{ matrix.platform.target }}/release/${{ matrix.platform.binary_name }} 53 | 54 | - name: Generate SHA-256 55 | run: | 56 | cd target/${{ matrix.platform.target }}/release 57 | sha256sum ${{ matrix.platform.binary_name }} > ${{ matrix.platform.binary_name }}.sha256 58 | 59 | - name: Upload artifacts 60 | uses: softprops/action-gh-release@v1 61 | with: 62 | files: | 63 | target/${{ matrix.platform.target }}/release/${{ matrix.platform.binary_name }} 64 | target/${{ matrix.platform.target }}/release/${{ matrix.platform.binary_name }}.sha256 65 | -------------------------------------------------------------------------------- /src/terminal/mod.rs: -------------------------------------------------------------------------------- 1 | mod buffer; 2 | mod handlers; 3 | mod types; 4 | 5 | use axum::{ 6 | routing::{get, post}, 7 | Router, 8 | }; 9 | 10 | use axum::http::HeaderValue; 11 | use std::sync::OnceLock; 12 | use std::{collections::HashMap, sync::Arc}; 13 | use std::{io::ErrorKind, net::Ipv4Addr}; 14 | use tokio::sync::Mutex; 15 | use tower_http::cors::{Any, CorsLayer}; 16 | use tower_http::trace::{DefaultMakeSpan, TraceLayer}; 17 | use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; 18 | 19 | use handlers::*; 20 | 21 | pub type Sessions = Arc>>; 22 | 23 | static DEFAULT_COMMAND: OnceLock = OnceLock::new(); 24 | 25 | pub fn set_default_command(cmd: String) { 26 | let _ = DEFAULT_COMMAND.set(cmd); 27 | } 28 | 29 | pub fn get_default_command() -> Option<&'static str> { 30 | DEFAULT_COMMAND.get().map(|s| s.as_str()) 31 | } 32 | 33 | pub async fn start_server(host: Ipv4Addr, port: u16, allow_any_origin: bool) { 34 | tracing_subscriber::registry() 35 | .with( 36 | tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { 37 | format!("{}=debug,tower_http=info", env!("CARGO_CRATE_NAME")).into() 38 | }), 39 | ) 40 | .with(tracing_subscriber::fmt::layer()) 41 | .init(); 42 | let sessions: Sessions = Arc::new(Mutex::new(HashMap::new())); 43 | 44 | let cors = if allow_any_origin { 45 | CorsLayer::new() 46 | .allow_origin(Any) 47 | .allow_methods(Any) 48 | .allow_headers(Any) 49 | } else { 50 | // Only allow your Cordova app origin by default 51 | let localhost = "https://localhost" 52 | .parse::() 53 | .expect("valid origin"); 54 | CorsLayer::new() 55 | .allow_origin(localhost) 56 | .allow_methods(Any) 57 | .allow_headers(Any) 58 | }; 59 | 60 | let app = Router::new() 61 | .route("/", get(|| async { "Rust based AcodeX server" })) 62 | .route("/terminals", post(create_terminal)) 63 | .route("/terminals/{pid}/resize", post(resize_terminal)) 64 | .route("/terminals/{pid}", get(terminal_websocket)) 65 | .route("/terminals/{pid}/terminate", post(terminate_terminal)) 66 | .route("/execute-command", post(execute_command)) 67 | .route("/status", get(|| async { "OK" })) 68 | .with_state(sessions) 69 | .layer(cors) 70 | .layer( 71 | TraceLayer::new_for_http() 72 | .make_span_with(DefaultMakeSpan::default().include_headers(true)), 73 | ); 74 | 75 | let addr: std::net::SocketAddr = (host, port).into(); 76 | 77 | match tokio::net::TcpListener::bind(addr).await { 78 | Ok(listener) => { 79 | tracing::info!("listening on {}", listener.local_addr().unwrap()); 80 | 81 | if let Err(e) = axum::serve(listener, app).await { 82 | tracing::error!("Server error: {}", e); 83 | } 84 | } 85 | Err(e) => { 86 | if e.kind() == ErrorKind::AddrInUse { 87 | tracing::error!("Port is already in use please kill all other instances of axs server or stop any other process or app that maybe be using port {}", port); 88 | } else { 89 | tracing::error!("Failed to bind: {}", e); 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/updates.rs: -------------------------------------------------------------------------------- 1 | use reqwest::Client; 2 | use semver::Version; 3 | use serde::Deserialize; 4 | use std::path::PathBuf; 5 | use std::time::{Duration, SystemTime}; 6 | use tokio::fs::{self, File}; 7 | use tokio::io::AsyncWriteExt; 8 | 9 | const GITHUB_API_URL: &str = 10 | "https://api.github.com/repos/bajrangCoder/acodex_server/releases/latest"; 11 | const UPDATE_CHECK_INTERVAL: Duration = Duration::from_secs(24 * 60 * 60); 12 | const CACHE_FILE: &str = ".axs_update_cache"; 13 | 14 | #[derive(Deserialize)] 15 | struct GithubRelease { 16 | tag_name: String, 17 | assets: Vec, 18 | } 19 | 20 | #[derive(Deserialize)] 21 | struct GithubAsset { 22 | name: String, 23 | browser_download_url: String, 24 | } 25 | 26 | struct UpdateCache { 27 | last_check: SystemTime, 28 | latest_version: String, 29 | } 30 | 31 | pub struct UpdateChecker { 32 | client: Client, 33 | current_version: Version, 34 | } 35 | 36 | impl UpdateChecker { 37 | pub fn new(current_version: &str) -> Self { 38 | Self { 39 | client: Client::new(), 40 | current_version: Version::parse(current_version).unwrap(), 41 | } 42 | } 43 | 44 | async fn get_cache() -> Option { 45 | let cache_path = Self::get_cache_path()?; 46 | let content = fs::read_to_string(cache_path).await.ok()?; 47 | let parts: Vec<&str> = content.split(',').collect(); 48 | if parts.len() != 2 { 49 | return None; 50 | } 51 | 52 | let timestamp = parts[0].parse::().ok()?; 53 | Some(UpdateCache { 54 | last_check: SystemTime::UNIX_EPOCH + Duration::from_secs(timestamp), 55 | latest_version: parts[1].to_string(), 56 | }) 57 | } 58 | 59 | async fn save_cache(version: &str) -> tokio::io::Result<()> { 60 | if let Some(cache_path) = Self::get_cache_path() { 61 | if let Some(parent) = cache_path.parent() { 62 | fs::create_dir_all(parent).await?; 63 | } 64 | 65 | let now = SystemTime::now() 66 | .duration_since(SystemTime::UNIX_EPOCH) 67 | .unwrap() 68 | .as_secs(); 69 | let content = format!("{now},{version}"); 70 | fs::write(cache_path, content).await?; 71 | } 72 | Ok(()) 73 | } 74 | 75 | fn get_cache_path() -> Option { 76 | match std::env::var_os("HOME") { 77 | Some(home) => { 78 | let mut path = PathBuf::from(home); 79 | path.push(".cache"); 80 | path.push("axs"); 81 | path.push(CACHE_FILE); 82 | Some(path) 83 | } 84 | None => match std::env::var_os("TMPDIR").or_else(|| std::env::var_os("TMP")) { 85 | Some(tmp) => { 86 | let mut path = PathBuf::from(tmp); 87 | path.push("axs"); 88 | path.push(CACHE_FILE); 89 | Some(path) 90 | } 91 | None => None, 92 | }, 93 | } 94 | } 95 | 96 | pub async fn check_update(&self) -> Result, Box> { 97 | // Check cache first 98 | if let Some(cache) = Self::get_cache().await { 99 | let elapsed = SystemTime::now() 100 | .duration_since(cache.last_check) 101 | .unwrap_or(UPDATE_CHECK_INTERVAL); 102 | if elapsed < UPDATE_CHECK_INTERVAL { 103 | let cached_version = Version::parse(&cache.latest_version)?; 104 | if cached_version > self.current_version { 105 | return Ok(Some(cache.latest_version)); 106 | } 107 | return Ok(None); 108 | } 109 | } 110 | 111 | // Fetch latest release from GitHub 112 | let release: GithubRelease = self 113 | .client 114 | .get(GITHUB_API_URL) 115 | .header("User-Agent", "axs-update-checker") 116 | .send() 117 | .await? 118 | .json() 119 | .await?; 120 | 121 | let latest_version = Version::parse(release.tag_name.trim_start_matches('v'))?; 122 | Self::save_cache(&latest_version.to_string()).await?; 123 | 124 | if latest_version > self.current_version { 125 | Ok(Some(release.tag_name)) 126 | } else { 127 | Ok(None) 128 | } 129 | } 130 | 131 | pub async fn update(&self) -> Result<(), Box> { 132 | let release: GithubRelease = self 133 | .client 134 | .get(GITHUB_API_URL) 135 | .header("User-Agent", "axs-update-checker") 136 | .send() 137 | .await? 138 | .json() 139 | .await?; 140 | 141 | // Detect current architecture 142 | let arch = match std::env::consts::ARCH { 143 | "arm" => "android-armv7", 144 | "aarch64" => "android-arm64", 145 | _ => return Err("Unsupported architecture".into()), 146 | }; 147 | 148 | let asset = release 149 | .assets 150 | .iter() 151 | .find(|a| a.name == format!("axs-{arch}")) 152 | .ok_or("No matching binary found")?; 153 | 154 | // Download binary 155 | let response = self 156 | .client 157 | .get(&asset.browser_download_url) 158 | .send() 159 | .await? 160 | .bytes() 161 | .await?; 162 | 163 | let current_exe = std::env::current_exe()?; 164 | let temp_path = current_exe.with_extension("new"); 165 | 166 | let mut file = File::create(&temp_path).await?; 167 | file.write_all(&response).await?; 168 | file.sync_all().await?; 169 | 170 | #[cfg(unix)] 171 | { 172 | use std::os::unix::fs::PermissionsExt; 173 | let metadata = fs::metadata(&temp_path).await?; 174 | let mut perms = metadata.permissions(); 175 | perms.set_mode(0o755); 176 | fs::set_permissions(&temp_path, perms).await?; 177 | } 178 | 179 | // Replace old binary with new one 180 | fs::rename(temp_path, current_exe).await?; 181 | 182 | Ok(()) 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod lsp; 2 | mod terminal; 3 | mod updates; 4 | mod utils; 5 | 6 | use clap::{Parser, Subcommand}; 7 | use colored::Colorize; 8 | use lsp::{start_lsp_server, LspBridgeConfig}; 9 | use std::net::Ipv4Addr; 10 | use terminal::{set_default_command, start_server}; 11 | use updates::UpdateChecker; 12 | use utils::get_ip_address; 13 | 14 | const DEFAULT_PORT: u16 = 8767; 15 | const LOCAL_IP: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1); 16 | 17 | #[derive(Parser)] 18 | #[command(name = "acodex_server(axs)",version, author = "Raunak Raj ", about = "CLI/Server backend to serve pty over socket", long_about = None)] 19 | struct Cli { 20 | /// Port to start the server 21 | #[arg(short, long, default_value_t = DEFAULT_PORT, value_parser = clap::value_parser!(u16).range(1..), global = true)] 22 | port: u16, 23 | /// Start the server on local network (ip) 24 | #[arg(short, long, global = true)] 25 | ip: bool, 26 | /// Custom command or shell for interactive PTY (e.g. "/usr/bin/bash") 27 | #[arg(short = 'c', long = "command")] 28 | command_override: Option, 29 | /// Allow all origins for CORS (dangerous). By default only https://localhost is allowed. 30 | #[arg(long = "allow-any-origin", global = true)] 31 | allow_any_origin: bool, 32 | #[command(subcommand)] 33 | command: Option, 34 | } 35 | 36 | #[derive(Subcommand)] 37 | enum Commands { 38 | /// Update axs server 39 | Update, 40 | /// Start a WebSocket LSP bridge for a stdio language server 41 | Lsp { 42 | /// The language server binary to run (e.g. "rust-analyzer") 43 | server: String, 44 | /// Additional arguments to forward to the language server 45 | #[arg(trailing_var_arg = true)] 46 | server_args: Vec, 47 | }, 48 | } 49 | 50 | fn print_update_available(current_version: &str, new_version: &str) { 51 | println!("\n{}", "═".repeat(40).yellow()); 52 | println!("{}", " 🎉 Update Available!".bright_yellow().bold()); 53 | println!(" Current version: {}", current_version.bright_red()); 54 | println!(" Latest version: {}", new_version.bright_green()); 55 | println!(" To update, run: {} {}", "axs".cyan(), "update".cyan()); 56 | println!("{}\n", "═".repeat(40).yellow()); 57 | } 58 | 59 | async fn check_updates_in_background() { 60 | let checker = UpdateChecker::new(env!("CARGO_PKG_VERSION")); 61 | match checker.check_update().await { 62 | Ok(Some(version)) => { 63 | print_update_available(env!("CARGO_PKG_VERSION"), &version); 64 | } 65 | Err(e) => eprintln!( 66 | "{} {}", 67 | "⚠️".yellow(), 68 | format!("Failed to check for updates: {e}").red() 69 | ), 70 | _ => {} 71 | } 72 | } 73 | 74 | #[tokio::main] 75 | async fn main() { 76 | let cli: Cli = Cli::parse(); 77 | 78 | let Cli { 79 | port, 80 | ip, 81 | command_override, 82 | allow_any_origin, 83 | command, 84 | } = cli; 85 | 86 | match command { 87 | Some(Commands::Update) => { 88 | println!("{} {}", "⟳".blue().bold(), "Checking for updates...".blue()); 89 | 90 | let checker = UpdateChecker::new(env!("CARGO_PKG_VERSION")); 91 | 92 | match checker.check_update().await { 93 | Ok(Some(version)) => { 94 | println!( 95 | "{} Found new version: {}", 96 | "↓".bright_green(), 97 | version.green() 98 | ); 99 | println!( 100 | "{} {}", 101 | "⟳".blue(), 102 | "Downloading and installing update...".blue() 103 | ); 104 | 105 | match checker.update().await { 106 | Ok(()) => { 107 | println!( 108 | "\n{} {}", 109 | "✓".bright_green().bold(), 110 | "Update successful! Please restart axs.".green().bold() 111 | ); 112 | } 113 | Err(e) => { 114 | eprintln!( 115 | "\n{} {} {}", 116 | "✗".red().bold(), 117 | "Update failed:".red().bold(), 118 | e 119 | ); 120 | std::process::exit(1); 121 | } 122 | } 123 | } 124 | Ok(None) => { 125 | println!( 126 | "{} {}", 127 | "✓".bright_green().bold(), 128 | "You're already on the latest version!".green().bold() 129 | ); 130 | } 131 | Err(e) => { 132 | eprintln!( 133 | "{} {} {}", 134 | "✗".red().bold(), 135 | "Failed to check for updates:".red().bold(), 136 | e 137 | ); 138 | std::process::exit(1); 139 | } 140 | } 141 | } 142 | Some(Commands::Lsp { 143 | server, 144 | server_args, 145 | }) => { 146 | let host = if ip { 147 | get_ip_address().unwrap_or_else(|| { 148 | println!( 149 | "{} localhost.", 150 | "Error: IP address not found. Starting server on" 151 | .red() 152 | .bold() 153 | ); 154 | LOCAL_IP 155 | }) 156 | } else { 157 | LOCAL_IP 158 | }; 159 | 160 | let config = LspBridgeConfig { 161 | program: server, 162 | args: server_args, 163 | }; 164 | 165 | start_lsp_server(host, port, allow_any_origin, config).await; 166 | } 167 | None => { 168 | tokio::task::spawn(check_updates_in_background()); 169 | 170 | if let Some(cmd) = command_override { 171 | // Set custom default command for interactive terminals 172 | set_default_command(cmd); 173 | } 174 | 175 | let ip = if ip { 176 | get_ip_address().unwrap_or_else(|| { 177 | println!( 178 | "{} localhost.", 179 | "Error: IP address not found. Starting server on" 180 | .red() 181 | .bold() 182 | ); 183 | LOCAL_IP 184 | }) 185 | } else { 186 | LOCAL_IP 187 | }; 188 | 189 | start_server(ip, port, allow_any_origin).await; 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/lsp.rs: -------------------------------------------------------------------------------- 1 | //! LSP WebSocket Proxy 2 | 3 | use axum::extract::ws::{Message, WebSocket, WebSocketUpgrade}; 4 | use axum::http::HeaderValue; 5 | use axum::response::IntoResponse; 6 | use axum::routing::get; 7 | use axum::Router; 8 | use bytes::{Buf, BufMut, BytesMut}; 9 | use futures::{SinkExt, StreamExt}; 10 | use nom::{ 11 | branch::alt, 12 | bytes::streaming::{is_not, tag, take_until}, 13 | character::streaming::{char, crlf, digit1, space0}, 14 | combinator::{map, map_res, opt}, 15 | multi::length_data, 16 | sequence::{delimited, terminated, tuple}, 17 | IResult, 18 | }; 19 | use std::io::Write; 20 | use std::net::Ipv4Addr; 21 | use std::process::Stdio; 22 | use std::str; 23 | use std::sync::Arc; 24 | use tokio::process::Command; 25 | use tokio_util::codec::{Decoder, Encoder, FramedRead, FramedWrite}; 26 | use tower_http::cors::{Any, CorsLayer}; 27 | use tower_http::trace::{DefaultMakeSpan, TraceLayer}; 28 | use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; 29 | 30 | #[derive(Clone)] 31 | pub struct LspBridgeConfig { 32 | pub program: String, 33 | pub args: Vec, 34 | } 35 | 36 | #[derive(Clone)] 37 | struct LspState { 38 | config: Arc, 39 | } 40 | 41 | pub async fn start_lsp_server( 42 | host: Ipv4Addr, 43 | port: u16, 44 | allow_any_origin: bool, 45 | config: LspBridgeConfig, 46 | ) { 47 | tracing_subscriber::registry() 48 | .with( 49 | tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { 50 | format!("{}=info,tower_http=info", env!("CARGO_CRATE_NAME")).into() 51 | }), 52 | ) 53 | .with(tracing_subscriber::fmt::layer()) 54 | .init(); 55 | 56 | tracing::info!( 57 | program = %config.program, 58 | args = ?config.args, 59 | "Starting LSP bridge server", 60 | ); 61 | 62 | let cors = if allow_any_origin { 63 | CorsLayer::new() 64 | .allow_origin(Any) 65 | .allow_methods(Any) 66 | .allow_headers(Any) 67 | } else { 68 | let localhost = "https://localhost" 69 | .parse::() 70 | .expect("valid origin"); 71 | CorsLayer::new() 72 | .allow_origin(localhost) 73 | .allow_methods(Any) 74 | .allow_headers(Any) 75 | }; 76 | 77 | let state = LspState { 78 | config: Arc::new(config), 79 | }; 80 | 81 | let app = Router::new() 82 | .route("/", get(upgrade_lsp_bridge)) 83 | .with_state(state) 84 | .layer( 85 | TraceLayer::new_for_http() 86 | .make_span_with(DefaultMakeSpan::default().include_headers(true)), 87 | ) 88 | .layer(cors); 89 | 90 | let addr: std::net::SocketAddr = (host, port).into(); 91 | 92 | match tokio::net::TcpListener::bind(addr).await { 93 | Ok(listener) => { 94 | tracing::info!("listening on {}", listener.local_addr().unwrap()); 95 | 96 | if let Err(e) = axum::serve(listener, app).await { 97 | tracing::error!("Server error: {}", e); 98 | } 99 | } 100 | Err(e) => { 101 | if e.kind() == std::io::ErrorKind::AddrInUse { 102 | tracing::error!( 103 | "Port {} is already in use. Please kill other instances or apps using this port.", 104 | port 105 | ); 106 | } else { 107 | tracing::error!("Failed to bind: {}", e); 108 | } 109 | } 110 | } 111 | } 112 | 113 | async fn upgrade_lsp_bridge( 114 | ws: WebSocketUpgrade, 115 | axum::extract::State(state): axum::extract::State, 116 | ) -> impl IntoResponse { 117 | let config = state.config.clone(); 118 | ws.on_upgrade(move |socket| async move { 119 | tracing::info!("connected"); 120 | if let Err(err) = run_bridge(socket, config).await { 121 | tracing::error!(error = %err, "connection error"); 122 | } 123 | tracing::info!("disconnected"); 124 | }) 125 | } 126 | 127 | /// Run the bridge between a WebSocket client and an LSP server process 128 | async fn run_bridge(socket: WebSocket, config: Arc) -> Result<(), String> { 129 | let mut command = Command::new(&config.program); 130 | command.args(&config.args); 131 | command.stdin(Stdio::piped()); 132 | command.stdout(Stdio::piped()); 133 | command.kill_on_drop(true); 134 | 135 | tracing::info!( 136 | "starting {} in {:?}", 137 | config.program, 138 | std::env::current_dir() 139 | ); 140 | 141 | let mut child = command 142 | .spawn() 143 | .map_err(|e| format!("Failed to spawn LSP command '{}': {e}", config.program))?; 144 | 145 | tracing::trace!("running {}", config.program); 146 | 147 | let stdin = child 148 | .stdin 149 | .take() 150 | .ok_or_else(|| "Failed to capture LSP stdin".to_string())?; 151 | let stdout = child 152 | .stdout 153 | .take() 154 | .ok_or_else(|| "Failed to capture LSP stdout".to_string())?; 155 | 156 | // Create framed readers/writers 157 | let mut server_send = FramedWrite::new(stdin, LspFrameCodec::default()); 158 | let mut server_recv = FramedRead::new(stdout, LspFrameCodec::default()); 159 | 160 | // Split WebSocket 161 | let (mut client_send, client_recv) = socket.split(); 162 | 163 | // Process client messages, filtering to just what we care about 164 | let mut client_recv = client_recv.filter_map(filter_map_ws_message).boxed(); 165 | 166 | let mut client_msg = client_recv.next(); 167 | let mut server_msg = server_recv.next(); 168 | 169 | loop { 170 | tokio::select! { 171 | // From Client 172 | from_client = &mut client_msg => { 173 | match from_client { 174 | // Text message from client 175 | Some(Ok(ClientMessage::Text(text))) => { 176 | tracing::trace!("-> {}", if text.len() > 200 { &text[..200] } else { &text }); 177 | if let Err(e) = server_send.send(text).await { 178 | tracing::error!(error = %e, "failed to send to server"); 179 | break; 180 | } 181 | } 182 | 183 | // Ping from client 184 | Some(Ok(ClientMessage::Ping(data))) => { 185 | if client_send.send(Message::Pong(data.into())).await.is_err() { 186 | break; 187 | } 188 | } 189 | 190 | // Pong from client (keep-alive response) 191 | Some(Ok(ClientMessage::Pong)) => { 192 | tracing::trace!("received pong"); 193 | } 194 | 195 | // Close from client 196 | Some(Ok(ClientMessage::Close)) => { 197 | tracing::info!("received Close message"); 198 | break; 199 | } 200 | 201 | // WebSocket error 202 | Some(Err(e)) => { 203 | tracing::error!(error = %e, "websocket error"); 204 | break; 205 | } 206 | 207 | // Connection closed 208 | None => { 209 | tracing::info!("connection closed"); 210 | break; 211 | } 212 | } 213 | 214 | client_msg = client_recv.next(); 215 | } 216 | 217 | // From Server 218 | from_server = &mut server_msg => { 219 | match from_server { 220 | // Serialized LSP message 221 | Some(Ok(text)) => { 222 | tracing::trace!("<- {}", if text.len() > 200 { &text[..200] } else { &text }); 223 | if client_send.send(Message::Text(text.into())).await.is_err() { 224 | tracing::error!("failed to send to client"); 225 | break; 226 | } 227 | } 228 | 229 | // Codec error 230 | Some(Err(e)) => { 231 | tracing::error!(error = %e, "codec error"); 232 | } 233 | 234 | // Server exited 235 | None => { 236 | tracing::error!("server process exited unexpectedly"); 237 | let _ = client_send.send(Message::Close(None)).await; 238 | break; 239 | } 240 | } 241 | 242 | server_msg = server_recv.next(); 243 | } 244 | } 245 | } 246 | 247 | Ok(()) 248 | } 249 | 250 | // Client message handling 251 | 252 | enum ClientMessage { 253 | Text(String), 254 | Ping(Vec), 255 | Pong, 256 | Close, 257 | } 258 | 259 | async fn filter_map_ws_message( 260 | msg: Result, 261 | ) -> Option> { 262 | match msg { 263 | Ok(Message::Text(text)) => Some(Ok(ClientMessage::Text(text.to_string()))), 264 | Ok(Message::Binary(data)) => { 265 | // Try to decode as text 266 | match String::from_utf8(data.to_vec()) { 267 | Ok(text) => Some(Ok(ClientMessage::Text(text))), 268 | Err(_) => None, // Ignore non-UTF8 binary 269 | } 270 | } 271 | Ok(Message::Ping(data)) => Some(Ok(ClientMessage::Ping(data.to_vec()))), 272 | Ok(Message::Pong(_)) => Some(Ok(ClientMessage::Pong)), 273 | Ok(Message::Close(_)) => Some(Ok(ClientMessage::Close)), 274 | Err(e) => Some(Err(e)), 275 | } 276 | } 277 | 278 | #[derive(Debug)] 279 | pub enum CodecError { 280 | MissingHeader, 281 | InvalidLength, 282 | InvalidType, 283 | Encode(std::io::Error), 284 | Utf8(std::str::Utf8Error), 285 | } 286 | 287 | impl std::fmt::Display for CodecError { 288 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 289 | match self { 290 | Self::MissingHeader => write!(f, "missing required `Content-Length` header"), 291 | Self::InvalidLength => write!(f, "unable to parse content length"), 292 | Self::InvalidType => write!(f, "unable to parse content type"), 293 | Self::Encode(e) => write!(f, "failed to encode frame: {}", e), 294 | Self::Utf8(e) => write!(f, "frame contains invalid UTF8: {}", e), 295 | } 296 | } 297 | } 298 | 299 | impl std::error::Error for CodecError { 300 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 301 | match self { 302 | Self::Encode(e) => Some(e), 303 | Self::Utf8(e) => Some(e), 304 | _ => None, 305 | } 306 | } 307 | } 308 | 309 | impl From for CodecError { 310 | fn from(error: std::io::Error) -> Self { 311 | Self::Encode(error) 312 | } 313 | } 314 | 315 | impl From for CodecError { 316 | fn from(error: std::str::Utf8Error) -> Self { 317 | Self::Utf8(error) 318 | } 319 | } 320 | 321 | #[derive(Clone, Debug, Default)] 322 | pub struct LspFrameCodec { 323 | remaining_bytes: usize, 324 | } 325 | 326 | impl Encoder for LspFrameCodec { 327 | type Error = CodecError; 328 | 329 | fn encode(&mut self, item: String, dst: &mut BytesMut) -> Result<(), Self::Error> { 330 | if !item.is_empty() { 331 | // Reserve space: "Content-Length: " (16) + digits + "\r\n\r\n" (4) + body 332 | dst.reserve(item.len() + number_of_digits(item.len()) + 20); 333 | let mut writer = dst.writer(); 334 | write!(writer, "Content-Length: {}\r\n\r\n{}", item.len(), item)?; 335 | writer.flush()?; 336 | } 337 | Ok(()) 338 | } 339 | } 340 | 341 | impl Decoder for LspFrameCodec { 342 | type Item = String; 343 | type Error = CodecError; 344 | 345 | fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { 346 | if self.remaining_bytes > src.len() { 347 | return Ok(None); 348 | } 349 | 350 | match parse_message(src) { 351 | Ok((remaining, message)) => { 352 | let message = str::from_utf8(message)?.to_string(); 353 | let len = src.len() - remaining.len(); 354 | src.advance(len); 355 | self.remaining_bytes = 0; 356 | // Ignore empty frame 357 | if message.is_empty() { 358 | Ok(None) 359 | } else { 360 | Ok(Some(message)) 361 | } 362 | } 363 | 364 | Err(nom::Err::Incomplete(nom::Needed::Size(needed))) => { 365 | self.remaining_bytes = needed.get(); 366 | Ok(None) 367 | } 368 | 369 | Err(nom::Err::Incomplete(nom::Needed::Unknown)) => Ok(None), 370 | 371 | Err(nom::Err::Error(err)) | Err(nom::Err::Failure(err)) => { 372 | let code = err.code; 373 | let parsed_bytes = src.len() - err.input.len(); 374 | src.advance(parsed_bytes); 375 | match find_next_message(src) { 376 | Ok((_, position)) => src.advance(position), 377 | Err(_) => src.advance(src.len()), 378 | } 379 | match code { 380 | nom::error::ErrorKind::Digit | nom::error::ErrorKind::MapRes => { 381 | Err(CodecError::InvalidLength) 382 | } 383 | nom::error::ErrorKind::Char | nom::error::ErrorKind::IsNot => { 384 | Err(CodecError::InvalidType) 385 | } 386 | _ => Err(CodecError::MissingHeader), 387 | } 388 | } 389 | } 390 | } 391 | } 392 | 393 | #[inline] 394 | fn number_of_digits(mut n: usize) -> usize { 395 | let mut num_digits = 0; 396 | while n > 0 { 397 | n /= 10; 398 | num_digits += 1; 399 | } 400 | num_digits 401 | } 402 | 403 | // LSP Message Parser 404 | 405 | /// Get JSON message from input using the Content-Length header. 406 | fn parse_message(input: &[u8]) -> IResult<&[u8], &[u8]> { 407 | let content_len = delimited(tag("Content-Length: "), digit1, crlf); 408 | 409 | let utf8 = alt((tag("utf-8"), tag("utf8"))); 410 | let charset = tuple((char(';'), space0, tag("charset="), utf8)); 411 | let content_type = tuple((tag("Content-Type: "), is_not(";\r"), opt(charset), crlf)); 412 | 413 | let header = terminated(terminated(content_len, opt(content_type)), crlf); 414 | 415 | let header = map_res(header, str::from_utf8); 416 | let length = map_res(header, |s: &str| s.parse::()); 417 | let mut message = length_data(length); 418 | 419 | message(input) 420 | } 421 | 422 | fn find_next_message(input: &[u8]) -> IResult<&[u8], usize> { 423 | map(take_until("Content-Length"), |s: &[u8]| s.len())(input) 424 | } 425 | -------------------------------------------------------------------------------- /src/terminal/handlers.rs: -------------------------------------------------------------------------------- 1 | use super::buffer::CircularBuffer; 2 | use super::get_default_command; 3 | use super::types::*; 4 | use crate::utils::parse_u16; 5 | use axum::{ 6 | body::Bytes, 7 | extract::{ 8 | ws::{Message, WebSocket, WebSocketUpgrade}, 9 | Path, State, 10 | }, 11 | response::IntoResponse, 12 | Json, 13 | }; 14 | use futures::{SinkExt, StreamExt}; 15 | use portable_pty::{native_pty_system, Child, ChildKiller, CommandBuilder, MasterPty, PtySize}; 16 | use regex::Regex; 17 | use std::io::Write; 18 | use std::time::SystemTime; 19 | use std::{ 20 | io::Read, 21 | path::PathBuf, 22 | sync::{mpsc, Arc}, 23 | time::Duration, 24 | }; 25 | use tokio::sync::Mutex; 26 | use tokio::task::spawn_blocking; 27 | 28 | pub struct TerminalSession { 29 | pub master: Arc>>, 30 | pub child_killer: Arc>>, 31 | pub child: Arc>>, 32 | pub reader: Arc>>, 33 | pub writer: Arc>>, 34 | pub buffer: Arc>, 35 | pub last_accessed: Arc>, 36 | } 37 | 38 | pub async fn create_terminal( 39 | State(sessions): State, 40 | Json(options): Json, 41 | ) -> impl IntoResponse { 42 | let rows = parse_u16(&options.rows, "rows").expect("failed"); 43 | let cols = parse_u16(&options.cols, "cols").expect("failed"); 44 | tracing::info!("Creating new terminal with cols={}, rows={}", cols, rows); 45 | 46 | let pty_system = native_pty_system(); 47 | 48 | // Determine the command to run for the interactive PTY 49 | let mut program = String::from("login"); 50 | let mut args: Vec = Vec::new(); 51 | if let Some(cmd) = get_default_command() { 52 | let parts: Vec = cmd.split_whitespace().map(|s| s.to_string()).collect(); 53 | if !parts.is_empty() { 54 | program = parts[0].clone(); 55 | if parts.len() > 1 { 56 | args = parts[1..].to_vec(); 57 | } 58 | } 59 | } 60 | 61 | let size = PtySize { 62 | rows, 63 | cols, 64 | pixel_width: 0, 65 | pixel_height: 0, 66 | }; 67 | 68 | match pty_system.openpty(size) { 69 | Ok(pair) => { 70 | let mut cmd = CommandBuilder::new(program); 71 | if !args.is_empty() { 72 | let arg_refs: Vec<&str> = args.iter().map(|s| s.as_str()).collect(); 73 | cmd.args(arg_refs); 74 | } 75 | match pair.slave.spawn_command(cmd) { 76 | Ok(child) => { 77 | let pid = child.process_id().unwrap_or(0); 78 | tracing::info!("Terminal created successfully with PID: {}", pid); 79 | drop(pair.slave); // Release slave after spawning 80 | 81 | let reader = Arc::new(Mutex::new(pair.master.try_clone_reader().unwrap())); 82 | let writer = Arc::new(Mutex::new(pair.master.take_writer().unwrap())); 83 | let master = Arc::new(Mutex::new(pair.master as Box)); 84 | let child_killer = Arc::new(Mutex::new(child.clone_killer())); 85 | 86 | let session = TerminalSession { 87 | master, 88 | child_killer, 89 | child: Arc::new(Mutex::new(child)), 90 | reader, 91 | writer, 92 | buffer: Arc::new(Mutex::new(CircularBuffer::new(MAX_BUFFER_SIZE))), 93 | last_accessed: Arc::new(Mutex::new(SystemTime::now())), 94 | }; 95 | 96 | sessions.lock().await.insert(pid, session); 97 | (axum::http::StatusCode::OK, pid.to_string()).into_response() 98 | } 99 | Err(e) => { 100 | tracing::error!("Failed to spawn command: {}", e); 101 | Json(ErrorResponse { 102 | error: format!("Failed to spawn command: {e}"), 103 | }) 104 | .into_response() 105 | } 106 | } 107 | } 108 | Err(e) => { 109 | tracing::error!("Failed to open PTY: {}", e); 110 | Json(ErrorResponse { 111 | error: format!("Failed to open PTY: {e}"), 112 | }) 113 | .into_response() 114 | } 115 | } 116 | } 117 | 118 | pub async fn resize_terminal( 119 | State(sessions): State, 120 | Path(pid): Path, 121 | Json(options): Json, 122 | ) -> impl IntoResponse { 123 | let rows = parse_u16(&options.rows, "rows").expect("Failed"); 124 | let cols = parse_u16(&options.cols, "cols").expect("Failed"); 125 | tracing::info!("Resizing terminal {} to cols={}, rows={}", pid, cols, rows); 126 | let mut sessions = sessions.lock().await; 127 | if let Some(session) = sessions.get_mut(&pid) { 128 | let size = PtySize { 129 | rows, 130 | cols, 131 | pixel_width: 0, 132 | pixel_height: 0, 133 | }; 134 | 135 | match session.master.lock().await.resize(size) { 136 | Ok(_) => Json(serde_json::json!({"success": true})).into_response(), 137 | Err(e) => Json(ErrorResponse { 138 | error: format!("Failed to resize: {e}"), 139 | }) 140 | .into_response(), 141 | } 142 | } else { 143 | Json(ErrorResponse { 144 | error: "Session not found".to_string(), 145 | }) 146 | .into_response() 147 | } 148 | } 149 | 150 | pub async fn terminal_websocket( 151 | ws: WebSocketUpgrade, 152 | Path(pid): Path, 153 | State(sessions): State, 154 | ) -> impl IntoResponse { 155 | tracing::info!("WebSocket connection request for terminal {}", pid); 156 | ws.on_upgrade(move |socket| handle_socket(socket, pid, sessions)) 157 | } 158 | 159 | async fn handle_socket(socket: WebSocket, pid: u32, sessions: Sessions) { 160 | let (mut sender, mut receiver) = socket.split(); 161 | 162 | let session_lock = sessions.lock().await; 163 | if let Some(session) = session_lock.get(&pid) { 164 | // Update last accessed time 165 | let mut last_accessed = session.last_accessed.lock().await; 166 | *last_accessed = SystemTime::now(); 167 | drop(last_accessed); 168 | 169 | tracing::info!("WebSocket connection established for terminal {}", pid); 170 | let reader = session.reader.clone(); 171 | let writer = session.writer.clone(); 172 | let buffer = session.buffer.clone(); 173 | let child = session.child.clone(); 174 | drop(session_lock); 175 | 176 | // Send initial buffer contents 177 | let buffer_guard = buffer.lock().await; 178 | let contents = buffer_guard.get_contents(); 179 | if !contents.is_empty() { 180 | let _ = sender.send(Message::Binary(contents)).await; 181 | } 182 | drop(buffer_guard); 183 | 184 | // Create channels for communication 185 | let (output_tx, mut output_rx) = tokio::sync::mpsc::channel::>(100); 186 | let (exit_tx, mut exit_rx) = tokio::sync::mpsc::channel::(1); 187 | 188 | let pty_to_ws = { 189 | let reader = reader.clone(); 190 | let child = child.clone(); 191 | 192 | tokio::spawn(async move { 193 | // Spawn read task 194 | let read_task = spawn_blocking({ 195 | let reader = reader.clone(); 196 | let output_tx = output_tx.clone(); 197 | move || { 198 | let mut read_buffer = [0u8; 1024]; 199 | loop { 200 | let n = { 201 | let mut reader_guard = reader.blocking_lock(); 202 | match reader_guard.read(&mut read_buffer) { 203 | Ok(n) if n > 0 => n, 204 | _ => break, 205 | } 206 | }; 207 | 208 | let data = read_buffer[..n].to_vec(); 209 | if output_tx.blocking_send(data).is_err() { 210 | break; 211 | } 212 | } 213 | } 214 | }); 215 | 216 | // Spawn wait task 217 | let wait_task = spawn_blocking({ 218 | let child = child.clone(); 219 | let exit_tx = exit_tx.clone(); 220 | move || { 221 | let mut child_guard = child.blocking_lock(); 222 | if let Ok(exit_status) = child_guard.wait() { 223 | let _ = exit_tx.blocking_send(exit_status); 224 | } 225 | } 226 | }); 227 | 228 | // Wait for tasks to complete 229 | let _ = tokio::join!(read_task, wait_task); 230 | }) 231 | }; 232 | 233 | // Handle output and exit events 234 | let output_handler = tokio::spawn(async move { 235 | loop { 236 | tokio::select! { 237 | data = output_rx.recv() => { 238 | if let Some(data) = data { 239 | let mut buffer_guard = buffer.lock().await; 240 | buffer_guard.write(&data); 241 | drop(buffer_guard); 242 | 243 | if sender.send(Message::Binary(Bytes::from(data))).await.is_err() { 244 | break; 245 | } 246 | } else { 247 | break; 248 | } 249 | } 250 | exit_status = exit_rx.recv() => { 251 | if let Some(exit_status) = exit_status { 252 | let exit_message = if exit_status.success() { 253 | ProcessExitMessage { 254 | exit_code: Some(0), 255 | signal: None, 256 | message: "Process exited successfully".to_string(), 257 | } 258 | } else { 259 | ProcessExitMessage { 260 | exit_code: Some(1), 261 | signal: None, 262 | message: "Process exited with non-zero status".to_string(), 263 | } 264 | }; 265 | 266 | // Send exit message to WebSocket 267 | let exit_json = serde_json::to_string(&exit_message).unwrap_or_else(|_| 268 | "{\"exit_code\":1,\"signal\":null,\"message\":\"Process exited\"}".to_string() 269 | ); 270 | 271 | let _ = sender.send(Message::Text(format!("{{\"type\":\"exit\",\"data\":{exit_json}}}").into())).await; 272 | 273 | // Remove session from sessions map 274 | let mut sessions_guard = sessions.lock().await; 275 | sessions_guard.remove(&pid); 276 | break; 277 | } 278 | } 279 | } 280 | } 281 | }); 282 | 283 | // Handle WebSocket input to PTY 284 | let ws_to_pty = { 285 | let writer = writer.clone(); 286 | tokio::spawn(async move { 287 | let (tx, rx) = std::sync::mpsc::channel::>(); 288 | let tx = std::sync::Arc::new(tx); 289 | let tx_clone = tx.clone(); 290 | 291 | let write_handle = spawn_blocking(move || { 292 | while let Ok(data) = rx.recv() { 293 | let mut writer_guard = writer.blocking_lock(); 294 | if writer_guard.write_all(&data).is_err() || writer_guard.flush().is_err() { 295 | break; 296 | } 297 | } 298 | }); 299 | 300 | while let Some(Ok(message)) = receiver.next().await { 301 | let data: axum::body::Bytes = match message { 302 | Message::Text(text) => Bytes::from(text), 303 | Message::Binary(data) => data, 304 | Message::Close(_) => break, 305 | _ => continue, 306 | }; 307 | 308 | if tx_clone.send(data.to_vec()).is_err() { 309 | break; 310 | } 311 | } 312 | 313 | // Clean up 314 | drop(tx_clone); 315 | let _ = write_handle.await; 316 | }) 317 | }; 318 | 319 | // Wait for any task to complete 320 | tokio::select! { 321 | _ = pty_to_ws => { 322 | tracing::info!("PTY to WebSocket task completed for terminal {}", pid); 323 | } 324 | _ = output_handler => { 325 | tracing::info!("Output handler completed for terminal {}", pid); 326 | } 327 | _ = ws_to_pty => { 328 | tracing::info!("WebSocket to PTY task completed for terminal {}", pid); 329 | } 330 | } 331 | } else { 332 | tracing::error!("Session {} not found", pid); 333 | } 334 | } 335 | 336 | pub async fn terminate_terminal( 337 | State(sessions): State, 338 | Path(pid): Path, 339 | ) -> impl IntoResponse { 340 | tracing::info!("Terminating terminal {}", pid); 341 | let mut sessions = sessions.lock().await; 342 | 343 | if let Some(session) = sessions.remove(&pid) { 344 | let result = { 345 | session 346 | .child_killer 347 | .lock() 348 | .await 349 | .kill() 350 | .map_err(|e| e.to_string()) 351 | }; 352 | drop(session.writer.lock().await); 353 | drop(session.reader.lock().await); 354 | 355 | // Clear the circular buffer 356 | if let Ok(mut buffer) = session.buffer.try_lock() { 357 | buffer.data.clear(); 358 | } 359 | 360 | match result { 361 | Ok(_) => { 362 | tracing::info!("Terminal {} terminated successfully", pid); 363 | Json(serde_json::json!({"success": true})).into_response() 364 | } 365 | Err(e) => { 366 | tracing::error!("Failed to terminate terminal {}: {}", pid, e); 367 | Json(ErrorResponse { 368 | error: format!("Failed to terminate terminal {pid}: {e}"), 369 | }) 370 | .into_response() 371 | } 372 | } 373 | } else { 374 | tracing::error!("Failed to terminate terminal {}: session not found", pid); 375 | Json(ErrorResponse { 376 | error: "Session not found".to_string(), 377 | }) 378 | .into_response() 379 | } 380 | } 381 | 382 | pub async fn execute_command(Json(options): Json) -> impl IntoResponse { 383 | let cwd = options.cwd.or(options.u_cwd).unwrap_or("".to_string()); 384 | 385 | tracing::info!( 386 | command = %options.command, 387 | cwd = %cwd, 388 | "Executing command" 389 | ); 390 | 391 | // Use a POSIX shell to execute non-interactive commands 392 | let shell = String::from("sh"); 393 | let cwd = if cwd.is_empty() { 394 | std::env::var("HOME") 395 | .map(PathBuf::from) 396 | .unwrap_or_else(|_| std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))) 397 | } else { 398 | PathBuf::from(cwd) 399 | }; 400 | 401 | if !cwd.exists() { 402 | return ( 403 | axum::http::StatusCode::BAD_REQUEST, 404 | Json(CommandResponse { 405 | output: String::new(), 406 | error: Some("Working directory does not exist".to_string()), 407 | }), 408 | ) 409 | .into_response(); 410 | } 411 | 412 | let command = options.command.clone(); 413 | 414 | // Execute command in a blocking task 415 | let result = spawn_blocking(move || { 416 | // Set up PTY 417 | let pty_system = native_pty_system(); 418 | let size = PtySize { 419 | rows: 24, 420 | cols: 80, 421 | pixel_width: 0, 422 | pixel_height: 0, 423 | }; 424 | 425 | let pair = pty_system.openpty(size)?; 426 | 427 | // Set up command 428 | let mut cmd = CommandBuilder::new(shell); 429 | cmd.args(["-c", &command]); 430 | cmd.cwd(cwd); 431 | 432 | // Spawn the command 433 | let mut child = pair.slave.spawn_command(cmd)?; 434 | drop(pair.slave); 435 | 436 | let mut reader = pair.master.try_clone_reader()?; 437 | let writer = pair.master.take_writer()?; 438 | 439 | // Create channel for reading output 440 | let (tx, rx) = std::sync::mpsc::channel::>(); 441 | 442 | // Spawn a thread for reading 443 | let read_thread = std::thread::spawn(move || { 444 | let mut buffer = [0u8; 1024]; 445 | loop { 446 | match reader.read(&mut buffer) { 447 | Ok(0) => break, // EOF 448 | Ok(n) => { 449 | if tx.send(buffer[..n].to_vec()).is_err() { 450 | break; 451 | } 452 | } 453 | Err(_) => break, 454 | } 455 | } 456 | }); 457 | 458 | // Collect output with timeout 459 | let timeout_duration = Duration::from_secs(30); 460 | let start_time = SystemTime::now(); 461 | let mut output = Vec::new(); 462 | 463 | loop { 464 | match rx.recv_timeout(Duration::from_millis(100)) { 465 | Ok(data) => { 466 | output.extend(data); 467 | } 468 | Err(mpsc::RecvTimeoutError::Timeout) => { 469 | if start_time.elapsed().unwrap_or_default() > timeout_duration { 470 | child.kill()?; 471 | return Err("Command execution timed out".into()); 472 | } 473 | } 474 | Err(mpsc::RecvTimeoutError::Disconnected) => break, 475 | } 476 | 477 | // Check if process has finished 478 | if let Ok(Some(_)) = child.try_wait() { 479 | break; 480 | } 481 | } 482 | 483 | // Clean up resources 484 | drop(writer); 485 | let _ = read_thread.join(); 486 | child.wait()?; 487 | 488 | Ok::, Box>(output) 489 | }) 490 | .await; 491 | 492 | // Process the result 493 | match result { 494 | Ok(Ok(output)) => { 495 | let output_str = String::from_utf8_lossy(&output).into_owned(); 496 | 497 | // Clean ANSI escape sequences 498 | let ansi_regex = 499 | Regex::new(r"\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]|\x1B\[[0-9]+[A-Za-z]").unwrap(); 500 | let cleaned_output = ansi_regex.replace_all(&output_str, "").to_string(); 501 | 502 | tracing::info!( 503 | output_length = cleaned_output.len(), 504 | "Command completed successfully" 505 | ); 506 | 507 | ( 508 | axum::http::StatusCode::OK, 509 | Json(CommandResponse { 510 | output: cleaned_output, 511 | error: None, 512 | }), 513 | ) 514 | .into_response() 515 | } 516 | Ok(Err(e)) => { 517 | tracing::error!("Command execution failed: {}", e); 518 | ( 519 | axum::http::StatusCode::INTERNAL_SERVER_ERROR, 520 | Json(CommandResponse { 521 | output: String::new(), 522 | error: Some(e.to_string()), 523 | }), 524 | ) 525 | .into_response() 526 | } 527 | Err(e) => { 528 | tracing::error!("Blocking task failed: {}", e); 529 | ( 530 | axum::http::StatusCode::INTERNAL_SERVER_ERROR, 531 | Json(CommandResponse { 532 | output: String::new(), 533 | error: Some("Internal server error".to_string()), 534 | }), 535 | ) 536 | .into_response() 537 | } 538 | } 539 | } 540 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.1.3" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "anstream" 31 | version = "0.6.18" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" 34 | dependencies = [ 35 | "anstyle", 36 | "anstyle-parse", 37 | "anstyle-query", 38 | "anstyle-wincon", 39 | "colorchoice", 40 | "is_terminal_polyfill", 41 | "utf8parse", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle" 46 | version = "1.0.10" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" 49 | 50 | [[package]] 51 | name = "anstyle-parse" 52 | version = "0.2.6" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" 55 | dependencies = [ 56 | "utf8parse", 57 | ] 58 | 59 | [[package]] 60 | name = "anstyle-query" 61 | version = "1.1.2" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" 64 | dependencies = [ 65 | "windows-sys 0.59.0", 66 | ] 67 | 68 | [[package]] 69 | name = "anstyle-wincon" 70 | version = "3.0.7" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" 73 | dependencies = [ 74 | "anstyle", 75 | "once_cell", 76 | "windows-sys 0.59.0", 77 | ] 78 | 79 | [[package]] 80 | name = "anyhow" 81 | version = "1.0.97" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" 84 | 85 | [[package]] 86 | name = "autocfg" 87 | version = "1.4.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 90 | 91 | [[package]] 92 | name = "axs" 93 | version = "0.2.10" 94 | dependencies = [ 95 | "axum", 96 | "bytes", 97 | "clap", 98 | "colored", 99 | "futures", 100 | "nom", 101 | "pnet", 102 | "portable-pty", 103 | "regex", 104 | "reqwest", 105 | "semver", 106 | "serde", 107 | "serde_json", 108 | "tokio", 109 | "tokio-util", 110 | "tower-http", 111 | "tracing", 112 | "tracing-subscriber", 113 | ] 114 | 115 | [[package]] 116 | name = "axum" 117 | version = "0.8.3" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "de45108900e1f9b9242f7f2e254aa3e2c029c921c258fe9e6b4217eeebd54288" 120 | dependencies = [ 121 | "axum-core", 122 | "base64", 123 | "bytes", 124 | "form_urlencoded", 125 | "futures-util", 126 | "http", 127 | "http-body", 128 | "http-body-util", 129 | "hyper", 130 | "hyper-util", 131 | "itoa", 132 | "matchit", 133 | "memchr", 134 | "mime", 135 | "percent-encoding", 136 | "pin-project-lite", 137 | "rustversion", 138 | "serde", 139 | "serde_json", 140 | "serde_path_to_error", 141 | "serde_urlencoded", 142 | "sha1", 143 | "sync_wrapper", 144 | "tokio", 145 | "tokio-tungstenite", 146 | "tower", 147 | "tower-layer", 148 | "tower-service", 149 | "tracing", 150 | ] 151 | 152 | [[package]] 153 | name = "axum-core" 154 | version = "0.5.2" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" 157 | dependencies = [ 158 | "bytes", 159 | "futures-core", 160 | "http", 161 | "http-body", 162 | "http-body-util", 163 | "mime", 164 | "pin-project-lite", 165 | "rustversion", 166 | "sync_wrapper", 167 | "tower-layer", 168 | "tower-service", 169 | "tracing", 170 | ] 171 | 172 | [[package]] 173 | name = "backtrace" 174 | version = "0.3.74" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 177 | dependencies = [ 178 | "addr2line", 179 | "cfg-if", 180 | "libc", 181 | "miniz_oxide", 182 | "object", 183 | "rustc-demangle", 184 | "windows-targets 0.52.6", 185 | ] 186 | 187 | [[package]] 188 | name = "base64" 189 | version = "0.22.1" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 192 | 193 | [[package]] 194 | name = "bitflags" 195 | version = "1.3.2" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 198 | 199 | [[package]] 200 | name = "bitflags" 201 | version = "2.9.0" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" 204 | 205 | [[package]] 206 | name = "block-buffer" 207 | version = "0.10.4" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 210 | dependencies = [ 211 | "generic-array", 212 | ] 213 | 214 | [[package]] 215 | name = "bumpalo" 216 | version = "3.17.0" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 219 | 220 | [[package]] 221 | name = "bytes" 222 | version = "1.10.1" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 225 | 226 | [[package]] 227 | name = "cc" 228 | version = "1.2.18" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c" 231 | dependencies = [ 232 | "shlex", 233 | ] 234 | 235 | [[package]] 236 | name = "cfg-if" 237 | version = "1.0.0" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 240 | 241 | [[package]] 242 | name = "cfg_aliases" 243 | version = "0.1.1" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" 246 | 247 | [[package]] 248 | name = "cfg_aliases" 249 | version = "0.2.1" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 252 | 253 | [[package]] 254 | name = "clap" 255 | version = "4.5.35" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" 258 | dependencies = [ 259 | "clap_builder", 260 | "clap_derive", 261 | ] 262 | 263 | [[package]] 264 | name = "clap_builder" 265 | version = "4.5.35" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" 268 | dependencies = [ 269 | "anstream", 270 | "anstyle", 271 | "clap_lex", 272 | "strsim", 273 | ] 274 | 275 | [[package]] 276 | name = "clap_derive" 277 | version = "4.5.32" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" 280 | dependencies = [ 281 | "heck", 282 | "proc-macro2", 283 | "quote", 284 | "syn", 285 | ] 286 | 287 | [[package]] 288 | name = "clap_lex" 289 | version = "0.7.4" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 292 | 293 | [[package]] 294 | name = "colorchoice" 295 | version = "1.0.3" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" 298 | 299 | [[package]] 300 | name = "colored" 301 | version = "3.0.0" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" 304 | dependencies = [ 305 | "windows-sys 0.59.0", 306 | ] 307 | 308 | [[package]] 309 | name = "cpufeatures" 310 | version = "0.2.17" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" 313 | dependencies = [ 314 | "libc", 315 | ] 316 | 317 | [[package]] 318 | name = "crypto-common" 319 | version = "0.1.6" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 322 | dependencies = [ 323 | "generic-array", 324 | "typenum", 325 | ] 326 | 327 | [[package]] 328 | name = "data-encoding" 329 | version = "2.8.0" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" 332 | 333 | [[package]] 334 | name = "digest" 335 | version = "0.10.7" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 338 | dependencies = [ 339 | "block-buffer", 340 | "crypto-common", 341 | ] 342 | 343 | [[package]] 344 | name = "displaydoc" 345 | version = "0.2.5" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 348 | dependencies = [ 349 | "proc-macro2", 350 | "quote", 351 | "syn", 352 | ] 353 | 354 | [[package]] 355 | name = "downcast-rs" 356 | version = "1.2.1" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" 359 | 360 | [[package]] 361 | name = "filedescriptor" 362 | version = "0.8.3" 363 | source = "git+https://github.com/wez/wezterm?rev=1e941fca5c17155b5f64a74221a396ef12ef71d0#1e941fca5c17155b5f64a74221a396ef12ef71d0" 364 | dependencies = [ 365 | "libc", 366 | "thiserror 1.0.69", 367 | "winapi", 368 | ] 369 | 370 | [[package]] 371 | name = "fnv" 372 | version = "1.0.7" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 375 | 376 | [[package]] 377 | name = "form_urlencoded" 378 | version = "1.2.1" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 381 | dependencies = [ 382 | "percent-encoding", 383 | ] 384 | 385 | [[package]] 386 | name = "futures" 387 | version = "0.3.31" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 390 | dependencies = [ 391 | "futures-channel", 392 | "futures-core", 393 | "futures-executor", 394 | "futures-io", 395 | "futures-sink", 396 | "futures-task", 397 | "futures-util", 398 | ] 399 | 400 | [[package]] 401 | name = "futures-channel" 402 | version = "0.3.31" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 405 | dependencies = [ 406 | "futures-core", 407 | "futures-sink", 408 | ] 409 | 410 | [[package]] 411 | name = "futures-core" 412 | version = "0.3.31" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 415 | 416 | [[package]] 417 | name = "futures-executor" 418 | version = "0.3.31" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 421 | dependencies = [ 422 | "futures-core", 423 | "futures-task", 424 | "futures-util", 425 | ] 426 | 427 | [[package]] 428 | name = "futures-io" 429 | version = "0.3.31" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 432 | 433 | [[package]] 434 | name = "futures-macro" 435 | version = "0.3.31" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 438 | dependencies = [ 439 | "proc-macro2", 440 | "quote", 441 | "syn", 442 | ] 443 | 444 | [[package]] 445 | name = "futures-sink" 446 | version = "0.3.31" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 449 | 450 | [[package]] 451 | name = "futures-task" 452 | version = "0.3.31" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 455 | 456 | [[package]] 457 | name = "futures-util" 458 | version = "0.3.31" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 461 | dependencies = [ 462 | "futures-channel", 463 | "futures-core", 464 | "futures-io", 465 | "futures-macro", 466 | "futures-sink", 467 | "futures-task", 468 | "memchr", 469 | "pin-project-lite", 470 | "pin-utils", 471 | "slab", 472 | ] 473 | 474 | [[package]] 475 | name = "generic-array" 476 | version = "0.14.7" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 479 | dependencies = [ 480 | "typenum", 481 | "version_check", 482 | ] 483 | 484 | [[package]] 485 | name = "getrandom" 486 | version = "0.2.15" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 489 | dependencies = [ 490 | "cfg-if", 491 | "js-sys", 492 | "libc", 493 | "wasi 0.11.0+wasi-snapshot-preview1", 494 | "wasm-bindgen", 495 | ] 496 | 497 | [[package]] 498 | name = "getrandom" 499 | version = "0.3.2" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" 502 | dependencies = [ 503 | "cfg-if", 504 | "js-sys", 505 | "libc", 506 | "r-efi", 507 | "wasi 0.14.2+wasi-0.2.4", 508 | "wasm-bindgen", 509 | ] 510 | 511 | [[package]] 512 | name = "gimli" 513 | version = "0.31.1" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 516 | 517 | [[package]] 518 | name = "glob" 519 | version = "0.3.2" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" 522 | 523 | [[package]] 524 | name = "heck" 525 | version = "0.5.0" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 528 | 529 | [[package]] 530 | name = "http" 531 | version = "1.3.1" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 534 | dependencies = [ 535 | "bytes", 536 | "fnv", 537 | "itoa", 538 | ] 539 | 540 | [[package]] 541 | name = "http-body" 542 | version = "1.0.1" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 545 | dependencies = [ 546 | "bytes", 547 | "http", 548 | ] 549 | 550 | [[package]] 551 | name = "http-body-util" 552 | version = "0.1.3" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 555 | dependencies = [ 556 | "bytes", 557 | "futures-core", 558 | "http", 559 | "http-body", 560 | "pin-project-lite", 561 | ] 562 | 563 | [[package]] 564 | name = "httparse" 565 | version = "1.10.1" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 568 | 569 | [[package]] 570 | name = "httpdate" 571 | version = "1.0.3" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 574 | 575 | [[package]] 576 | name = "hyper" 577 | version = "1.6.0" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" 580 | dependencies = [ 581 | "bytes", 582 | "futures-channel", 583 | "futures-util", 584 | "http", 585 | "http-body", 586 | "httparse", 587 | "httpdate", 588 | "itoa", 589 | "pin-project-lite", 590 | "smallvec", 591 | "tokio", 592 | "want", 593 | ] 594 | 595 | [[package]] 596 | name = "hyper-rustls" 597 | version = "0.27.5" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" 600 | dependencies = [ 601 | "futures-util", 602 | "http", 603 | "hyper", 604 | "hyper-util", 605 | "rustls", 606 | "rustls-pki-types", 607 | "tokio", 608 | "tokio-rustls", 609 | "tower-service", 610 | "webpki-roots", 611 | ] 612 | 613 | [[package]] 614 | name = "hyper-util" 615 | version = "0.1.11" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" 618 | dependencies = [ 619 | "bytes", 620 | "futures-channel", 621 | "futures-util", 622 | "http", 623 | "http-body", 624 | "hyper", 625 | "libc", 626 | "pin-project-lite", 627 | "socket2", 628 | "tokio", 629 | "tower-service", 630 | "tracing", 631 | ] 632 | 633 | [[package]] 634 | name = "icu_collections" 635 | version = "1.5.0" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" 638 | dependencies = [ 639 | "displaydoc", 640 | "yoke", 641 | "zerofrom", 642 | "zerovec", 643 | ] 644 | 645 | [[package]] 646 | name = "icu_locid" 647 | version = "1.5.0" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" 650 | dependencies = [ 651 | "displaydoc", 652 | "litemap", 653 | "tinystr", 654 | "writeable", 655 | "zerovec", 656 | ] 657 | 658 | [[package]] 659 | name = "icu_locid_transform" 660 | version = "1.5.0" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" 663 | dependencies = [ 664 | "displaydoc", 665 | "icu_locid", 666 | "icu_locid_transform_data", 667 | "icu_provider", 668 | "tinystr", 669 | "zerovec", 670 | ] 671 | 672 | [[package]] 673 | name = "icu_locid_transform_data" 674 | version = "1.5.1" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" 677 | 678 | [[package]] 679 | name = "icu_normalizer" 680 | version = "1.5.0" 681 | source = "registry+https://github.com/rust-lang/crates.io-index" 682 | checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" 683 | dependencies = [ 684 | "displaydoc", 685 | "icu_collections", 686 | "icu_normalizer_data", 687 | "icu_properties", 688 | "icu_provider", 689 | "smallvec", 690 | "utf16_iter", 691 | "utf8_iter", 692 | "write16", 693 | "zerovec", 694 | ] 695 | 696 | [[package]] 697 | name = "icu_normalizer_data" 698 | version = "1.5.1" 699 | source = "registry+https://github.com/rust-lang/crates.io-index" 700 | checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" 701 | 702 | [[package]] 703 | name = "icu_properties" 704 | version = "1.5.1" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" 707 | dependencies = [ 708 | "displaydoc", 709 | "icu_collections", 710 | "icu_locid_transform", 711 | "icu_properties_data", 712 | "icu_provider", 713 | "tinystr", 714 | "zerovec", 715 | ] 716 | 717 | [[package]] 718 | name = "icu_properties_data" 719 | version = "1.5.1" 720 | source = "registry+https://github.com/rust-lang/crates.io-index" 721 | checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" 722 | 723 | [[package]] 724 | name = "icu_provider" 725 | version = "1.5.0" 726 | source = "registry+https://github.com/rust-lang/crates.io-index" 727 | checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" 728 | dependencies = [ 729 | "displaydoc", 730 | "icu_locid", 731 | "icu_provider_macros", 732 | "stable_deref_trait", 733 | "tinystr", 734 | "writeable", 735 | "yoke", 736 | "zerofrom", 737 | "zerovec", 738 | ] 739 | 740 | [[package]] 741 | name = "icu_provider_macros" 742 | version = "1.5.0" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" 745 | dependencies = [ 746 | "proc-macro2", 747 | "quote", 748 | "syn", 749 | ] 750 | 751 | [[package]] 752 | name = "idna" 753 | version = "1.0.3" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 756 | dependencies = [ 757 | "idna_adapter", 758 | "smallvec", 759 | "utf8_iter", 760 | ] 761 | 762 | [[package]] 763 | name = "idna_adapter" 764 | version = "1.2.0" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" 767 | dependencies = [ 768 | "icu_normalizer", 769 | "icu_properties", 770 | ] 771 | 772 | [[package]] 773 | name = "ipnet" 774 | version = "2.11.0" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" 777 | 778 | [[package]] 779 | name = "ipnetwork" 780 | version = "0.20.0" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" 783 | dependencies = [ 784 | "serde", 785 | ] 786 | 787 | [[package]] 788 | name = "is_terminal_polyfill" 789 | version = "1.70.1" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 792 | 793 | [[package]] 794 | name = "itoa" 795 | version = "1.0.15" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 798 | 799 | [[package]] 800 | name = "js-sys" 801 | version = "0.3.77" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 804 | dependencies = [ 805 | "once_cell", 806 | "wasm-bindgen", 807 | ] 808 | 809 | [[package]] 810 | name = "lazy_static" 811 | version = "1.5.0" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 814 | 815 | [[package]] 816 | name = "libc" 817 | version = "0.2.171" 818 | source = "registry+https://github.com/rust-lang/crates.io-index" 819 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" 820 | 821 | [[package]] 822 | name = "litemap" 823 | version = "0.7.5" 824 | source = "registry+https://github.com/rust-lang/crates.io-index" 825 | checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" 826 | 827 | [[package]] 828 | name = "lock_api" 829 | version = "0.4.12" 830 | source = "registry+https://github.com/rust-lang/crates.io-index" 831 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 832 | dependencies = [ 833 | "autocfg", 834 | "scopeguard", 835 | ] 836 | 837 | [[package]] 838 | name = "log" 839 | version = "0.4.27" 840 | source = "registry+https://github.com/rust-lang/crates.io-index" 841 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 842 | 843 | [[package]] 844 | name = "matchers" 845 | version = "0.1.0" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 848 | dependencies = [ 849 | "regex-automata 0.1.10", 850 | ] 851 | 852 | [[package]] 853 | name = "matchit" 854 | version = "0.8.4" 855 | source = "registry+https://github.com/rust-lang/crates.io-index" 856 | checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" 857 | 858 | [[package]] 859 | name = "memchr" 860 | version = "2.7.4" 861 | source = "registry+https://github.com/rust-lang/crates.io-index" 862 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 863 | 864 | [[package]] 865 | name = "mime" 866 | version = "0.3.17" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 869 | 870 | [[package]] 871 | name = "minimal-lexical" 872 | version = "0.2.1" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 875 | 876 | [[package]] 877 | name = "miniz_oxide" 878 | version = "0.8.8" 879 | source = "registry+https://github.com/rust-lang/crates.io-index" 880 | checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" 881 | dependencies = [ 882 | "adler2", 883 | ] 884 | 885 | [[package]] 886 | name = "mio" 887 | version = "1.0.3" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 890 | dependencies = [ 891 | "libc", 892 | "wasi 0.11.0+wasi-snapshot-preview1", 893 | "windows-sys 0.52.0", 894 | ] 895 | 896 | [[package]] 897 | name = "nix" 898 | version = "0.28.0" 899 | source = "registry+https://github.com/rust-lang/crates.io-index" 900 | checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" 901 | dependencies = [ 902 | "bitflags 2.9.0", 903 | "cfg-if", 904 | "cfg_aliases 0.1.1", 905 | "libc", 906 | ] 907 | 908 | [[package]] 909 | name = "no-std-net" 910 | version = "0.6.0" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" 913 | 914 | [[package]] 915 | name = "nom" 916 | version = "7.1.3" 917 | source = "registry+https://github.com/rust-lang/crates.io-index" 918 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 919 | dependencies = [ 920 | "memchr", 921 | "minimal-lexical", 922 | ] 923 | 924 | [[package]] 925 | name = "nu-ansi-term" 926 | version = "0.46.0" 927 | source = "registry+https://github.com/rust-lang/crates.io-index" 928 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 929 | dependencies = [ 930 | "overload", 931 | "winapi", 932 | ] 933 | 934 | [[package]] 935 | name = "object" 936 | version = "0.36.7" 937 | source = "registry+https://github.com/rust-lang/crates.io-index" 938 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 939 | dependencies = [ 940 | "memchr", 941 | ] 942 | 943 | [[package]] 944 | name = "once_cell" 945 | version = "1.21.3" 946 | source = "registry+https://github.com/rust-lang/crates.io-index" 947 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 948 | 949 | [[package]] 950 | name = "overload" 951 | version = "0.1.1" 952 | source = "registry+https://github.com/rust-lang/crates.io-index" 953 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 954 | 955 | [[package]] 956 | name = "parking_lot" 957 | version = "0.12.3" 958 | source = "registry+https://github.com/rust-lang/crates.io-index" 959 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 960 | dependencies = [ 961 | "lock_api", 962 | "parking_lot_core", 963 | ] 964 | 965 | [[package]] 966 | name = "parking_lot_core" 967 | version = "0.9.10" 968 | source = "registry+https://github.com/rust-lang/crates.io-index" 969 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 970 | dependencies = [ 971 | "cfg-if", 972 | "libc", 973 | "redox_syscall", 974 | "smallvec", 975 | "windows-targets 0.52.6", 976 | ] 977 | 978 | [[package]] 979 | name = "percent-encoding" 980 | version = "2.3.1" 981 | source = "registry+https://github.com/rust-lang/crates.io-index" 982 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 983 | 984 | [[package]] 985 | name = "pin-project-lite" 986 | version = "0.2.16" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 989 | 990 | [[package]] 991 | name = "pin-utils" 992 | version = "0.1.0" 993 | source = "registry+https://github.com/rust-lang/crates.io-index" 994 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 995 | 996 | [[package]] 997 | name = "pnet" 998 | version = "0.35.0" 999 | source = "registry+https://github.com/rust-lang/crates.io-index" 1000 | checksum = "682396b533413cc2e009fbb48aadf93619a149d3e57defba19ff50ce0201bd0d" 1001 | dependencies = [ 1002 | "ipnetwork", 1003 | "pnet_base", 1004 | "pnet_datalink", 1005 | "pnet_packet", 1006 | "pnet_sys", 1007 | "pnet_transport", 1008 | ] 1009 | 1010 | [[package]] 1011 | name = "pnet_base" 1012 | version = "0.35.0" 1013 | source = "registry+https://github.com/rust-lang/crates.io-index" 1014 | checksum = "ffc190d4067df16af3aba49b3b74c469e611cad6314676eaf1157f31aa0fb2f7" 1015 | dependencies = [ 1016 | "no-std-net", 1017 | ] 1018 | 1019 | [[package]] 1020 | name = "pnet_datalink" 1021 | version = "0.35.0" 1022 | source = "registry+https://github.com/rust-lang/crates.io-index" 1023 | checksum = "e79e70ec0be163102a332e1d2d5586d362ad76b01cec86f830241f2b6452a7b7" 1024 | dependencies = [ 1025 | "ipnetwork", 1026 | "libc", 1027 | "pnet_base", 1028 | "pnet_sys", 1029 | "winapi", 1030 | ] 1031 | 1032 | [[package]] 1033 | name = "pnet_macros" 1034 | version = "0.35.0" 1035 | source = "registry+https://github.com/rust-lang/crates.io-index" 1036 | checksum = "13325ac86ee1a80a480b0bc8e3d30c25d133616112bb16e86f712dcf8a71c863" 1037 | dependencies = [ 1038 | "proc-macro2", 1039 | "quote", 1040 | "regex", 1041 | "syn", 1042 | ] 1043 | 1044 | [[package]] 1045 | name = "pnet_macros_support" 1046 | version = "0.35.0" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "eed67a952585d509dd0003049b1fc56b982ac665c8299b124b90ea2bdb3134ab" 1049 | dependencies = [ 1050 | "pnet_base", 1051 | ] 1052 | 1053 | [[package]] 1054 | name = "pnet_packet" 1055 | version = "0.35.0" 1056 | source = "registry+https://github.com/rust-lang/crates.io-index" 1057 | checksum = "4c96ebadfab635fcc23036ba30a7d33a80c39e8461b8bd7dc7bb186acb96560f" 1058 | dependencies = [ 1059 | "glob", 1060 | "pnet_base", 1061 | "pnet_macros", 1062 | "pnet_macros_support", 1063 | ] 1064 | 1065 | [[package]] 1066 | name = "pnet_sys" 1067 | version = "0.35.0" 1068 | source = "registry+https://github.com/rust-lang/crates.io-index" 1069 | checksum = "7d4643d3d4db6b08741050c2f3afa9a892c4244c085a72fcda93c9c2c9a00f4b" 1070 | dependencies = [ 1071 | "libc", 1072 | "winapi", 1073 | ] 1074 | 1075 | [[package]] 1076 | name = "pnet_transport" 1077 | version = "0.35.0" 1078 | source = "registry+https://github.com/rust-lang/crates.io-index" 1079 | checksum = "5f604d98bc2a6591cf719b58d3203fd882bdd6bf1db696c4ac97978e9f4776bf" 1080 | dependencies = [ 1081 | "libc", 1082 | "pnet_base", 1083 | "pnet_packet", 1084 | "pnet_sys", 1085 | ] 1086 | 1087 | [[package]] 1088 | name = "portable-pty" 1089 | version = "0.9.0" 1090 | source = "git+https://github.com/wez/wezterm?rev=1e941fca5c17155b5f64a74221a396ef12ef71d0#1e941fca5c17155b5f64a74221a396ef12ef71d0" 1091 | dependencies = [ 1092 | "anyhow", 1093 | "bitflags 1.3.2", 1094 | "downcast-rs", 1095 | "filedescriptor", 1096 | "lazy_static", 1097 | "libc", 1098 | "log", 1099 | "nix", 1100 | "serial2", 1101 | "shared_library", 1102 | "shell-words", 1103 | "winapi", 1104 | "winreg", 1105 | ] 1106 | 1107 | [[package]] 1108 | name = "ppv-lite86" 1109 | version = "0.2.21" 1110 | source = "registry+https://github.com/rust-lang/crates.io-index" 1111 | checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 1112 | dependencies = [ 1113 | "zerocopy", 1114 | ] 1115 | 1116 | [[package]] 1117 | name = "proc-macro2" 1118 | version = "1.0.94" 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" 1120 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 1121 | dependencies = [ 1122 | "unicode-ident", 1123 | ] 1124 | 1125 | [[package]] 1126 | name = "quinn" 1127 | version = "0.11.7" 1128 | source = "registry+https://github.com/rust-lang/crates.io-index" 1129 | checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012" 1130 | dependencies = [ 1131 | "bytes", 1132 | "cfg_aliases 0.2.1", 1133 | "pin-project-lite", 1134 | "quinn-proto", 1135 | "quinn-udp", 1136 | "rustc-hash", 1137 | "rustls", 1138 | "socket2", 1139 | "thiserror 2.0.12", 1140 | "tokio", 1141 | "tracing", 1142 | "web-time", 1143 | ] 1144 | 1145 | [[package]] 1146 | name = "quinn-proto" 1147 | version = "0.11.10" 1148 | source = "registry+https://github.com/rust-lang/crates.io-index" 1149 | checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc" 1150 | dependencies = [ 1151 | "bytes", 1152 | "getrandom 0.3.2", 1153 | "rand", 1154 | "ring", 1155 | "rustc-hash", 1156 | "rustls", 1157 | "rustls-pki-types", 1158 | "slab", 1159 | "thiserror 2.0.12", 1160 | "tinyvec", 1161 | "tracing", 1162 | "web-time", 1163 | ] 1164 | 1165 | [[package]] 1166 | name = "quinn-udp" 1167 | version = "0.5.11" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5" 1170 | dependencies = [ 1171 | "cfg_aliases 0.2.1", 1172 | "libc", 1173 | "once_cell", 1174 | "socket2", 1175 | "tracing", 1176 | "windows-sys 0.59.0", 1177 | ] 1178 | 1179 | [[package]] 1180 | name = "quote" 1181 | version = "1.0.40" 1182 | source = "registry+https://github.com/rust-lang/crates.io-index" 1183 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 1184 | dependencies = [ 1185 | "proc-macro2", 1186 | ] 1187 | 1188 | [[package]] 1189 | name = "r-efi" 1190 | version = "5.2.0" 1191 | source = "registry+https://github.com/rust-lang/crates.io-index" 1192 | checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" 1193 | 1194 | [[package]] 1195 | name = "rand" 1196 | version = "0.9.0" 1197 | source = "registry+https://github.com/rust-lang/crates.io-index" 1198 | checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" 1199 | dependencies = [ 1200 | "rand_chacha", 1201 | "rand_core", 1202 | "zerocopy", 1203 | ] 1204 | 1205 | [[package]] 1206 | name = "rand_chacha" 1207 | version = "0.9.0" 1208 | source = "registry+https://github.com/rust-lang/crates.io-index" 1209 | checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 1210 | dependencies = [ 1211 | "ppv-lite86", 1212 | "rand_core", 1213 | ] 1214 | 1215 | [[package]] 1216 | name = "rand_core" 1217 | version = "0.9.3" 1218 | source = "registry+https://github.com/rust-lang/crates.io-index" 1219 | checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 1220 | dependencies = [ 1221 | "getrandom 0.3.2", 1222 | ] 1223 | 1224 | [[package]] 1225 | name = "redox_syscall" 1226 | version = "0.5.11" 1227 | source = "registry+https://github.com/rust-lang/crates.io-index" 1228 | checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" 1229 | dependencies = [ 1230 | "bitflags 2.9.0", 1231 | ] 1232 | 1233 | [[package]] 1234 | name = "regex" 1235 | version = "1.11.1" 1236 | source = "registry+https://github.com/rust-lang/crates.io-index" 1237 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 1238 | dependencies = [ 1239 | "aho-corasick", 1240 | "memchr", 1241 | "regex-automata 0.4.9", 1242 | "regex-syntax 0.8.5", 1243 | ] 1244 | 1245 | [[package]] 1246 | name = "regex-automata" 1247 | version = "0.1.10" 1248 | source = "registry+https://github.com/rust-lang/crates.io-index" 1249 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 1250 | dependencies = [ 1251 | "regex-syntax 0.6.29", 1252 | ] 1253 | 1254 | [[package]] 1255 | name = "regex-automata" 1256 | version = "0.4.9" 1257 | source = "registry+https://github.com/rust-lang/crates.io-index" 1258 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 1259 | dependencies = [ 1260 | "aho-corasick", 1261 | "memchr", 1262 | "regex-syntax 0.8.5", 1263 | ] 1264 | 1265 | [[package]] 1266 | name = "regex-syntax" 1267 | version = "0.6.29" 1268 | source = "registry+https://github.com/rust-lang/crates.io-index" 1269 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 1270 | 1271 | [[package]] 1272 | name = "regex-syntax" 1273 | version = "0.8.5" 1274 | source = "registry+https://github.com/rust-lang/crates.io-index" 1275 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 1276 | 1277 | [[package]] 1278 | name = "reqwest" 1279 | version = "0.12.15" 1280 | source = "registry+https://github.com/rust-lang/crates.io-index" 1281 | checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" 1282 | dependencies = [ 1283 | "base64", 1284 | "bytes", 1285 | "futures-core", 1286 | "futures-util", 1287 | "http", 1288 | "http-body", 1289 | "http-body-util", 1290 | "hyper", 1291 | "hyper-rustls", 1292 | "hyper-util", 1293 | "ipnet", 1294 | "js-sys", 1295 | "log", 1296 | "mime", 1297 | "once_cell", 1298 | "percent-encoding", 1299 | "pin-project-lite", 1300 | "quinn", 1301 | "rustls", 1302 | "rustls-pemfile", 1303 | "rustls-pki-types", 1304 | "serde", 1305 | "serde_json", 1306 | "serde_urlencoded", 1307 | "sync_wrapper", 1308 | "tokio", 1309 | "tokio-rustls", 1310 | "tower", 1311 | "tower-service", 1312 | "url", 1313 | "wasm-bindgen", 1314 | "wasm-bindgen-futures", 1315 | "web-sys", 1316 | "webpki-roots", 1317 | "windows-registry", 1318 | ] 1319 | 1320 | [[package]] 1321 | name = "ring" 1322 | version = "0.17.14" 1323 | source = "registry+https://github.com/rust-lang/crates.io-index" 1324 | checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 1325 | dependencies = [ 1326 | "cc", 1327 | "cfg-if", 1328 | "getrandom 0.2.15", 1329 | "libc", 1330 | "untrusted", 1331 | "windows-sys 0.52.0", 1332 | ] 1333 | 1334 | [[package]] 1335 | name = "rustc-demangle" 1336 | version = "0.1.24" 1337 | source = "registry+https://github.com/rust-lang/crates.io-index" 1338 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1339 | 1340 | [[package]] 1341 | name = "rustc-hash" 1342 | version = "2.1.1" 1343 | source = "registry+https://github.com/rust-lang/crates.io-index" 1344 | checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 1345 | 1346 | [[package]] 1347 | name = "rustls" 1348 | version = "0.23.26" 1349 | source = "registry+https://github.com/rust-lang/crates.io-index" 1350 | checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" 1351 | dependencies = [ 1352 | "once_cell", 1353 | "ring", 1354 | "rustls-pki-types", 1355 | "rustls-webpki", 1356 | "subtle", 1357 | "zeroize", 1358 | ] 1359 | 1360 | [[package]] 1361 | name = "rustls-pemfile" 1362 | version = "2.2.0" 1363 | source = "registry+https://github.com/rust-lang/crates.io-index" 1364 | checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" 1365 | dependencies = [ 1366 | "rustls-pki-types", 1367 | ] 1368 | 1369 | [[package]] 1370 | name = "rustls-pki-types" 1371 | version = "1.11.0" 1372 | source = "registry+https://github.com/rust-lang/crates.io-index" 1373 | checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" 1374 | dependencies = [ 1375 | "web-time", 1376 | ] 1377 | 1378 | [[package]] 1379 | name = "rustls-webpki" 1380 | version = "0.103.1" 1381 | source = "registry+https://github.com/rust-lang/crates.io-index" 1382 | checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" 1383 | dependencies = [ 1384 | "ring", 1385 | "rustls-pki-types", 1386 | "untrusted", 1387 | ] 1388 | 1389 | [[package]] 1390 | name = "rustversion" 1391 | version = "1.0.20" 1392 | source = "registry+https://github.com/rust-lang/crates.io-index" 1393 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 1394 | 1395 | [[package]] 1396 | name = "ryu" 1397 | version = "1.0.20" 1398 | source = "registry+https://github.com/rust-lang/crates.io-index" 1399 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 1400 | 1401 | [[package]] 1402 | name = "scopeguard" 1403 | version = "1.2.0" 1404 | source = "registry+https://github.com/rust-lang/crates.io-index" 1405 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1406 | 1407 | [[package]] 1408 | name = "semver" 1409 | version = "1.0.26" 1410 | source = "registry+https://github.com/rust-lang/crates.io-index" 1411 | checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" 1412 | 1413 | [[package]] 1414 | name = "serde" 1415 | version = "1.0.219" 1416 | source = "registry+https://github.com/rust-lang/crates.io-index" 1417 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 1418 | dependencies = [ 1419 | "serde_derive", 1420 | ] 1421 | 1422 | [[package]] 1423 | name = "serde_derive" 1424 | version = "1.0.219" 1425 | source = "registry+https://github.com/rust-lang/crates.io-index" 1426 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 1427 | dependencies = [ 1428 | "proc-macro2", 1429 | "quote", 1430 | "syn", 1431 | ] 1432 | 1433 | [[package]] 1434 | name = "serde_json" 1435 | version = "1.0.140" 1436 | source = "registry+https://github.com/rust-lang/crates.io-index" 1437 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 1438 | dependencies = [ 1439 | "itoa", 1440 | "memchr", 1441 | "ryu", 1442 | "serde", 1443 | ] 1444 | 1445 | [[package]] 1446 | name = "serde_path_to_error" 1447 | version = "0.1.17" 1448 | source = "registry+https://github.com/rust-lang/crates.io-index" 1449 | checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" 1450 | dependencies = [ 1451 | "itoa", 1452 | "serde", 1453 | ] 1454 | 1455 | [[package]] 1456 | name = "serde_urlencoded" 1457 | version = "0.7.1" 1458 | source = "registry+https://github.com/rust-lang/crates.io-index" 1459 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1460 | dependencies = [ 1461 | "form_urlencoded", 1462 | "itoa", 1463 | "ryu", 1464 | "serde", 1465 | ] 1466 | 1467 | [[package]] 1468 | name = "serial2" 1469 | version = "0.2.29" 1470 | source = "registry+https://github.com/rust-lang/crates.io-index" 1471 | checksum = "c7d1d08630509d69f90eff4afcd02c3bd974d979225cbd815ff5942351b14375" 1472 | dependencies = [ 1473 | "cfg-if", 1474 | "libc", 1475 | "winapi", 1476 | ] 1477 | 1478 | [[package]] 1479 | name = "sha1" 1480 | version = "0.10.6" 1481 | source = "registry+https://github.com/rust-lang/crates.io-index" 1482 | checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 1483 | dependencies = [ 1484 | "cfg-if", 1485 | "cpufeatures", 1486 | "digest", 1487 | ] 1488 | 1489 | [[package]] 1490 | name = "sharded-slab" 1491 | version = "0.1.7" 1492 | source = "registry+https://github.com/rust-lang/crates.io-index" 1493 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 1494 | dependencies = [ 1495 | "lazy_static", 1496 | ] 1497 | 1498 | [[package]] 1499 | name = "shared_library" 1500 | version = "0.1.9" 1501 | source = "registry+https://github.com/rust-lang/crates.io-index" 1502 | checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" 1503 | dependencies = [ 1504 | "lazy_static", 1505 | "libc", 1506 | ] 1507 | 1508 | [[package]] 1509 | name = "shell-words" 1510 | version = "1.1.0" 1511 | source = "registry+https://github.com/rust-lang/crates.io-index" 1512 | checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" 1513 | 1514 | [[package]] 1515 | name = "shlex" 1516 | version = "1.3.0" 1517 | source = "registry+https://github.com/rust-lang/crates.io-index" 1518 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1519 | 1520 | [[package]] 1521 | name = "signal-hook-registry" 1522 | version = "1.4.2" 1523 | source = "registry+https://github.com/rust-lang/crates.io-index" 1524 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 1525 | dependencies = [ 1526 | "libc", 1527 | ] 1528 | 1529 | [[package]] 1530 | name = "slab" 1531 | version = "0.4.9" 1532 | source = "registry+https://github.com/rust-lang/crates.io-index" 1533 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1534 | dependencies = [ 1535 | "autocfg", 1536 | ] 1537 | 1538 | [[package]] 1539 | name = "smallvec" 1540 | version = "1.15.0" 1541 | source = "registry+https://github.com/rust-lang/crates.io-index" 1542 | checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" 1543 | 1544 | [[package]] 1545 | name = "socket2" 1546 | version = "0.5.9" 1547 | source = "registry+https://github.com/rust-lang/crates.io-index" 1548 | checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" 1549 | dependencies = [ 1550 | "libc", 1551 | "windows-sys 0.52.0", 1552 | ] 1553 | 1554 | [[package]] 1555 | name = "stable_deref_trait" 1556 | version = "1.2.0" 1557 | source = "registry+https://github.com/rust-lang/crates.io-index" 1558 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1559 | 1560 | [[package]] 1561 | name = "strsim" 1562 | version = "0.11.1" 1563 | source = "registry+https://github.com/rust-lang/crates.io-index" 1564 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1565 | 1566 | [[package]] 1567 | name = "subtle" 1568 | version = "2.6.1" 1569 | source = "registry+https://github.com/rust-lang/crates.io-index" 1570 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1571 | 1572 | [[package]] 1573 | name = "syn" 1574 | version = "2.0.100" 1575 | source = "registry+https://github.com/rust-lang/crates.io-index" 1576 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 1577 | dependencies = [ 1578 | "proc-macro2", 1579 | "quote", 1580 | "unicode-ident", 1581 | ] 1582 | 1583 | [[package]] 1584 | name = "sync_wrapper" 1585 | version = "1.0.2" 1586 | source = "registry+https://github.com/rust-lang/crates.io-index" 1587 | checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 1588 | dependencies = [ 1589 | "futures-core", 1590 | ] 1591 | 1592 | [[package]] 1593 | name = "synstructure" 1594 | version = "0.13.1" 1595 | source = "registry+https://github.com/rust-lang/crates.io-index" 1596 | checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" 1597 | dependencies = [ 1598 | "proc-macro2", 1599 | "quote", 1600 | "syn", 1601 | ] 1602 | 1603 | [[package]] 1604 | name = "thiserror" 1605 | version = "1.0.69" 1606 | source = "registry+https://github.com/rust-lang/crates.io-index" 1607 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 1608 | dependencies = [ 1609 | "thiserror-impl 1.0.69", 1610 | ] 1611 | 1612 | [[package]] 1613 | name = "thiserror" 1614 | version = "2.0.12" 1615 | source = "registry+https://github.com/rust-lang/crates.io-index" 1616 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 1617 | dependencies = [ 1618 | "thiserror-impl 2.0.12", 1619 | ] 1620 | 1621 | [[package]] 1622 | name = "thiserror-impl" 1623 | version = "1.0.69" 1624 | source = "registry+https://github.com/rust-lang/crates.io-index" 1625 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 1626 | dependencies = [ 1627 | "proc-macro2", 1628 | "quote", 1629 | "syn", 1630 | ] 1631 | 1632 | [[package]] 1633 | name = "thiserror-impl" 1634 | version = "2.0.12" 1635 | source = "registry+https://github.com/rust-lang/crates.io-index" 1636 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 1637 | dependencies = [ 1638 | "proc-macro2", 1639 | "quote", 1640 | "syn", 1641 | ] 1642 | 1643 | [[package]] 1644 | name = "thread_local" 1645 | version = "1.1.8" 1646 | source = "registry+https://github.com/rust-lang/crates.io-index" 1647 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 1648 | dependencies = [ 1649 | "cfg-if", 1650 | "once_cell", 1651 | ] 1652 | 1653 | [[package]] 1654 | name = "tinystr" 1655 | version = "0.7.6" 1656 | source = "registry+https://github.com/rust-lang/crates.io-index" 1657 | checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" 1658 | dependencies = [ 1659 | "displaydoc", 1660 | "zerovec", 1661 | ] 1662 | 1663 | [[package]] 1664 | name = "tinyvec" 1665 | version = "1.9.0" 1666 | source = "registry+https://github.com/rust-lang/crates.io-index" 1667 | checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" 1668 | dependencies = [ 1669 | "tinyvec_macros", 1670 | ] 1671 | 1672 | [[package]] 1673 | name = "tinyvec_macros" 1674 | version = "0.1.1" 1675 | source = "registry+https://github.com/rust-lang/crates.io-index" 1676 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1677 | 1678 | [[package]] 1679 | name = "tokio" 1680 | version = "1.44.2" 1681 | source = "registry+https://github.com/rust-lang/crates.io-index" 1682 | checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" 1683 | dependencies = [ 1684 | "backtrace", 1685 | "bytes", 1686 | "libc", 1687 | "mio", 1688 | "parking_lot", 1689 | "pin-project-lite", 1690 | "signal-hook-registry", 1691 | "socket2", 1692 | "tokio-macros", 1693 | "windows-sys 0.52.0", 1694 | ] 1695 | 1696 | [[package]] 1697 | name = "tokio-macros" 1698 | version = "2.5.0" 1699 | source = "registry+https://github.com/rust-lang/crates.io-index" 1700 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 1701 | dependencies = [ 1702 | "proc-macro2", 1703 | "quote", 1704 | "syn", 1705 | ] 1706 | 1707 | [[package]] 1708 | name = "tokio-rustls" 1709 | version = "0.26.2" 1710 | source = "registry+https://github.com/rust-lang/crates.io-index" 1711 | checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" 1712 | dependencies = [ 1713 | "rustls", 1714 | "tokio", 1715 | ] 1716 | 1717 | [[package]] 1718 | name = "tokio-tungstenite" 1719 | version = "0.26.2" 1720 | source = "registry+https://github.com/rust-lang/crates.io-index" 1721 | checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" 1722 | dependencies = [ 1723 | "futures-util", 1724 | "log", 1725 | "tokio", 1726 | "tungstenite", 1727 | ] 1728 | 1729 | [[package]] 1730 | name = "tokio-util" 1731 | version = "0.7.17" 1732 | source = "registry+https://github.com/rust-lang/crates.io-index" 1733 | checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" 1734 | dependencies = [ 1735 | "bytes", 1736 | "futures-core", 1737 | "futures-sink", 1738 | "pin-project-lite", 1739 | "tokio", 1740 | ] 1741 | 1742 | [[package]] 1743 | name = "tower" 1744 | version = "0.5.2" 1745 | source = "registry+https://github.com/rust-lang/crates.io-index" 1746 | checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 1747 | dependencies = [ 1748 | "futures-core", 1749 | "futures-util", 1750 | "pin-project-lite", 1751 | "sync_wrapper", 1752 | "tokio", 1753 | "tower-layer", 1754 | "tower-service", 1755 | "tracing", 1756 | ] 1757 | 1758 | [[package]] 1759 | name = "tower-http" 1760 | version = "0.6.2" 1761 | source = "registry+https://github.com/rust-lang/crates.io-index" 1762 | checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" 1763 | dependencies = [ 1764 | "bitflags 2.9.0", 1765 | "bytes", 1766 | "http", 1767 | "http-body", 1768 | "pin-project-lite", 1769 | "tower-layer", 1770 | "tower-service", 1771 | "tracing", 1772 | ] 1773 | 1774 | [[package]] 1775 | name = "tower-layer" 1776 | version = "0.3.3" 1777 | source = "registry+https://github.com/rust-lang/crates.io-index" 1778 | checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 1779 | 1780 | [[package]] 1781 | name = "tower-service" 1782 | version = "0.3.3" 1783 | source = "registry+https://github.com/rust-lang/crates.io-index" 1784 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1785 | 1786 | [[package]] 1787 | name = "tracing" 1788 | version = "0.1.41" 1789 | source = "registry+https://github.com/rust-lang/crates.io-index" 1790 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1791 | dependencies = [ 1792 | "log", 1793 | "pin-project-lite", 1794 | "tracing-attributes", 1795 | "tracing-core", 1796 | ] 1797 | 1798 | [[package]] 1799 | name = "tracing-attributes" 1800 | version = "0.1.28" 1801 | source = "registry+https://github.com/rust-lang/crates.io-index" 1802 | checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" 1803 | dependencies = [ 1804 | "proc-macro2", 1805 | "quote", 1806 | "syn", 1807 | ] 1808 | 1809 | [[package]] 1810 | name = "tracing-core" 1811 | version = "0.1.33" 1812 | source = "registry+https://github.com/rust-lang/crates.io-index" 1813 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 1814 | dependencies = [ 1815 | "once_cell", 1816 | "valuable", 1817 | ] 1818 | 1819 | [[package]] 1820 | name = "tracing-log" 1821 | version = "0.2.0" 1822 | source = "registry+https://github.com/rust-lang/crates.io-index" 1823 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 1824 | dependencies = [ 1825 | "log", 1826 | "once_cell", 1827 | "tracing-core", 1828 | ] 1829 | 1830 | [[package]] 1831 | name = "tracing-subscriber" 1832 | version = "0.3.19" 1833 | source = "registry+https://github.com/rust-lang/crates.io-index" 1834 | checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" 1835 | dependencies = [ 1836 | "matchers", 1837 | "nu-ansi-term", 1838 | "once_cell", 1839 | "regex", 1840 | "sharded-slab", 1841 | "smallvec", 1842 | "thread_local", 1843 | "tracing", 1844 | "tracing-core", 1845 | "tracing-log", 1846 | ] 1847 | 1848 | [[package]] 1849 | name = "try-lock" 1850 | version = "0.2.5" 1851 | source = "registry+https://github.com/rust-lang/crates.io-index" 1852 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1853 | 1854 | [[package]] 1855 | name = "tungstenite" 1856 | version = "0.26.2" 1857 | source = "registry+https://github.com/rust-lang/crates.io-index" 1858 | checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" 1859 | dependencies = [ 1860 | "bytes", 1861 | "data-encoding", 1862 | "http", 1863 | "httparse", 1864 | "log", 1865 | "rand", 1866 | "sha1", 1867 | "thiserror 2.0.12", 1868 | "utf-8", 1869 | ] 1870 | 1871 | [[package]] 1872 | name = "typenum" 1873 | version = "1.18.0" 1874 | source = "registry+https://github.com/rust-lang/crates.io-index" 1875 | checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 1876 | 1877 | [[package]] 1878 | name = "unicode-ident" 1879 | version = "1.0.18" 1880 | source = "registry+https://github.com/rust-lang/crates.io-index" 1881 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 1882 | 1883 | [[package]] 1884 | name = "untrusted" 1885 | version = "0.9.0" 1886 | source = "registry+https://github.com/rust-lang/crates.io-index" 1887 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1888 | 1889 | [[package]] 1890 | name = "url" 1891 | version = "2.5.4" 1892 | source = "registry+https://github.com/rust-lang/crates.io-index" 1893 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 1894 | dependencies = [ 1895 | "form_urlencoded", 1896 | "idna", 1897 | "percent-encoding", 1898 | ] 1899 | 1900 | [[package]] 1901 | name = "utf-8" 1902 | version = "0.7.6" 1903 | source = "registry+https://github.com/rust-lang/crates.io-index" 1904 | checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 1905 | 1906 | [[package]] 1907 | name = "utf16_iter" 1908 | version = "1.0.5" 1909 | source = "registry+https://github.com/rust-lang/crates.io-index" 1910 | checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" 1911 | 1912 | [[package]] 1913 | name = "utf8_iter" 1914 | version = "1.0.4" 1915 | source = "registry+https://github.com/rust-lang/crates.io-index" 1916 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 1917 | 1918 | [[package]] 1919 | name = "utf8parse" 1920 | version = "0.2.2" 1921 | source = "registry+https://github.com/rust-lang/crates.io-index" 1922 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1923 | 1924 | [[package]] 1925 | name = "valuable" 1926 | version = "0.1.1" 1927 | source = "registry+https://github.com/rust-lang/crates.io-index" 1928 | checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 1929 | 1930 | [[package]] 1931 | name = "version_check" 1932 | version = "0.9.5" 1933 | source = "registry+https://github.com/rust-lang/crates.io-index" 1934 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 1935 | 1936 | [[package]] 1937 | name = "want" 1938 | version = "0.3.1" 1939 | source = "registry+https://github.com/rust-lang/crates.io-index" 1940 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1941 | dependencies = [ 1942 | "try-lock", 1943 | ] 1944 | 1945 | [[package]] 1946 | name = "wasi" 1947 | version = "0.11.0+wasi-snapshot-preview1" 1948 | source = "registry+https://github.com/rust-lang/crates.io-index" 1949 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1950 | 1951 | [[package]] 1952 | name = "wasi" 1953 | version = "0.14.2+wasi-0.2.4" 1954 | source = "registry+https://github.com/rust-lang/crates.io-index" 1955 | checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 1956 | dependencies = [ 1957 | "wit-bindgen-rt", 1958 | ] 1959 | 1960 | [[package]] 1961 | name = "wasm-bindgen" 1962 | version = "0.2.100" 1963 | source = "registry+https://github.com/rust-lang/crates.io-index" 1964 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 1965 | dependencies = [ 1966 | "cfg-if", 1967 | "once_cell", 1968 | "rustversion", 1969 | "wasm-bindgen-macro", 1970 | ] 1971 | 1972 | [[package]] 1973 | name = "wasm-bindgen-backend" 1974 | version = "0.2.100" 1975 | source = "registry+https://github.com/rust-lang/crates.io-index" 1976 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 1977 | dependencies = [ 1978 | "bumpalo", 1979 | "log", 1980 | "proc-macro2", 1981 | "quote", 1982 | "syn", 1983 | "wasm-bindgen-shared", 1984 | ] 1985 | 1986 | [[package]] 1987 | name = "wasm-bindgen-futures" 1988 | version = "0.4.50" 1989 | source = "registry+https://github.com/rust-lang/crates.io-index" 1990 | checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 1991 | dependencies = [ 1992 | "cfg-if", 1993 | "js-sys", 1994 | "once_cell", 1995 | "wasm-bindgen", 1996 | "web-sys", 1997 | ] 1998 | 1999 | [[package]] 2000 | name = "wasm-bindgen-macro" 2001 | version = "0.2.100" 2002 | source = "registry+https://github.com/rust-lang/crates.io-index" 2003 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 2004 | dependencies = [ 2005 | "quote", 2006 | "wasm-bindgen-macro-support", 2007 | ] 2008 | 2009 | [[package]] 2010 | name = "wasm-bindgen-macro-support" 2011 | version = "0.2.100" 2012 | source = "registry+https://github.com/rust-lang/crates.io-index" 2013 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 2014 | dependencies = [ 2015 | "proc-macro2", 2016 | "quote", 2017 | "syn", 2018 | "wasm-bindgen-backend", 2019 | "wasm-bindgen-shared", 2020 | ] 2021 | 2022 | [[package]] 2023 | name = "wasm-bindgen-shared" 2024 | version = "0.2.100" 2025 | source = "registry+https://github.com/rust-lang/crates.io-index" 2026 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 2027 | dependencies = [ 2028 | "unicode-ident", 2029 | ] 2030 | 2031 | [[package]] 2032 | name = "web-sys" 2033 | version = "0.3.77" 2034 | source = "registry+https://github.com/rust-lang/crates.io-index" 2035 | checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 2036 | dependencies = [ 2037 | "js-sys", 2038 | "wasm-bindgen", 2039 | ] 2040 | 2041 | [[package]] 2042 | name = "web-time" 2043 | version = "1.1.0" 2044 | source = "registry+https://github.com/rust-lang/crates.io-index" 2045 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 2046 | dependencies = [ 2047 | "js-sys", 2048 | "wasm-bindgen", 2049 | ] 2050 | 2051 | [[package]] 2052 | name = "webpki-roots" 2053 | version = "0.26.8" 2054 | source = "registry+https://github.com/rust-lang/crates.io-index" 2055 | checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" 2056 | dependencies = [ 2057 | "rustls-pki-types", 2058 | ] 2059 | 2060 | [[package]] 2061 | name = "winapi" 2062 | version = "0.3.9" 2063 | source = "registry+https://github.com/rust-lang/crates.io-index" 2064 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2065 | dependencies = [ 2066 | "winapi-i686-pc-windows-gnu", 2067 | "winapi-x86_64-pc-windows-gnu", 2068 | ] 2069 | 2070 | [[package]] 2071 | name = "winapi-i686-pc-windows-gnu" 2072 | version = "0.4.0" 2073 | source = "registry+https://github.com/rust-lang/crates.io-index" 2074 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2075 | 2076 | [[package]] 2077 | name = "winapi-x86_64-pc-windows-gnu" 2078 | version = "0.4.0" 2079 | source = "registry+https://github.com/rust-lang/crates.io-index" 2080 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2081 | 2082 | [[package]] 2083 | name = "windows-link" 2084 | version = "0.1.1" 2085 | source = "registry+https://github.com/rust-lang/crates.io-index" 2086 | checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" 2087 | 2088 | [[package]] 2089 | name = "windows-registry" 2090 | version = "0.4.0" 2091 | source = "registry+https://github.com/rust-lang/crates.io-index" 2092 | checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" 2093 | dependencies = [ 2094 | "windows-result", 2095 | "windows-strings", 2096 | "windows-targets 0.53.0", 2097 | ] 2098 | 2099 | [[package]] 2100 | name = "windows-result" 2101 | version = "0.3.2" 2102 | source = "registry+https://github.com/rust-lang/crates.io-index" 2103 | checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" 2104 | dependencies = [ 2105 | "windows-link", 2106 | ] 2107 | 2108 | [[package]] 2109 | name = "windows-strings" 2110 | version = "0.3.1" 2111 | source = "registry+https://github.com/rust-lang/crates.io-index" 2112 | checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" 2113 | dependencies = [ 2114 | "windows-link", 2115 | ] 2116 | 2117 | [[package]] 2118 | name = "windows-sys" 2119 | version = "0.52.0" 2120 | source = "registry+https://github.com/rust-lang/crates.io-index" 2121 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2122 | dependencies = [ 2123 | "windows-targets 0.52.6", 2124 | ] 2125 | 2126 | [[package]] 2127 | name = "windows-sys" 2128 | version = "0.59.0" 2129 | source = "registry+https://github.com/rust-lang/crates.io-index" 2130 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 2131 | dependencies = [ 2132 | "windows-targets 0.52.6", 2133 | ] 2134 | 2135 | [[package]] 2136 | name = "windows-targets" 2137 | version = "0.52.6" 2138 | source = "registry+https://github.com/rust-lang/crates.io-index" 2139 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2140 | dependencies = [ 2141 | "windows_aarch64_gnullvm 0.52.6", 2142 | "windows_aarch64_msvc 0.52.6", 2143 | "windows_i686_gnu 0.52.6", 2144 | "windows_i686_gnullvm 0.52.6", 2145 | "windows_i686_msvc 0.52.6", 2146 | "windows_x86_64_gnu 0.52.6", 2147 | "windows_x86_64_gnullvm 0.52.6", 2148 | "windows_x86_64_msvc 0.52.6", 2149 | ] 2150 | 2151 | [[package]] 2152 | name = "windows-targets" 2153 | version = "0.53.0" 2154 | source = "registry+https://github.com/rust-lang/crates.io-index" 2155 | checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" 2156 | dependencies = [ 2157 | "windows_aarch64_gnullvm 0.53.0", 2158 | "windows_aarch64_msvc 0.53.0", 2159 | "windows_i686_gnu 0.53.0", 2160 | "windows_i686_gnullvm 0.53.0", 2161 | "windows_i686_msvc 0.53.0", 2162 | "windows_x86_64_gnu 0.53.0", 2163 | "windows_x86_64_gnullvm 0.53.0", 2164 | "windows_x86_64_msvc 0.53.0", 2165 | ] 2166 | 2167 | [[package]] 2168 | name = "windows_aarch64_gnullvm" 2169 | version = "0.52.6" 2170 | source = "registry+https://github.com/rust-lang/crates.io-index" 2171 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2172 | 2173 | [[package]] 2174 | name = "windows_aarch64_gnullvm" 2175 | version = "0.53.0" 2176 | source = "registry+https://github.com/rust-lang/crates.io-index" 2177 | checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" 2178 | 2179 | [[package]] 2180 | name = "windows_aarch64_msvc" 2181 | version = "0.52.6" 2182 | source = "registry+https://github.com/rust-lang/crates.io-index" 2183 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2184 | 2185 | [[package]] 2186 | name = "windows_aarch64_msvc" 2187 | version = "0.53.0" 2188 | source = "registry+https://github.com/rust-lang/crates.io-index" 2189 | checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" 2190 | 2191 | [[package]] 2192 | name = "windows_i686_gnu" 2193 | version = "0.52.6" 2194 | source = "registry+https://github.com/rust-lang/crates.io-index" 2195 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2196 | 2197 | [[package]] 2198 | name = "windows_i686_gnu" 2199 | version = "0.53.0" 2200 | source = "registry+https://github.com/rust-lang/crates.io-index" 2201 | checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" 2202 | 2203 | [[package]] 2204 | name = "windows_i686_gnullvm" 2205 | version = "0.52.6" 2206 | source = "registry+https://github.com/rust-lang/crates.io-index" 2207 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2208 | 2209 | [[package]] 2210 | name = "windows_i686_gnullvm" 2211 | version = "0.53.0" 2212 | source = "registry+https://github.com/rust-lang/crates.io-index" 2213 | checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" 2214 | 2215 | [[package]] 2216 | name = "windows_i686_msvc" 2217 | version = "0.52.6" 2218 | source = "registry+https://github.com/rust-lang/crates.io-index" 2219 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2220 | 2221 | [[package]] 2222 | name = "windows_i686_msvc" 2223 | version = "0.53.0" 2224 | source = "registry+https://github.com/rust-lang/crates.io-index" 2225 | checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" 2226 | 2227 | [[package]] 2228 | name = "windows_x86_64_gnu" 2229 | version = "0.52.6" 2230 | source = "registry+https://github.com/rust-lang/crates.io-index" 2231 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2232 | 2233 | [[package]] 2234 | name = "windows_x86_64_gnu" 2235 | version = "0.53.0" 2236 | source = "registry+https://github.com/rust-lang/crates.io-index" 2237 | checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" 2238 | 2239 | [[package]] 2240 | name = "windows_x86_64_gnullvm" 2241 | version = "0.52.6" 2242 | source = "registry+https://github.com/rust-lang/crates.io-index" 2243 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2244 | 2245 | [[package]] 2246 | name = "windows_x86_64_gnullvm" 2247 | version = "0.53.0" 2248 | source = "registry+https://github.com/rust-lang/crates.io-index" 2249 | checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" 2250 | 2251 | [[package]] 2252 | name = "windows_x86_64_msvc" 2253 | version = "0.52.6" 2254 | source = "registry+https://github.com/rust-lang/crates.io-index" 2255 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2256 | 2257 | [[package]] 2258 | name = "windows_x86_64_msvc" 2259 | version = "0.53.0" 2260 | source = "registry+https://github.com/rust-lang/crates.io-index" 2261 | checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 2262 | 2263 | [[package]] 2264 | name = "winreg" 2265 | version = "0.10.1" 2266 | source = "registry+https://github.com/rust-lang/crates.io-index" 2267 | checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" 2268 | dependencies = [ 2269 | "winapi", 2270 | ] 2271 | 2272 | [[package]] 2273 | name = "wit-bindgen-rt" 2274 | version = "0.39.0" 2275 | source = "registry+https://github.com/rust-lang/crates.io-index" 2276 | checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 2277 | dependencies = [ 2278 | "bitflags 2.9.0", 2279 | ] 2280 | 2281 | [[package]] 2282 | name = "write16" 2283 | version = "1.0.0" 2284 | source = "registry+https://github.com/rust-lang/crates.io-index" 2285 | checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" 2286 | 2287 | [[package]] 2288 | name = "writeable" 2289 | version = "0.5.5" 2290 | source = "registry+https://github.com/rust-lang/crates.io-index" 2291 | checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" 2292 | 2293 | [[package]] 2294 | name = "yoke" 2295 | version = "0.7.5" 2296 | source = "registry+https://github.com/rust-lang/crates.io-index" 2297 | checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" 2298 | dependencies = [ 2299 | "serde", 2300 | "stable_deref_trait", 2301 | "yoke-derive", 2302 | "zerofrom", 2303 | ] 2304 | 2305 | [[package]] 2306 | name = "yoke-derive" 2307 | version = "0.7.5" 2308 | source = "registry+https://github.com/rust-lang/crates.io-index" 2309 | checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" 2310 | dependencies = [ 2311 | "proc-macro2", 2312 | "quote", 2313 | "syn", 2314 | "synstructure", 2315 | ] 2316 | 2317 | [[package]] 2318 | name = "zerocopy" 2319 | version = "0.8.24" 2320 | source = "registry+https://github.com/rust-lang/crates.io-index" 2321 | checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" 2322 | dependencies = [ 2323 | "zerocopy-derive", 2324 | ] 2325 | 2326 | [[package]] 2327 | name = "zerocopy-derive" 2328 | version = "0.8.24" 2329 | source = "registry+https://github.com/rust-lang/crates.io-index" 2330 | checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" 2331 | dependencies = [ 2332 | "proc-macro2", 2333 | "quote", 2334 | "syn", 2335 | ] 2336 | 2337 | [[package]] 2338 | name = "zerofrom" 2339 | version = "0.1.6" 2340 | source = "registry+https://github.com/rust-lang/crates.io-index" 2341 | checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 2342 | dependencies = [ 2343 | "zerofrom-derive", 2344 | ] 2345 | 2346 | [[package]] 2347 | name = "zerofrom-derive" 2348 | version = "0.1.6" 2349 | source = "registry+https://github.com/rust-lang/crates.io-index" 2350 | checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 2351 | dependencies = [ 2352 | "proc-macro2", 2353 | "quote", 2354 | "syn", 2355 | "synstructure", 2356 | ] 2357 | 2358 | [[package]] 2359 | name = "zeroize" 2360 | version = "1.8.1" 2361 | source = "registry+https://github.com/rust-lang/crates.io-index" 2362 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 2363 | 2364 | [[package]] 2365 | name = "zerovec" 2366 | version = "0.10.4" 2367 | source = "registry+https://github.com/rust-lang/crates.io-index" 2368 | checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" 2369 | dependencies = [ 2370 | "yoke", 2371 | "zerofrom", 2372 | "zerovec-derive", 2373 | ] 2374 | 2375 | [[package]] 2376 | name = "zerovec-derive" 2377 | version = "0.10.3" 2378 | source = "registry+https://github.com/rust-lang/crates.io-index" 2379 | checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" 2380 | dependencies = [ 2381 | "proc-macro2", 2382 | "quote", 2383 | "syn", 2384 | ] 2385 | --------------------------------------------------------------------------------