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.37: launch hands the user's foreground window back; battery green on both Altium builds (113 pass, 0 fail)
8 files changed
+32−11
CHANGELOG.md+1−1@@ -2,7 +2,7 @@ ## 0.2.37 (2026-09-21) - **`altium_launch` hands the user's window back.** Altium foregrounds itself at startup and offers no switch for it, so the bridge now remembers the window the user was working in and restores it once Altium has settled (`foregroundReturnedTo`, `windowEtiquette: background`). `background:false` opts out when the user asked to watch Altium come up. John, twice in two days: "dude u keep forcing altium in front of me while i'm typing".-- Full battery on Altium Designer 26.10.1: 122 verbs, **113 pass, 0 fail**, 9 skipped for safety — `close` included, for the first time.+- Full battery GREEN ON BOTH BUILDS: 122 verbs, **113 pass, 0 fail**, 9 skipped for safety, on Altium Designer 26.10.1 (arav-rog) and Altium Designer Develop (ConfRoomROG) — `close` included, for the first time. Both machines were left with no Altium running and no dialog on screen. ## 0.2.36 (2026-09-21) Fixes the last failing verb in the battery, `close`, and with it an overnight hostage situation: an Altium on the test box held the single licence seat for eight hours because nothing could reach it.
bridge-rs/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "altium-bridge"-version = "0.2.36"+version = "0.2.37" 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/lifecycle.rs+5@@ -745,6 +745,11 @@ fn recover_impl(reason: &str, verify_by_restart: bool) -> Value { // ---- application lifecycle --------------------------------------------------- +/// The window the user is actually working in, for verbs that must hand it back (`misc::launch`).+pub fn foreground_hwnd_pub() -> Option<i64> { foreground_hwnd() }+/// Hand a remembered foreground window back to the user.+pub fn restore_foreground_pub(hwnd: i64) { restore_foreground(hwnd) }+ pub fn launch(wait: Duration) -> Value { if crate::health::is_running() { return json!({"success": true, "alreadyRunning": true, "windowEtiquette": "background"});
bridge-rs/src/main.rs+1−1@@ -29,7 +29,7 @@ use tiny_http::{Header, Method, Response, Server}; /// Attached to every failed reply (see the /command handler). const REPORT_ISSUES: &str = "If this looks like an altium-bridge BUG (not a refusal you expected), report it: check for a duplicate with `adom-wiki issue list adom/altium-bridge`, then `adom-wiki issue create adom/altium-bridge --title \"<verb>: <one-line symptom>\" --category bug-report --body \"<what you ran, what you expected, what happened, the VERBATIM response JSON (and errorShot if present), bridgeVersion, the Altium version from altium_status, the OS>\"`. A relay, routing, toast or lifecycle problem belongs on adom/adom-bridge instead. Reports from real machines are how this bridge gets correct: three different Altium builds behaved three different ways in one weekend."; -pub const BRIDGE_VERSION: &str = "0.2.36";+pub const BRIDGE_VERSION: &str = "0.2.37"; 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-rs/src/misc.rs+18−2@@ -200,6 +200,12 @@ pub fn launch(args: &Value) -> Value { } let t0 = support::now(); progress::begin("cold_start", "cold", "launching Altium Designer", "bridge");+ // John, 2026-09-20/21: "dude u keep forcing altium in front of me [while] i'm typing".+ // Altium's own launch foregrounds itself and there is no switch for it, so remember the+ // window the user was actually in and hand it straight back once Altium is up.+ // `background:false` opts out when the launch IS the thing the user asked to see.+ let hand_back = args.get("background").and_then(|b| b.as_bool()).unwrap_or(true);+ let user_fg = if hand_back { lifecycle::foreground_hwnd_pub() } else { None }; let exe = inst.get("exe").and_then(|e| e.as_str()).unwrap_or("").to_string(); if let Err(e) = lifecycle::spawn_helper_pub(&exe, "") { progress::end("cold_start", "bridge");@@ -212,9 +218,19 @@ pub fn launch(args: &Value) -> Value { if lic.get("running").and_then(|b| b.as_bool()) == Some(true) { progress::record("cold_start", support::now() - t0, "cold"); progress::end("cold_start", "bridge");+ // Altium keeps painting for a moment after its main window exists; hand the focus+ // back once it has settled, or it simply takes it again.+ let mut returned = Value::Null;+ if let Some(fg) = user_fg {+ std::thread::sleep(Duration::from_secs(3));+ lifecycle::restore_foreground_pub(fg);+ returned = json!(fg);+ } return json!({"success": true, "startedInSec": support::elapsed_round(t0), "licence": lic,- "windowEtiquette": "foreground",- "_hint": "Altium's own launch foregrounds itself (the one legitimate foreground). Next: altium_readiness; if unlicensed, altium_license."});+ "windowEtiquette": if returned.is_null() { "foreground" } else { "background" },+ "foregroundReturnedTo": returned,+ "_hint": if returned.is_null() { "Altium's own launch foregrounds itself. Next: altium_readiness; if unlicensed, altium_license." }+ else { "Altium is up and the user's own window was handed back (it launches in front of everything and cannot be told not to). Pass background:false if the user asked to SEE Altium come up. Next: altium_readiness; if unlicensed, altium_license." }}); } std::thread::sleep(Duration::from_secs(1)); }
bridge-rs/src/verbs.rs+4−4@@ -219,12 +219,12 @@ fn describe() -> Value { "hwnd is NOT read by this verb, although other verbs' hints say {hwnd, button}: target a dialog with titles.", "success is true even when nothing was pressed or the click failed: check pressed[].ok."], json!({"button": "OK", "titles": ["Error"]})),- verb_doc("launch", "Start Altium (newest install) and wait for its main window. Answers alreadyRunning when one is up.",- json!({"waitSec": "number, default 180"}),- json!({"alreadyRunning": "bool", "startedInSec": "float", "licence": "{running, licensed, state, adVersion, edition, hwnd, pid}", "windowEtiquette": "foreground on a real launch, background when already running"}), 240,+ verb_doc("launch", "Start Altium (newest install) and wait for its main window, then hand the user's own window back. Answers alreadyRunning when one is up.",+ json!({"waitSec": "number, default 180", "background": "bool, default TRUE: remember the user's foreground window and restore it once Altium is up. Pass false only when the user asked to SEE Altium come up."}),+ json!({"alreadyRunning": "bool", "startedInSec": "float", "licence": "{running, licensed, state, adVersion, edition, hwnd, pid}", "foregroundReturnedTo": "hwnd|null", "windowEtiquette": "background once the user's window is handed back; foreground only with background:false"}), 240, "Cold start is about 60 s. Next: altium_readiness; licensed:false -> altium_ensure_licensed. Most verbs need this first (errorCode altium_not_running).", &["readiness", "ensure_licensed", "close", "install"],- &["A real launch RAISES Altium over the user's work (Altium foregrounds itself at startup). Call it once; never in a retry loop.",+ &["Altium foregrounds ITSELF at startup and offers no switch for it, so the window jumps over the user's work for a few seconds even with background:true — the bridge can only hand the focus back afterwards. Call it once; never in a retry loop.", "errorCode host_app_not_installed -> altium_install. launch_timeout = no main window after waitSec.", "A 'Workspace Unavailable' box at launch (altium_dialog_log) means another running Altium holds the seat: this one is view-only and opens fail with view_only_no_author_seat."], json!({})),
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "altium", "displayName": "Altium Designer",- "version": "0.2.36",+ "version": "0.2.37", "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",
page.json+1−1@@ -140,4 +140,4 @@ "skills/altium-wiki-import/SKILL.md", "uninstall.sh" ]-}+}