From 4f8835bd257dc6a9ebad5446a6b6dfd86e2990da Mon Sep 17 00:00:00 2001 From: Michael Mikovsky <77305074+Astatin3@users.noreply.github.com> Date: Sun, 26 Apr 2026 12:57:56 -0600 Subject: [PATCH] Fix examples for renamed leaf endpoint surface Update the remote shell examples to use unshell::leaves and the leaf_endpoint feature-gated endpoint module, and restore the local macro aliasing needed after removing the direct unshell dependency from unshell-leaves. --- Cargo.lock | 3 +- Cargo.toml | 12 ++- examples/protocol/remote_shell_endpoint.rs | 17 ++-- examples/protocol/remote_shell_receive.rs | 32 ++++---- .../protocol/remote_shell_single_endpoint.rs | 20 +++-- src/lib.rs | 3 + unshell-leaves/Cargo.toml | 7 +- unshell-leaves/src/lib.rs | 20 +++-- unshell-leaves/src/remote_shell/endpoint.rs | 6 +- unshell-leaves/src/remote_shell/mod.rs | 77 +++---------------- 10 files changed, 87 insertions(+), 110 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cf394ae..6f5a62e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1454,7 +1454,8 @@ version = "0.1.0" dependencies = [ "portable-pty", "rkyv", - "unshell", + "unshell-macros", + "unshell-protocol", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1be59ce..023a8d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,11 @@ description = "Pure no_std implementation of the UnShell Protocol" default = [] log = [] log_debug = ["log", "dep:chrono"] + +# Leaf features +leaf_endpoint = ["unshell-leaves/leaf_endpoint"] +leaf_tui = ["unshell-leaves/leaf_tui"] + # obfuscate_aes = ["ush-obfuscate/obfuscate_aes"] # obfuscate_ref = ["ush-obfuscate/obfuscate_ref"] @@ -55,9 +60,7 @@ chrono = { workspace = true, optional = true } static_init = { workspace = true } unshell-macros = { path = "./unshell-macros" } unshell-protocol = { workspace = true } - -[dev-dependencies] -unshell-leaves = { workspace = true, features = ["endpoint"] } +unshell-leaves = { workspace = true } [[example]] name = "leaf_derive" @@ -66,14 +69,17 @@ path = "examples/protocol/leaf_derive.rs" [[example]] name = "remote_shell_endpoint" path = "examples/protocol/remote_shell_endpoint.rs" +required-features = ["leaf_endpoint"] [[example]] name = "remote_shell_receive" path = "examples/protocol/remote_shell_receive.rs" +required-features = ["leaf_endpoint"] [[example]] name = "remote_shell_single_endpoint" path = "examples/protocol/remote_shell_single_endpoint.rs" +required-features = ["leaf_endpoint"] [[example]] name = "bench" diff --git a/examples/protocol/remote_shell_endpoint.rs b/examples/protocol/remote_shell_endpoint.rs index 8548acb..264fd15 100644 --- a/examples/protocol/remote_shell_endpoint.rs +++ b/examples/protocol/remote_shell_endpoint.rs @@ -9,29 +9,32 @@ use std::net::TcpStream; use std::sync::mpsc::RecvTimeoutError; use std::time::Duration; +use unshell::leaves::remote_shell; use unshell::protocol::tree::Ingress; -use unshell_leaves::remote_shell; fn main() -> Result<(), Box> { - let mut stream = TcpStream::connect(remote_shell::LISTEN_ADDR)?; - let frame_rx = remote_shell::spawn_frame_reader(stream.try_clone()?); - let mut runtime = remote_shell::build_agent_runtime(); + let mut stream = TcpStream::connect(remote_shell::endpoint::LISTEN_ADDR)?; + let frame_rx = remote_shell::endpoint::spawn_frame_reader(stream.try_clone()?); + let mut runtime = remote_shell::endpoint::build_agent_runtime(); - println!("connected to controller at {}", remote_shell::LISTEN_ADDR); + println!( + "connected to controller at {}", + remote_shell::endpoint::LISTEN_ADDR + ); loop { match frame_rx.recv_timeout(Duration::from_millis(25)) { Ok(result) => { let frame = result?; let outcome = runtime.receive(&Ingress::Parent, frame)?; - remote_shell::write_frames(&mut stream, &outcome.frames)?; + remote_shell::endpoint::write_frames(&mut stream, &outcome.frames)?; } Err(RecvTimeoutError::Timeout) => {} Err(RecvTimeoutError::Disconnected) => break, } let outcome = runtime.poll()?; - remote_shell::write_frames(&mut stream, &outcome.frames)?; + remote_shell::endpoint::write_frames(&mut stream, &outcome.frames)?; } Ok(()) diff --git a/examples/protocol/remote_shell_receive.rs b/examples/protocol/remote_shell_receive.rs index 8213ed9..69b6945 100644 --- a/examples/protocol/remote_shell_receive.rs +++ b/examples/protocol/remote_shell_receive.rs @@ -6,38 +6,40 @@ use std::error::Error; use std::net::TcpListener; +use unshell::leaves::remote_shell; +use unshell::leaves::remote_shell::OpenRequest; +use unshell::protocol::tree::encode_call_reply; use unshell::protocol::tree::{Endpoint, EndpointOutcome, Ingress, LocalEvent}; -use unshell_leaves::remote_shell; fn main() -> Result<(), Box> { - let listener = TcpListener::bind(remote_shell::LISTEN_ADDR)?; - println!("listening on {}", remote_shell::LISTEN_ADDR); + let listener = TcpListener::bind(remote_shell::endpoint::LISTEN_ADDR)?; + println!("listening on {}", remote_shell::endpoint::LISTEN_ADDR); let (mut stream, peer_addr) = listener.accept()?; println!("accepted endpoint connection from {peer_addr}"); - let frame_rx = remote_shell::spawn_frame_reader(stream.try_clone()?); - let mut endpoint = remote_shell::build_controller_endpoint(); + let frame_rx = remote_shell::endpoint::spawn_frame_reader(stream.try_clone()?); + let mut endpoint = remote_shell::endpoint::build_controller_endpoint(); let hook_id = endpoint.allocate_hook_id(); - let shell_leaf_name = remote_shell::shell_leaf_name(); - let open_procedure = remote_shell::shell_open_procedure(); + let shell_leaf_name = remote_shell::endpoint::RemoteShellEndpoint::protocol_leaf_name(); + let open_procedure = remote_shell::endpoint::ProcedureOpen::protocol_procedure_id(); - remote_shell::send_forward( + remote_shell::endpoint::send_forward( &mut stream, endpoint.send_call( - remote_shell::agent_path(), + agent_path(), Some(shell_leaf_name), open_procedure.clone(), Some(hook_id), - remote_shell::shell_open_payload(), + encode_call_reply(&OpenRequest).expect("remote shell open payload should encode"), )?, )?; for (index, command) in ["pwd\n", "whoami\n", "exit\n"].iter().enumerate() { - remote_shell::send_forward( + remote_shell::endpoint::send_forward( &mut stream, endpoint.send_data( - remote_shell::agent_path(), + agent_path(), hook_id, open_procedure.clone(), command.as_bytes().to_vec(), @@ -48,7 +50,7 @@ fn main() -> Result<(), Box> { for result in frame_rx { let frame = result?; - let outcome = endpoint.receive(&Ingress::Child(remote_shell::agent_path()), frame)?; + let outcome = endpoint.receive(&Ingress::Child(agent_path()), frame)?; let EndpointOutcome::Local(event) = outcome else { continue; }; @@ -71,3 +73,7 @@ fn main() -> Result<(), Box> { Ok(()) } + +fn agent_path() -> Vec { + vec![String::from("agent")] +} diff --git a/examples/protocol/remote_shell_single_endpoint.rs b/examples/protocol/remote_shell_single_endpoint.rs index 78b8b8a..8f21a79 100644 --- a/examples/protocol/remote_shell_single_endpoint.rs +++ b/examples/protocol/remote_shell_single_endpoint.rs @@ -7,25 +7,25 @@ use std::error::Error; +use unshell::leaves::remote_shell; use unshell::protocol::tree::{EndpointOutcome, LocalEvent, ProtocolEndpoint}; use unshell::protocol::{INTROSPECTION_PROCEDURE_ID, LeafIntrospection}; -use unshell_leaves::remote_shell; fn main() -> Result<(), Box> { let mut endpoint = ProtocolEndpoint::new( - remote_shell::agent_path(), + agent_path(), Some(Vec::new()), Vec::new(), vec![unshell::protocol::tree::LeafSpec { - name: remote_shell::shell_leaf_name(), - procedures: vec![remote_shell::shell_open_procedure()], + name: remote_shell::endpoint::RemoteShellEndpoint::protocol_leaf_name(), + procedures: vec![remote_shell::endpoint::ProcedureOpen::protocol_procedure_id()], }], ); let hook_id = endpoint.allocate_hook_id(); let outcome = endpoint.send_call( - remote_shell::agent_path(), - Some(remote_shell::shell_leaf_name()), + agent_path(), + Some(remote_shell::endpoint::RemoteShellEndpoint::protocol_leaf_name()), INTROSPECTION_PROCEDURE_ID, Some(hook_id), Vec::new(), @@ -38,10 +38,14 @@ fn main() -> Result<(), Box> { let payload = unshell::protocol::tree::decode_call_input::(&message.data)?; println!( "remote-shell examples normally listen on {}", - remote_shell::LISTEN_ADDR + remote_shell::endpoint::LISTEN_ADDR ); - println!("endpoint path: {:?}", remote_shell::agent_path()); + println!("endpoint path: {:?}", agent_path()); println!("leaf: {}", payload.leaf_name); println!("procedures: {:?}", payload.procedures); Ok(()) } + +fn agent_path() -> Vec { + vec![String::from("agent")] +} diff --git a/src/lib.rs b/src/lib.rs index d327043..a47b5ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,9 @@ pub mod logger; /// proc-macro output and downstream code do not need a second migration. pub use unshell_protocol as protocol; +/// Re-export the leaf library crate behind the historical `unshell::leaves` path +pub use unshell_leaves as leaves; + pub use unshell_macros::{Leaf, Procedure, procedures}; // pub use ush_obfuscate as obfuscate; diff --git a/unshell-leaves/Cargo.toml b/unshell-leaves/Cargo.toml index 0d49e16..01491c1 100644 --- a/unshell-leaves/Cargo.toml +++ b/unshell-leaves/Cargo.toml @@ -6,13 +6,14 @@ description = "Application-layer UnShell leaves and client surfaces" [features] default = [] -endpoint = ["dep:portable-pty"] -tui = [] +leaf_endpoint = ["dep:portable-pty"] +leaf_tui = [] [dependencies] rkyv = { workspace = true } portable-pty = { workspace = true, optional = true } -unshell = { workspace = true } +unshell-macros = { path = "../unshell-macros" } +unshell-protocol = { workspace = true } [lints.rust] elided_lifetimes_in_paths = "warn" diff --git a/unshell-leaves/src/lib.rs b/unshell-leaves/src/lib.rs index bbfb091..2b463aa 100644 --- a/unshell-leaves/src/lib.rs +++ b/unshell-leaves/src/lib.rs @@ -2,10 +2,18 @@ //! protocol runtime. //! //! Each leaf module always exports its shared protocol-facing types. Role-specific -//! implementations are selected with the crate-wide `endpoint` and `tui` +//! implementations are selected with the crate-wide `leaf_endpoint` and `leaf_tui` //! features, and can optionally be re-exported behind one stable alias. -use unshell::protocol::DataMessage; +#[allow(unused_extern_crates)] +extern crate self as unshell; + +pub extern crate alloc; + +use unshell_protocol::DataMessage; + +pub use unshell_macros::{Leaf, Procedure, procedures}; +pub use unshell_protocol as protocol; /// Re-exports one role-specific type behind a stable public alias. /// @@ -20,18 +28,18 @@ macro_rules! role_leaf { tui => $tui:path $(,)? } ) => { - #[cfg(all(feature = "endpoint", feature = "tui"))] + #[cfg(all(feature = "leaf_endpoint", feature = "leaf_tui"))] compile_error!(concat!( "`", stringify!($alias), - "` can only alias one concrete role at a time; enable either `endpoint` or `tui`, not both" + "` can only alias one concrete role at a time; enable either `leaf_endpoint` or `leaf_tui`, not both" )); - #[cfg(feature = "endpoint")] + #[cfg(feature = "leaf_endpoint")] $(#[$meta])* $vis type $alias = $endpoint; - #[cfg(all(not(feature = "endpoint"), feature = "tui"))] + #[cfg(all(not(feature = "leaf_endpoint"), feature = "leaf_tui"))] $(#[$meta])* $vis type $alias = $tui; }; diff --git a/unshell-leaves/src/remote_shell/endpoint.rs b/unshell-leaves/src/remote_shell/endpoint.rs index 7fdaebe..b4c437b 100644 --- a/unshell-leaves/src/remote_shell/endpoint.rs +++ b/unshell-leaves/src/remote_shell/endpoint.rs @@ -15,7 +15,7 @@ pub use errors::ShellLeafError; pub use session::ProcedureOpen; pub use transport::{LISTEN_ADDR, send_forward, spawn_frame_reader, write_frames}; -use super::{OpenRequest, agent_path}; +use super::OpenRequest; /// Leaf state for the remote shell endpoint runtime. /// @@ -94,3 +94,7 @@ pub fn build_agent_runtime() -> ProcedureRuntime Vec { + vec![String::from("agent")] +} diff --git a/unshell-leaves/src/remote_shell/mod.rs b/unshell-leaves/src/remote_shell/mod.rs index 3aa1dd1..80ea83d 100644 --- a/unshell-leaves/src/remote_shell/mod.rs +++ b/unshell-leaves/src/remote_shell/mod.rs @@ -2,26 +2,21 @@ //! //! The module always exports the protocol contract for the leaf. Role-specific //! implementations live behind crate-wide features: -//! - `endpoint` builds the PTY-backed runtime leaf -//! - `tui` builds a placeholder client-side TUI surface +//! - `leaf_endpoint` builds the PTY-backed runtime leaf +//! - `leaf_tui` builds a placeholder client-side TUI surface use rkyv::{Archive, Deserialize, Serialize}; -#[cfg(feature = "endpoint")] -mod endpoint; -#[cfg(feature = "tui")] -mod tui; +#[cfg(feature = "leaf_endpoint")] +pub mod endpoint; +#[cfg(feature = "leaf_tui")] +pub mod tui; -#[cfg(feature = "endpoint")] -pub use endpoint::{ - LISTEN_ADDR, RemoteShellEndpoint, ShellLeafError, build_agent_runtime, - build_controller_endpoint, send_forward, spawn_frame_reader, write_frames, -}; -#[cfg(feature = "tui")] +#[cfg(feature = "leaf_endpoint")] +pub use endpoint::RemoteShellEndpoint; +#[cfg(feature = "leaf_tui")] pub use tui::RemoteShellTui; -use unshell::protocol::tree::encode_call_reply; - /// Open-request payload for the remote shell leaf. /// /// The shell currently needs no structured arguments, but a named payload type is @@ -36,57 +31,3 @@ crate::role_leaf! { tui => tui::RemoteShellTui, } } - -/// Returns the example endpoint path used by the remote shell samples. -pub fn agent_path() -> Vec { - path(&["agent"]) -} - -/// Returns the canonical leaf id used by endpoint and TUI code. -#[cfg(feature = "endpoint")] -pub fn shell_leaf_name() -> String { - RemoteShellEndpoint::protocol_leaf_name() -} - -/// Returns the canonical opening `procedure_id` for the shell leaf. -#[cfg(feature = "endpoint")] -pub fn shell_open_procedure() -> String { - endpoint::ProcedureOpen::protocol_procedure_id() -} - -/// Encodes the empty open-request payload used by the shell example. -#[cfg(all(not(feature = "endpoint"), feature = "tui"))] -pub fn shell_leaf_name() -> String { - RemoteShellTui::protocol_leaf_name() -} - -/// Returns the canonical opening `procedure_id` for the shell leaf. -#[cfg(all(not(feature = "endpoint"), feature = "tui"))] -pub fn shell_open_procedure() -> String { - let mut procedure_id = shell_leaf_name(); - procedure_id.push_str(".open"); - procedure_id -} - -/// Encodes the empty open-request payload used by the shell example. -#[cfg(not(any(feature = "endpoint", feature = "tui")))] -pub fn shell_leaf_name() -> String { - String::from("remote_shell") -} - -/// Returns the canonical opening `procedure_id` for the shell leaf. -#[cfg(not(any(feature = "endpoint", feature = "tui")))] -pub fn shell_open_procedure() -> String { - let mut procedure_id = shell_leaf_name(); - procedure_id.push_str(".open"); - procedure_id -} - -/// Encodes the empty open-request payload used by the shell example. -pub fn shell_open_payload() -> Vec { - encode_call_reply(&OpenRequest).expect("remote shell open payload should encode") -} - -fn path(parts: &[&str]) -> Vec { - parts.iter().map(|part| (*part).to_owned()).collect() -}