app
Adom USB
Public Unreviewedby John Lauer
See every USB device across the entire Adom ecosystem — laptop, WSL2, cloud container, Azure VM, workcells — in one dashboard, and mount / unmount / reset / test / force-class / sniff any of them. USB/IP slingshot for the whole mesh.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
//! CLI surface for driving the dashboard from a terminal / from the AI.
//! `list` works standalone; `mount`/`unmount` POST to a running `app` server so
//! the webview updates live (same endpoints the UI buttons fire).
use crate::devices;
use crate::{hint, note, ok, MountArgs, UnmountArgs};
use std::collections::HashMap;
pub fn run_list(demo: bool) -> Result<(), String> {
let snap = devices::snapshot(demo, &HashMap::new());
note!("{}", serde_json::to_string_pretty(&snap).unwrap_or_default());
Ok(())
}
fn post(port: u16, path: &str, body: serde_json::Value) -> Result<serde_json::Value, String> {
let url = format!("http://127.0.0.1:{}{}", port, path);
match ureq::post(&url).send_json(body) {
Ok(resp) => resp.into_json::<serde_json::Value>().map_err(|e| format!("parse response: {}", e)),
Err(ureq::Error::Status(code, resp)) => {
let v: serde_json::Value = resp.into_json().unwrap_or(serde_json::json!({}));
Err(format!("server returned {}: {}", code, v.get("error").and_then(|e| e.as_str()).unwrap_or("")))
}
Err(e) => Err(format!("could not reach the dashboard on port {} ({}). Start it with: adom-usb app", port, e)),
}
}
pub fn run_mount(a: MountArgs) -> Result<(), String> {
let resp = post(a.port, "/mount", serde_json::json!({"device_id": a.device_id, "target_id": a.target_id}))?;
ok(format!("mounted {} → {}", a.device_id, a.target_id));
if let Some(cmd) = resp.get("command").and_then(|v| v.as_str()) {
note!(" agent command: {}", cmd);
}
Ok(())
}
pub fn run_flash(a: crate::FlashArgs) -> Result<(), String> {
let resp = post(a.port, "/flash", serde_json::json!({"device_id": a.device_id, "persona": a.persona}))?;
ok(format!("flashing {} -> persona '{}' via {}", a.device_id, a.persona,
resp.get("plugin").and_then(|v| v.as_str()).unwrap_or("?")));
if let Some(cmd) = resp.get("command").and_then(|v| v.as_str()) { note!(" agent command: {}", cmd); }
hint("after flashing, the device re-enumerates as the persona's USB class — run `adom-usb list` to see it");
Ok(())
}
pub fn run_plugins(sub: crate::PluginsCmd) -> Result<(), String> {
match sub {
crate::PluginsCmd::List => {
let ps = crate::plugins::discover_installed();
if ps.is_empty() {
note!("no plugins installed. Search the wiki: adom-usb plugins search");
} else {
for p in &ps {
note!("{} v{} [{}] matches {}", p.slug, p.version, p.kind,
serde_json::to_string(&p.matches).unwrap_or_default());
}
}
ok(format!("{} plugin(s) installed", ps.len()));
}
crate::PluginsCmd::Search => {
let installed = crate::plugins::discover_installed();
let avail = crate::plugins::discover_registry(&installed);
if avail.is_empty() {
note!("no installable plugins found on the wiki (tag: {})", crate::plugins::PLUGIN_TAG);
} else {
for p in &avail { note!("{} {} — adompkg install {}", p.slug, p.description, p.slug); }
}
ok(format!("{} plugin(s) available to install", avail.len()));
}
}
Ok(())
}
pub fn run_health(port: u16) -> Result<(), String> {
let url = format!("http://127.0.0.1:{}/health", port);
match ureq::get(&url).call() {
Ok(resp) => {
let v: serde_json::Value = resp.into_json().unwrap_or(serde_json::json!({}));
ok(format!("dashboard healthy on port {} (devices: {})", port,
v.get("devices").and_then(|d| d.as_u64()).unwrap_or(0)));
Ok(())
}
Err(e) => Err(format!("no dashboard on port {} ({}). Start it with: adom-usb app", port, e)),
}
}
pub fn run_unmount(a: UnmountArgs) -> Result<(), String> {
let resp = post(a.port, "/unmount", serde_json::json!({"device_id": a.device_id}))?;
ok(format!("unmounted {}", a.device_id));
if let Some(cmd) = resp.get("command").and_then(|v| v.as_str()) {
note!(" agent command: {}", cmd);
}
hint("see `adom-usb list` or the dashboard for the updated mount state");
Ok(())
}