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.
← Commit history
0.2.22 source: script-error net covers the Continue-execution X2 box
7 files changed
+35−6
bridge-rs/Cargo.lock+1−1@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "altium-bridge"-version = "0.2.21"+version = "0.2.22" dependencies = [ "getrandom", "serde",
bridge-rs/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "altium-bridge"-version = "0.2.21"+version = "0.2.22" edition = "2021" description = "Adom altium-bridge — the bridge process, in Rust. Calls ab's verbs for the desktop; speaks file IPC to the resident DelphiScript agent inside Altium." license = "MIT"
bridge-rs/src/agent.rs+24@@ -196,6 +196,30 @@ pub fn call_in(ws: &std::path::Path, command: &str, params: &[(&str, String)], p // not leave Altium parked in its debugger: stop the script now and say what happened. if t0.elapsed() >= next_halt_check { next_halt_check = t0.elapsed() + Duration::from_millis(400);+ // Variant 2 (script run from INSIDE the agent, e.g. eval): Altium asks "Undeclared+ // identifier ... Continue execution?" in an 'X2' box with OK + Cancel. It is OURS (it+ // only exists while our command runs), the reaper rightly never touches a two-button+ // box, and the agent loop is blocked behind it. OK = continue: the loop survives.+ for d in crate::dialogs::scan() {+ let title = d.get("title").and_then(|t| t.as_str()).unwrap_or("").trim().to_string();+ let labels: Vec<String> = d.get("buttons").and_then(|b| b.as_array())+ .map(|a| a.iter().filter_map(|x| x.as_str()).map(|s| s.trim().to_lowercase()).collect()).unwrap_or_default();+ let hwnd = d.get("hwnd").and_then(|h| h.as_i64());+ if title != "X2" || labels != ["ok", "cancel"] || hwnd.is_none() { continue; }+ let hwnd = hwnd.unwrap();+ let shot = crate::ab::call("desktop_screenshot_base64", json!({"hwnd": hwnd}), &[], 10);+ let img = shot.get("image").or_else(|| shot.get("base64")).cloned().unwrap_or(Value::Null);+ let pressed = crate::dialogs::press(hwnd, "OK").get("success").and_then(|b| b.as_bool()).unwrap_or(false);+ crate::dialogs::note_event(json!({"title": "X2", "category": "script_error_continue", "pressed": "OK", "ok": pressed,+ "note": format!("script runtime error inside agent command '{command}': photographed, answered OK (continue) so the agent loop survives")}));+ let settle = Instant::now();+ while settle.elapsed() < Duration::from_secs(3) && !resp.exists() { std::thread::sleep(Duration::from_millis(20)); }+ let _ = std::fs::remove_file(&resp);+ return json!({"success": false, "errorCode": "agent_script_error", "command": command,+ "error": format!("the script run by '{command}' raised a runtime error (Altium asked 'Continue execution?'; its text is in errorShot)"),+ "errorShot": img, "answered": "OK", "answeredOk": pressed, "agentAlive": is_alive(),+ "_hint": "READ errorShot (PNG of Altium's box, e.g. 'Undeclared identifier: X at Line:N'). The bridge answered OK so the agent loop is still resident; fix the script text and send it again."});+ } if let Some(ev) = crate::dialogs::script_error_since(posted_unix_ms) { let recovered = crate::lifecycle::recover(&format!("auto: script Error box during agent command '{command}'")); return json!({"success": false, "errorCode": "agent_script_error", "command": command,
bridge-rs/src/main.rs+1−1@@ -25,7 +25,7 @@ mod win32; use std::time::Instant; use tiny_http::{Header, Method, Response, Server}; -pub const BRIDGE_VERSION: &str = "0.2.21";+pub const BRIDGE_VERSION: &str = "0.2.22"; fn json_response(status: u16, body: &serde_json::Value) -> Response<std::io::Cursor<Vec<u8>>> { let data = serde_json::to_vec(body).unwrap_or_else(|_| b"{}".to_vec());
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "altium", "displayName": "Altium Designer",- "version": "0.2.21",+ "version": "0.2.22", "description": "Drive Altium Designer from Adom: a resident DelphiScript agent (eval + the 1,100-command process launcher), the X2 -R command line, UIA over the VCL shell, dialogs by PID, screenshots of every Altium window, and Altium 365.", "homepage": "https://wiki.adom.inc/adom/altium-bridge", "docs": "https://wiki.adom.inc/adom/altium-bridge",
dev-skills/altium-bridge-dev/SKILL.md+6−1@@ -85,7 +85,12 @@ when a script is stopped on a breakpoint"). Also: `ISch_Component.Implementation `.Implementation[i]` do not exist on AD 26.9.1 (walk models with a `SchIterator_Create` + `AddFilter_ObjectSet(MkSet(eImplementation))`). Every identifier in this section that a regex can recognise is in `scripts/release.py` `ENGINE_HALTING`, and a build that contains one is refused:-add to that list whenever you find another. Test new agent commands on a SECOND Altium build+add to that list whenever you find another. From 0.2.22 the same net covers the second shape: a script run from INSIDE the agent (`eval`) raises an+`X2` box "Undeclared identifier ... Continue execution?" with OK + Cancel; `agent::call` photographs it,+answers OK (the loop survives) and returns the same `agent_script_error`. To prove the net after any change+to `agent.rs`/`dialogs.rs`: `altium_eval {text:"Begin NoSuchProcAdomTest(1); End;"}` must come back in+seconds with `errorShot`, and `altium_status` must then read `ready: true` with no dialogs.+Test new agent commands on a SECOND Altium build before promoting; one machine proved nothing about 26.9.1. ## The brace rule for JSON-building code
page.json+1−1@@ -140,4 +140,4 @@ "skills/altium-wiki-import/SKILL.md", "uninstall.sh" ]-}+}