app
Altium - the Altium Bridge
Public Made by Adomby adom
Adom Bridge for Altium Designer: a resident DelphiScript agent gives the AI eval, the 1,100-command process launcher, all 8 EDA surfaces with screenshots, compile/ERC, DRC, OutJob outputs, native SchLib/PcbLib authoring with STEP 3D bodies, and dialog handling by PID.
main
John Lauer
bridge-rs: Rust bridge vertical slice (health via ab, agent file-IPC, Phase 1 verbs) — verified live on AdomLapper; doc…
0be1503
1mo ago
//! Loopback client for Adom Bridge's direct API — the bridge calls ab for everything
//! desktop-shaped (windows, dialogs, screenshots, UIA, shell) instead of re-implementing Win32.
use serde_json::{json, Value};
pub fn call(command: &str, args: Value, forwarded: &[(String, String)], timeout_s: u64) -> Value {
let base = std::env::var("ADOM_DIRECT_API_URL").unwrap_or_else(|_| "http://127.0.0.1:24680".into());
let url = format!("{}/command", base.trim_end_matches('/'));
let mut args = args;
if forwarded.is_empty() {
if let Some(o) = args.as_object_mut() {
o.entry("caller").or_insert(json!({"aiThread": "altium-bridge", "source": "bridge"}));
}
}
let mut req = ureq::post(&url)
.timeout(std::time::Duration::from_secs(timeout_s))
.set("Content-Type", "application/json");
if let Ok(tok) = std::env::var("ADOM_BRIDGE_TOKEN") {
req = req.set("X-Adom-Bridge-Token", &tok);
}
for (k, v) in forwarded {
req = req.set(k, v);
}
req = req.set("X-Adom-Caller-Delegate", "altium");
match req.send_json(json!({"command": command, "args": args})) {
Ok(r) => unwrap_output(r.into_json::<Value>().unwrap_or_else(|e| json!({"success": false, "error": format!("ab reply not JSON: {e}")}))),
Err(ureq::Error::Status(code, r)) => {
let body = r.into_string().unwrap_or_default();
json!({"success": false, "error": format!("ab HTTP {code}: {}", body.chars().take(200).collect::<String>()), "errorCode": "ab_callback_failed"})
}
Err(e) => json!({"success": false, "error": format!("ab unreachable: {e}"), "errorCode": "ab_unreachable"}),
}
}
/// ab's direct API returns {error, output, status, success} with the verb's real result JSON-encoded
/// as a STRING in `output`. Surface the inner object, keeping the outer status fields.
fn unwrap_output(v: Value) -> Value {
let inner = v.get("output").and_then(|o| o.as_str()).and_then(|s| serde_json::from_str::<Value>(s).ok());
match inner {
Some(Value::Object(mut m)) => {
if let Some(o) = v.as_object() {
for k in ["success", "status", "error", "errorCode"] {
if let Some(x) = o.get(k) { m.entry(k.to_string()).or_insert(x.clone()); }
}
}
Value::Object(m)
}
_ => v,
}
}