app
adom-desktop-altium-bridge
Public Unreviewedby barrett-land
Adom Desktop bridge for Altium Designer — build and edit file-based symbol/footprint/Integrated libraries with LCSC/JLCPCB part numbers attached, and generate batch outputs (STEP, PDF, Gerber, BOM, CPL) from an OutJob, by driving Altium via DelphiScript.
← Commit history
Publish 0.0.42
18 files changed
+394−25
README.md+25−1SKILL.md+120−2altium-bridge-manifest.json+3−3altium-bridge-v0.0.42.zipbridge-src/BRIDGE_VERSION+1−1bridge-src/__pycache__/check_wiring.cpython-312.pycbridge-src/__pycache__/server.cpython-312.pycbridge-src/bridge.json+3−2bridge-src/check_wiring.py+2−1bridge-src/extension/AdomBridge/AdomBridge.dllbridge-src/extension/AdomBridge/AdomBridge.ins+21bridge-src/extension/AdomBridge/AdomBridge.rcs+15bridge-src/extension/AdomBridge/Help/ServerDlls.txt+1bridge-src/extension/Install-AdomBridge.ps1+100bridge-src/extension/REMOVE-AdomBridge.cmd+38bridge-src/server.py+47package.json+8−7page.json+10−8README.md+25−1@@ -11,7 +11,7 @@ verbs. ```bash adom-wiki pkg install barrett-land/adom-desktop-altium-bridge # the skill, in the container-adom-desktop bridge_install '{"manifestUrl":"https://wiki.adom.inc/download/barrett-land/adom-desktop-altium-bridge/0.0.41/altium-bridge-manifest.json"}'+adom-desktop bridge_install '{"manifestUrl":"https://wiki.adom.inc/download/barrett-land/adom-desktop-altium-bridge/0.0.42/altium-bridge-manifest.json"}' adom-desktop --ai-thread "<your thread>" altium_ping '{}' ``` @@ -26,6 +26,30 @@ adom-desktop --ai-thread "<your thread>" altium_ping '{}' | Edit | `altium_add_component` `altium_set_visibility` `altium_rename_component` `altium_rename_footprint` `altium_remove_component` | | Package | `altium_compile_intlib` `altium_extract_intlib` `altium_cleanup_libs` `altium_close_doc` | | Batch output | `altium_export_step` `altium_run_outjob` |+| Extension | `altium_install_extension` (installs the in-process .NET server) |++## Headless STEP export (via the extension)++Altium registers no parameterized processes, so from outside you only get parameterless,+dialog-driven commands. This package ships a small in-process .NET extension that fixes that:++```bash+adom-desktop --ai-thread "<thread>" altium_install_extension '{}' # Altium must be CLOSED+# start Altium, then:+X2.exe -RAdomBridge:ExportStep(Req=C:\Users\Public\AdomAltium\exportstep_req.txt)+```++The request file carries `PcbDoc`, `Out`, and options — `Holes=All` for **real drilled geometry**+(mounting holes become actual holes, not pad artwork), `Copper=All` to bake 3D copper into the+STEP, `Models=Step|Extruded|Both`.++🔴 Altium's `-R` parser **splits on whitespace**, so a path with a space is truncated *and* the+leftover tokens are opened as files. Never put data on the command line — that is why parameters+go in a request file at a space-free path.++⚠️ Installing or updating the extension requires Altium **closed** (it holds the DLL open), and a+failed load takes Altium down at startup. Put `extension/REMOVE-AdomBridge.cmd` on the Desktop+first: it is a self-contained one-click rollback. ## What it can't
SKILL.md+120−2@@ -12,7 +12,7 @@ forwards work into the running GUI as DelphiScript via `X2.EXE`. ## Install ```bash-adom-desktop bridge_install '{"manifestUrl":"https://wiki.adom.inc/download/barrett-land/adom-desktop-altium-bridge/0.0.41/altium-bridge-manifest.json"}'+adom-desktop bridge_install '{"manifestUrl":"https://wiki.adom.inc/download/barrett-land/adom-desktop-altium-bridge/0.0.42/altium-bridge-manifest.json"}' adom-desktop --ai-thread "<your thread>" altium_ping '{}' # -> {"altiumInstalled": true, "version": "..."} ``` @@ -94,6 +94,12 @@ adom-desktop --ai-thread "<thread>" altium_run_outjob '{ | **Gerber** (`.GTL` copper, `.GTO` silk, `.GTS` mask, `.GM1` outline) | ✅ yes | | **ExportSTEP** | ❌ no — `GenerateReport` no-ops; a human must click *Generate content* once | +> **For STEP, do not use an OutJob at all — use `AdomBridge:ExportStep`** (see the extension+> section below). It is fully headless, and exposes hole/copper options the OutJob route never had.++Also note an OutJob only generates if it is a **member of the project**; a stray `.OutJob` dropped+in the folder reports `Files Generated: 0`.+ The STEP exception is not a misconfiguration. It was verified with `ObjectKind=OutputBatch/OutputMedium/Document`, with and without `OutputMedium`, and with the OutJob already a member of a compiled project. Ask the user to click the button rather than@@ -109,7 +115,9 @@ rebuilding an OutJob that is already correct. - The real one, `PCB:Export`, is index-based (`Group=Model`) and opens a file dialog. `run: true` now demands an explicit `proc` (a probe hatch only) and fails loudly when no file-appears. **To actually get a STEP, use an ExportSTEP OutJob plus one human click.**+appears. **To get a STEP, use `AdomBridge:ExportStep` from the extension** — headless, parameterized+and strictly better than the OutJob route. The OutJob-plus-click path remains as a fallback if the+extension is not installed. ### The STEP has no copper and no silkscreen @@ -123,6 +131,116 @@ it alone is a bare green board. Rebuild both from Gerbers: Composite them into one RGBA overlay and pass it to `step2glb --silk-top` (which only takes effect with `--molecule`). The full recipe lives in the **altium-export-molecule** skill. +Alternatively, `AdomBridge:ExportStep` with `Copper=All` bakes real 3D copper into the STEP so no+overlay is needed — at a large size cost (1.9 MB → 57.7 MB on a mid-size board).++## The AdomBridge extension — parameterized, non-interactive verbs++Altium registers **no parameterized processes** for the things automation actually needs. From+outside the process you get parameterless, dialog-driven commands, and that single fact is why+STEP export, OutJob→PDF and KiCad import were all blocked. Code running **inside** Altium can call+the SDK directly, so this package ships a small .NET extension that registers processes which+take arguments.++### Install++```bash+# Altium MUST be closed first (see below).+adom-desktop --ai-thread "<thread>" altium_install_extension '{}'+# then start Altium — extensions are only read at startup+```++`altium_install_extension '{"remove": true}'` uninstalls. Both are idempotent, need no elevation+(the Extensions folder and `ExtensionsRegistry.xml` carry an inherited `Everyone: Modify` ACE), and+back up the registry with a timestamp before touching it.++⚠️ **Altium must be closed to install or update.** Once any AdomBridge process has run, Altium+holds `AdomBridge.dll` open and the copy fails with *"Access to the path is denied"*. The verb+detects this and says so rather than half-applying.++⚠️ **A failed extension load takes Altium down at startup**, not just the extension — the blast+radius is the whole application. `extension/REMOVE-AdomBridge.cmd` is a self-contained+double-click rollback; put it on the user's Desktop **before** installing so recovery never depends+on the agent being reachable.++### How dispatch works++```bash+"C:\Program Files\Altium\AD26\X2.exe" -RAdomBridge:<Process>(Key=Value|Key2=Value2)+```++Altium forwards this to the already-running instance and **loads the server lazily**, on first use+— so an absent load log right after startup is normal, not a failure.++🔴 **Altium's `-R` parser splits on WHITESPACE.** A path containing a space is truncated at the+first space *and* the remaining tokens are treated as files to open, at which point Altium tries to+start a second instance and Windows returns `ERROR_INVALID_STATE (0x8007139F)`. **Never put data on+the command line.** Write a request file at a space-free path and pass only its path.++### `AdomBridge:ExportStep` — headless STEP export++This replaces the OutJob-plus-human-click route entirely.++```bash+# 1. request file at a SPACE-FREE path; spaces are safe INSIDE it+cat > C:\Users\Public\AdomAltium\exportstep_req.txt <<'REQ'+PcbDoc=C:\...\SL1 Xilinx Spartan-IIE PQ208 Rev1.02.PcbDoc+Out=C:\...\board.step+Holes=All+PadHoles=true+Copper=None+Models=Step+REQ++# 2. dispatch+X2.exe -RAdomBridge:ExportStep(Req=C:\Users\Public\AdomAltium\exportstep_req.txt)++# 3. result lands in C:\Users\Public\AdomAltium\adombridge_exportstep.txt+# (reports exists= and bytes= from the filesystem, not from the call)+```++| Key | Values | Default | Notes |+|---|---|---|---|+| `PcbDoc` | path | — | required |+| `Out` | path | — | required, `.step` |+| `Holes` | `All` `Selected` `None` | `All` | **real drilled geometry** |+| `PadHoles` | `true` `false` | `true` | mechanical + electrical + free pad holes |+| `Copper` | `None` `Only` `All` `Layer` | `None` | bakes copper in as 3D solids |+| `Models` | `Step` `Extruded` `Both` | `Step` | component body source |+| `Folded` | `true` `false` | `false` | flex/folded board |++**Holes matter.** Without them the board is a solid slab and mounting holes exist only as pad+artwork. Measured on SpiritLevel-SL1: 1,657,922 B / 3,599 meshes without → **1,874,312 B / 3,727+meshes with**, and the holes are visibly through the board.++**Copper is expensive.** `Copper=All` bakes every trace and pour as a solid: the same board goes+from 1.9 MB to **57.7 MB**. Use it when you want true 3D copper instead of a painted overlay;+otherwise leave it `None` and composite the pads from Gerbers.++The export opens the board with `ShowDocumentDontFocus` and resolves it via `GetPCBBoardByPath`, so+it neither steals the user's screen nor depends on which document happens to be focused.++### `AdomBridge:Ping` — connectivity++`-RAdomBridge:Ping(Msg=hello)` writes `adombridge_ping.txt`. Use it to prove the extension loaded+and that `-R` dispatch reaches it.++### Extending it++Source: `bridge-src/extension/` (prebuilt DLL) and the `adom-altium-extension` project.+Cross-built on Linux against the in-box .NET 8 Altium assemblies, because the design machine has no+.NET SDK. Rules learned the hard way:++- **Never let an exception escape `InvokePluginFactory`** — it is unhandled inside X2.EXE startup+ and kills the application. Wrap the whole body; return `null` to degrade to "extension absent".+- Do **not** set `EnableDynamicLoading`; it emits `.deps.json`/`.runtimeconfig.json` the assembly+ then requires. Altium loads managed servers into its own CoreCLR and ships neither file.+- The factory is COM-activated: keep `[ComVisible(true)]` and a **valid hex** `[Guid]`.+- Entry point is `CSharpPlugin.PluginFactory.InvokePluginFactory(IClient)`; the server derives from+ `DXP.ServerModule` and registers via the concrete `DXP.CommandLauncher.RegisterCommand`.++---+ ## Developing the bridge Source lives in the page repo under `bridge-src/`. Three files each carry their own list of what
altium-bridge-manifest.json+3−3@@ -1,3 +1,3 @@-{ "manifest_version": 1, "name": "altium", "version": "0.0.41",- "url": "https://wiki.adom.inc/download/barrett-land/adom-desktop-altium-bridge/0.0.41/altium-bridge-v0.0.41.zip",- "sha256": "ce614444afefcdffa56f4d03e57ff4fdd4bd13d2f8b59859477f2b192db9c92f", "size": 26776, "released_at": "2026-08-05T22:16:21Z" }+{ "manifest_version": 1, "name": "altium", "version": "0.0.42",+ "url": "https://wiki.adom.inc/download/barrett-land/adom-desktop-altium-bridge/0.0.42/altium-bridge-v0.0.42.zip",+ "sha256": "cba583cb0e56b776769b3f0c21812fc62b3d3e974a17cc223a1fe2d22036d7b2", "size": 37658, "released_at": "2026-08-05T23:40:39Z" }
altium-bridge-v0.0.42.zipadded⋯ 1 unchanged line ⋯
bridge-src/BRIDGE_VERSION+1−1@@ -1 +1 @@-0.0.41+0.0.42
bridge-src/__pycache__/check_wiring.cpython-312.pyc⋯ 1 unchanged line ⋯
bridge-src/__pycache__/server.cpython-312.pyc⋯ 1 unchanged line ⋯
bridge-src/bridge.json+3−2@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "altium", "displayName": "Altium Bridge",- "version": "0.0.41",+ "version": "0.0.42", "description": "Drives Altium Designer from a container: build, clone and edit file-based symbol/footprint/Integrated libraries with LCSC/JLCPCB part numbers attached, and generate batch outputs (STEP, PDF, Gerber, BOM, CPL) from an OutJob. Pairs with the container-side altium-jlc orchestrator (adom-jlcpcb + chip-fetcher).", "homepage": "https://wiki.adom.inc/adom/adom-desktop-altium-bridge", "author": "Adom Inc.",@@ -47,7 +47,8 @@ "altium_copy_footprint", "altium_script_state", "altium_export_step",- "altium_run_outjob"+ "altium_run_outjob",+ "altium_install_extension" ], "dependencies": { "python": ">=3.8"
bridge-src/check_wiring.py+2−1@@ -24,7 +24,8 @@ import sys from pathlib import Path # Verbs the server answers itself, with no in-Altium counterpart to match against.-HOST_ONLY = {"altium_ping", "altium_detect", "altium_script_state"}+HOST_ONLY = {"altium_ping", "altium_detect", "altium_script_state",+ "altium_install_extension"} def routed_verbs(server_py: Path) -> dict:
bridge-src/extension/AdomBridge/AdomBridge.dlladded⋯ 1 unchanged line ⋯
bridge-src/extension/AdomBridge/AdomBridge.insadded+21@@ -0,0 +1,21 @@+ClientInsFile 1.0++Server+ EditorName = 'AdomBridge'+ EditorExePath = 'AdomBridge.DLL'+ EditorDescription = 'Adom Bridge - parameterized processes for container automation'+ Version = 'Version 0.2.0'+ Date = '05-Aug-2026'+ HelpAboutInfo = 'Adom Inc. in-process automation bridge.'+ Copyright = 'Copyright Adom Inc. 2026'+End++Command+ Name = 'Ping'+ LongSummary = 'AdomBridge connectivity test - writes adombridge_ping.txt'+End++Command+ Name = 'ExportStep'+ LongSummary = 'Export a PcbDoc 3D model to STEP. PcbDoc=<path>|Out=<path>'+End
bridge-src/extension/AdomBridge/AdomBridge.rcsadded+15@@ -0,0 +1,15 @@+PL PLAdomBridge_Ping+ Command='AdomBridge:Ping'+ Caption='Adom Ping'+ Image=''+ Params=''+ Description='AdomBridge connectivity test'+End++PL PLAdomBridge_ExportStep+ Command='AdomBridge:ExportStep'+ Caption='Adom Export STEP'+ Image=''+ Params=''+ Description='Export a PcbDoc 3D model to STEP'+End
bridge-src/extension/AdomBridge/Help/ServerDlls.txtadded+1@@ -0,0 +1 @@+AdomBridge=AdomBridge
bridge-src/extension/Install-AdomBridge.ps1added+100@@ -0,0 +1,100 @@+# Install-AdomBridge.ps1 — hand-install the AdomBridge extension.+#+# Altium ships no silent extension-install CLI (AltiumInstaller.exe has no /silent-style switch,+# and no .rcs registers an install process), but the Extensions folder and ExtensionsRegistry.xml+# both carry an inherited "Everyone: Modify" ACE, so writing the files directly IS the supported+# shape. This is what a working third-party extension's Deploy.ps1 does too.+#+# Does NOT restart Altium — killing a live Altium risks unsaved schematics. Restart is the+# caller's decision, made explicitly.+#+# powershell -ExecutionPolicy Bypass -File Install-AdomBridge.ps1 -Source <dir> [-Remove]++[CmdletBinding()]+param(+ [string] $Source = "$PSScriptRoot\AdomBridge",+ [switch] $Remove+)+$ErrorActionPreference = 'Stop'++$adRoot = Get-ChildItem 'C:\ProgramData\Altium' -Directory |+ Where-Object { $_.Name -like 'Altium Designer*' } | Select-Object -First 1+if (-not $adRoot) { throw 'No "Altium Designer {GUID}" folder under C:\ProgramData\Altium' }++$extRoot = Join-Path $adRoot.FullName 'Extensions'+$target = Join-Path $extRoot 'AdomBridge'+$reg = Join-Path $extRoot 'ExtensionsRegistry.xml'+$guid = 'CDFE1901-218C-5A6A-BEF9-E5BE7083B9FC'++Write-Output "extensions root : $extRoot"+Write-Output "registry : $reg"++# --- always back the registry up before touching it -------------------------------------+$backup = "$reg.bak-" + (Get-Date -Format 'yyyyMMdd-HHmmss')+Copy-Item -LiteralPath $reg -Destination $backup -Force+Write-Output "registry backup : $backup"++$xml = [xml](Get-Content -LiteralPath $reg -Raw)+$existing = $xml.SelectNodes('//Item') | Where-Object { $_.GetAttribute('HRID') -eq 'AdomBridge' }+foreach ($n in $existing) { [void]$n.ParentNode.RemoveChild($n) } # idempotent re-install++if ($Remove) {+ $xml.Save($reg)+ if (Test-Path $target) { Remove-Item -LiteralPath $target -Recurse -Force }+ Write-Output 'REMOVED (restart Altium to unload)'+ exit 0+}++# --- files ------------------------------------------------------------------------------+if (Test-Path $target) { Remove-Item -LiteralPath $target -Recurse -Force }+Copy-Item -LiteralPath $Source -Destination $target -Recurse -Force+Write-Output "installed files : $target"+Get-ChildItem $target -Recurse -File | ForEach-Object { Write-Output (' ' + $_.FullName.Substring($target.Length + 1)) }++# --- registry node ----------------------------------------------------------------------+# Field set mirrors the shipped entries; anything Altium fills in itself is left empty.+$item = $xml.CreateElement('Item')+$item.SetAttribute('HRID', 'AdomBridge')+$item.SetAttribute('Guid', $guid)+function Add-Child([string]$name, [string]$text) {+ $e = $xml.CreateElement($name); $e.InnerText = $text; [void]$item.AppendChild($e)+}+Add-Child 'Path' $target+Add-Child 'Status' '0'+Add-Child 'VaultGuid' ''+Add-Child 'CreatedBy' 'Adom Inc.'+Add-Child 'CategoryGuid' ''+Add-Child 'CategoryName' ''+Add-Child 'ReadMe' ''+Add-Child 'Help' ''+Add-Child 'Requirements' ''+Add-Child 'Title' 'Adom Bridge'+Add-Child 'ShortDescription' 'Parameterized in-process automation for container-driven Altium.'+Add-Child 'LongDescription' 'Registers processes that accept arguments, so a container can drive Altium without the wizard-and-click surface Altium exposes externally.'+Add-Child 'SmallImage' ''+Add-Child 'LargeImage' ''+Add-Child 'Version' '0.1.0.0'+Add-Child 'VersionGuid' ([guid]::NewGuid().ToString().ToUpper())+$oleNow = [string]([datetime]::Now.ToOADate())+Add-Child 'ReleasedDate' $oleNow+Add-Child 'ReleaseNotes' ''+Add-Child 'DateInstalled' $oleNow++# Shipped entries all carry this block; absent it, the client may treat the entry as malformed.+$pv = $xml.CreateElement('PlatformVersions')+foreach ($pair in @(@('DXP','1.0.16.43'), @('EDP','10.0.16.43'), @('MaxDXP','0.0.0.0'), @('MaxEDP','0.0.0.0'))) {+ $e = $xml.CreateElement($pair[0]); $e.SetAttribute('BuildNumber', $pair[1]); [void]$pv.AppendChild($e)+}+[void]$item.AppendChild($pv)+[void]$xml.DocumentElement.AppendChild($item)++# AltiumInstaller carries a "…it is opened by some process" string, so tolerate a transient lock.+for ($i = 0; $i -lt 5; $i++) {+ try { $xml.Save($reg); break }+ catch { if ($i -eq 4) { throw }; Start-Sleep -Seconds 2 }+}++$check = [xml](Get-Content -LiteralPath $reg -Raw)+$n = ($check.SelectNodes('//Item') | Where-Object { $_.GetAttribute('HRID') -eq 'AdomBridge' }).Count+Write-Output "registry items : $(($check.SelectNodes('//Item')).Count) total, AdomBridge nodes = $n"+Write-Output 'INSTALLED — Altium must be restarted before the server is loaded.'
bridge-src/extension/REMOVE-AdomBridge.cmdadded+38@@ -0,0 +1,38 @@+@echo off+REM ============================================================================+REM EMERGENCY ROLLBACK - double-click this if Altium will not start.+REM+REM Deletes the AdomBridge extension folder and its ExtensionsRegistry.xml entry,+REM returning Altium to exactly how it was before the extension was installed.+REM Needs no admin rights and no network. Safe to run twice.+REM+REM After it finishes, start Altium normally.+REM ============================================================================+setlocal+echo.+echo Removing the AdomBridge Altium extension...+echo.++powershell -NoProfile -ExecutionPolicy Bypass -Command ^+ "$ErrorActionPreference='Stop';" ^+ "$root = Get-ChildItem 'C:\ProgramData\Altium' -Directory | Where-Object { $_.Name -like 'Altium Designer*' } | Select-Object -First 1;" ^+ "if (-not $root) { Write-Host ' No Altium Designer folder found - nothing to do.'; exit 0 };" ^+ "$ext = Join-Path $root.FullName 'Extensions';" ^+ "$dir = Join-Path $ext 'AdomBridge';" ^+ "$reg = Join-Path $ext 'ExtensionsRegistry.xml';" ^+ "if (Test-Path $dir) { Remove-Item -LiteralPath $dir -Recurse -Force; Write-Host ' removed folder :' $dir } else { Write-Host ' folder already gone' };" ^+ "if (Test-Path $reg) {" ^+ " Copy-Item -LiteralPath $reg -Destination ($reg + '.bak-remove-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) -Force;" ^+ " $x = [xml](Get-Content -LiteralPath $reg -Raw);" ^+ " $n = @($x.SelectNodes('//Item') | Where-Object { $_.GetAttribute('HRID') -eq 'AdomBridge' });" ^+ " foreach ($i in $n) { [void]$i.ParentNode.RemoveChild($i) };" ^+ " for ($k=0; $k -lt 5; $k++) { try { $x.Save($reg); break } catch { Start-Sleep -Seconds 2 } };" ^+ " Write-Host ' removed registry entries :' $n.Count;" ^+ " $c = [xml](Get-Content -LiteralPath $reg -Raw);" ^+ " Write-Host ' registry now has' @($c.SelectNodes('//Item')).Count 'items'" ^+ "} else { Write-Host ' no ExtensionsRegistry.xml found' }"++echo.+echo Done. Start Altium normally.+echo.+pause
bridge-src/server.py+47@@ -257,6 +257,52 @@ def handle_cleanup_libs(args: dict) -> dict: return addin_call("cleanup_libs", {"match": match}, timeout=args.get("timeout", 60)) +def _extension_dir() -> Path:+ return Path(__file__).parent / "extension"+++def handle_install_extension(args: dict) -> dict:+ """Install (or with remove=true, uninstall) the in-process AdomBridge Altium extension.++ The extension is what makes parameterized, non-interactive verbs possible at all -- from+ outside the process Altium only exposes parameterless dialog-driven processes. Installing+ is pure file-copy plus one node in ExtensionsRegistry.xml; both targets carry an inherited+ Everyone:Modify ACE, so no elevation is needed.++ ALTIUM MUST BE CLOSED. Once any AdomBridge process has run, Altium holds AdomBridge.dll+ open and the copy fails with 'Access to the path is denied'. And Altium only loads+ extensions at startup, so a restart is required afterwards either way.+ """+ src = _extension_dir()+ installer = src / "Install-AdomBridge.ps1"+ if not installer.exists():+ return {"ok": False, "error": f"installer missing at {installer}"}++ import subprocess+ cmd = ["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass",+ "-File", str(installer), "-Source", str(src / "AdomBridge")]+ if args.get("remove"):+ cmd.append("-Remove")++ try:+ p = subprocess.run(cmd, capture_output=True, text=True, timeout=args.get("timeout", 180))+ except Exception as e:+ return {"ok": False, "error": f"installer failed to run: {e}"}++ out, err = (p.stdout or "").strip(), (p.stderr or "").strip()+ ok = p.returncode == 0 and "denied" not in err.lower()+ res = {"ok": ok, "removed": bool(args.get("remove")), "output": out}+ if err:+ res["stderr"] = err[:800]+ if "denied" in err.lower():+ res["error"] = "AdomBridge.dll is locked - close Altium and retry."+ res["_hint"] = ("Altium holds the extension DLL open once any AdomBridge process has run. "+ "Close Altium, re-run this verb, then start Altium again.")+ else:+ res["_hint"] = "Restart Altium to load the extension; it is only read at startup."+ return res++ def handle_script_state(args: dict) -> dict: """Which in-Altium script is live vs. the one this bridge package ships. @@ -375,6 +421,7 @@ COMMAND_HANDLERS = { "altium_ping": handle_ping, "altium_detect": handle_detect, "altium_script_state": handle_script_state,+ "altium_install_extension": handle_install_extension, "altium_export_step": handle_export_step, "altium_run_outjob": handle_run_outjob, "altium_compile_intlib": handle_compile_intlib,
package.json+8−7@@ -1,8 +1,8 @@ { "slug": "adom-desktop-altium-bridge",- "version": "0.0.41",+ "version": "0.0.42", "type": "app",- "description": "Adom Desktop bridge for Altium Designer \u2014 build and edit file-based symbol/footprint/Integrated libraries with LCSC/JLCPCB part numbers attached, and generate batch outputs (STEP, PDF, Gerber, BOM, CPL) from an OutJob, by driving Altium via DelphiScript.",+ "description": "Adom Desktop bridge for Altium Designer: file-based symbol/footprint/IntLib authoring with LCSC/JLCPCB part numbers, Gerber/BOM/CPL batch output, and an in-process .NET extension that adds parameterized headless verbs including STEP export with hole and copper options.", "tags": [ "app", "adom-desktop",@@ -21,13 +21,14 @@ "path": "docs/hero.png" }, "discovery_triggers": [- "add a part to my altium library", "drive altium from claude", "altium bridge",- "export step from altium",- "run an altium outjob",- "build an altium footprint",- "altium symbol library"+ "export step from altium headless",+ "add a part to my altium library",+ "altium extension",+ "install altium extension",+ "altium step export",+ "run an altium outjob" ], "discovery_pitch": "Use when the user wants Claude to author or edit Altium libraries, or to generate STEP/PDF/Gerber/BOM/CPL from an Altium board on their Windows machine.", "sample_prompts": [
page.json+10−8@@ -1,8 +1,8 @@ { "slug": "adom-desktop-altium-bridge",- "version": "0.0.41",+ "version": "0.0.42", "type": "app",- "description": "Adom Desktop bridge for Altium Designer \u2014 build and edit file-based symbol/footprint/Integrated libraries with LCSC/JLCPCB part numbers attached, and generate batch outputs (STEP, PDF, Gerber, BOM, CPL) from an OutJob, by driving Altium via DelphiScript.",+ "description": "Adom Desktop bridge for Altium Designer: file-based symbol/footprint/IntLib authoring with LCSC/JLCPCB part numbers, Gerber/BOM/CPL batch output, and an in-process .NET extension that adds parameterized headless verbs including STEP export with hole and copper options.", "tags": [ "app", "adom-desktop",@@ -18,6 +18,7 @@ }, "needs_sudo": false, "hero": {+ "type": "image", "path": "docs/hero.png" }, "author": {@@ -25,13 +26,14 @@ "email": "[email protected]" }, "discovery_triggers": [- "add a part to my altium library", "drive altium from claude", "altium bridge",- "export step from altium",- "run an altium outjob",- "build an altium footprint",- "altium symbol library"+ "export step from altium headless",+ "add a part to my altium library",+ "altium extension",+ "install altium extension",+ "altium step export",+ "run an altium outjob" ], "discovery_pitch": "Use when the user wants Claude to author or edit Altium libraries, or to generate STEP/PDF/Gerber/BOM/CPL from an Altium board on their Windows machine.", "sample_prompts": [@@ -48,4 +50,4 @@ "prompt": "Is the Altium bridge connected and what version is it?" } ]-}+}