1pub mod api;
23pub mod bus;
24pub mod exec;
25pub mod fsops;
26pub mod jobs;
27pub mod osops;
28pub mod peer;
29#[cfg(feature = "sysinfo-caps")]
30pub mod procs;
31pub mod proto;
32#[cfg(feature = "pty")]
33pub mod pty;
34pub mod session;
35pub mod store;
36#[cfg(feature = "sysinfo-caps")]
37pub mod sysmon;
38pub mod transport;
39pub mod watch;
40
41pub use proto::{Framing, Out, Peer};
62pub use session::{caps, Session};
63
64use std::io::Read;
65use std::path::PathBuf;
66
67pub const VERSION: &str = env!("CARGO_PKG_VERSION");
69
70pub fn default_socket() -> PathBuf {
78 if let Ok(p) = std::env::var("ZWIRE_HOST_SOCK") {
79 return PathBuf::from(p);
80 }
81 #[cfg(windows)]
82 {
83 let user = std::env::var("USERNAME").unwrap_or_else(|_| "user".into());
84 let safe: String = user
85 .chars()
86 .filter(|c| c.is_ascii_alphanumeric() || matches!(c, '-' | '_'))
87 .collect();
88 let name = if safe.is_empty() { "user".into() } else { safe };
89 PathBuf::from(format!("zwire-host-{name}"))
90 }
91 #[cfg(not(windows))]
92 {
93 if let Ok(rt) = std::env::var("XDG_RUNTIME_DIR") {
94 if !rt.is_empty() {
95 return PathBuf::from(rt).join("zwire-host.sock");
96 }
97 }
98 store::app_dir("zwire").join("host.sock")
99 }
100}
101
102const BANNER: &str = concat!(
104 "\x1b[36m███████╗██╗ ██╗██╗██████╗ ███████╗ ██╗ ██╗ ██████╗ ███████╗████████╗\x1b[0m\n",
105 "\x1b[36m╚══███╔╝██║ ██║██║██╔══██╗██╔════╝ ██║ ██║██╔═══██╗██╔════╝╚══██╔══╝\x1b[0m\n",
106 "\x1b[35m ███╔╝ ██║ █╗ ██║██║██████╔╝█████╗█████╗███████║██║ ██║███████╗ ██║ \x1b[0m\n",
107 "\x1b[35m ███╔╝ ██║███╗██║██║██╔══██╗██╔══╝╚════╝██╔══██║██║ ██║╚════██║ ██║ \x1b[0m\n",
108 "\x1b[31m███████╗╚███╔███╔╝██║██║ ██║███████╗ ██║ ██║╚██████╔╝███████║ ██║ \x1b[0m\n",
109 "\x1b[31m╚══════╝ ╚══╝╚══╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝ \x1b[0m\n",
110);
111
112const HELP_BODY: &str = " \x1b[35m>> UNIVERSAL LOCAL HOST // FULL SPECTRUM <<\x1b[0m\n\n universal local host — system stats · fs · exec · pty · kv · os\n\n\x1b[33m USAGE:\x1b[0m zwire-host [MODE] [OPTIONS]\n\n\x1b[36m ── MODES ─────────────────────────────────────────────────────\x1b[0m\n zwire-host \x1b[32m//\x1b[0m native-messaging on stdio (Chrome default)\n zwire-host serve \x1b[32m//\x1b[0m run the NDJSON socket daemon\n zwire-host call '<json>' \x1b[32m//\x1b[0m send one request to the daemon, print reply\n zwire-host call \x1b[32m//\x1b[0m ...reading the request JSON from stdin\n zwire-host call --stream '...' \x1b[32m//\x1b[0m keep printing frames (sysinfo/pty streams)\n zwire-host version | help \x1b[32m//\x1b[0m print version / this help\n\n\x1b[36m ── OPTIONS ───────────────────────────────────────────────────\x1b[0m\n -s, --socket <path> \x1b[32m//\x1b[0m socket ($ZWIRE_HOST_SOCK / $XDG_RUNTIME_DIR / ~/.zwire)\n -f, --stream \x1b[32m//\x1b[0m relay every reply frame instead of just the first\n --tcp <addr> \x1b[32m//\x1b[0m (serve) also listen for peers/remote clients on TCP\n --token <tok> \x1b[32m//\x1b[0m (serve) shared secret required of inbound TCP\n --name <name> \x1b[32m//\x1b[0m (serve) advertised peer name (default: hostname)\n --peer <addr> \x1b[32m//\x1b[0m (serve) dial a peer and keep it linked; repeatable\n -h, --help \x1b[32m//\x1b[0m print this help\n -V, --version \x1b[32m//\x1b[0m print version\n\n\x1b[36m ── EXAMPLES ──────────────────────────────────────────────────\x1b[0m\n zwire-host serve &\n zwire-host call '{\"cmd\":\"hostinfo\"}'\n zwire-host call '{\"cmd\":\"fs_walk\",\"path\":\"~/src\",\"ext\":\"rs\"}'\n echo '{\"cmd\":\"exec\",\"program\":\"git\",\"args\":[\"status\"]}' | zwire-host call\n zwire-host call --stream '{\"cmd\":\"sysinfo_start\"}'\n zwire-host serve --tcp 0.0.0.0:7420 --token SECRET --peer other.local:7420\n";
115
116fn usage() -> String {
120 const BOX_W: usize = 72;
121 let status = format!(" STATUS: ONLINE // SIGNAL: ████████░░ // v{VERSION}");
122 let space = " ".repeat(BOX_W.saturating_sub(status.chars().count()));
123 let rule = "─".repeat(BOX_W);
124 format!(
125 "\n{BANNER} \x1b[36m┌{rule}┐\x1b[0m\n \x1b[36m│\x1b[0m{status}{space}\x1b[36m│\x1b[0m\n \x1b[36m└{rule}┘\x1b[0m\n{HELP_BODY}\n\x1b[36m ── SYSTEM ────────────────────────────────────────────────────\x1b[0m\n \x1b[35mv{VERSION} \x1b[0m// \x1b[33m(c) Jacob Menke and contributors\x1b[0m\n \x1b[35mOne pipe. One binary. The whole machine.\x1b[0m\n \x1b[33m>>> JACK IN. ONE SOCKET. OWN YOUR MACHINE. <<<\x1b[0m\n \x1b[36m░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░\x1b[0m\n"
126 )
127}
128
129pub fn run(args: Vec<String>) {
134 let mut socket = default_socket();
136 let mut follow = false;
137 let mut tcp: Option<String> = None;
138 let mut token: Option<String> = None;
139 let mut name: Option<String> = None;
140 let mut peers: Vec<String> = Vec::new();
141 let mut positional: Vec<String> = Vec::new();
142 let mut it = args.into_iter();
143 while let Some(a) = it.next() {
144 match a.as_str() {
145 "--socket" | "-s" => {
146 if let Some(p) = it.next() {
147 socket = PathBuf::from(p);
148 }
149 }
150 "--stream" | "--follow" | "-f" => follow = true,
151 "--tcp" => tcp = it.next(),
152 "--token" => token = it.next(),
153 "--name" => name = it.next(),
154 "--peer" => {
155 if let Some(p) = it.next() {
156 peers.push(p);
157 }
158 }
159 _ => positional.push(a),
160 }
161 }
162
163 match positional.first().map(String::as_str) {
164 Some("serve") => transport::serve(transport::ServeConfig {
165 socket,
166 tcp,
167 token,
168 name,
169 peers,
170 }),
171 Some("call") => {
172 let rest = positional[1..].join(" ");
173 let request = if rest.trim().is_empty() {
174 let mut s = String::new();
175 let _ = std::io::stdin().read_to_string(&mut s);
176 s
177 } else {
178 rest
179 };
180 transport::call(&socket, &request, follow);
181 }
182 Some("version") | Some("--version") | Some("-V") => {
183 println!("zwire-host {VERSION}");
184 }
185 Some("help") | Some("--help") | Some("-h") => {
186 print!("{}", usage());
187 }
188 _ => transport::stdio(),
190 }
191}