app
KiCad - the KiCad Bridge
Public Made by Adomby adom
Reference implementation of the KiCad bridge: multi-instance Python server, forward path via kicad-cli, reverse path via in-process plugin. Most complex of the three bundled bridges.
← Commit history
Update 6 file(s)
4 files changed
+84−12
dashboard/kicad_dashboard.py+32−2@@ -916,10 +916,40 @@ def main(): globals()["DEV_MODE"] = True if cmd == "serve":- if "--print-url" in argv:- print(proxy_url(port), flush=True)+ # BIND FIRST. The old order printed the URL and only then tried to bind,+ # so a port clash advertised a URL nothing was listening on (wiki #43). srv = ThreadingHTTPServer(("0.0.0.0", port), Handler) srv.daemon_threads = True++ if "--print-url" in argv:+ # The Hydrogen dock contract (adom/hydrogen-dock-sdk): print the URL+ # and SELF-BACKGROUND, so the workspace exec returns promptly.+ # Hydrogen deliberately does not detach for us - it runs this step+ # synchronously, and a serve that stays in the foreground holds the+ # exec for its whole timeout, after which the process tree is KILLED+ # and the readiness probe finds nothing listening. That is the+ # "Waiting to answer" hang John measured on the Fusion button.+ #+ # The socket is already bound above, so by the time the parent+ # prints, the URL is genuinely reachable.+ url = proxy_url(port)+ if hasattr(os, "fork"):+ if os.fork() > 0:+ print(url, flush=True)+ os._exit(0) # _exit: never run atexit / close the fd+ os.setsid()+ # Detach stdio or the exec's pipe stays open and it keeps waiting+ # on a command that has, as far as it can tell, not finished.+ _null = os.open(os.devnull, os.O_RDWR)+ for _fd in (0, 1, 2):+ try:+ os.dup2(_null, _fd)+ except Exception:+ pass+ else:+ # No fork (Windows): print and serve in the foreground. Honest+ # fallback - the dock runs this in a Linux container.+ print(url, flush=True) log(f"kicad-dashboard {VERSION} serving on {port} (target={target or 'auto'})") try: srv.serve_forever()
dock.json+15−8@@ -1,18 +1,25 @@ { "dock_manifest": "1.1.0",- "webview": { "title": "KiCad Dashboard", "favicon": "docs/img/dock-kicad.svg" },+ "webview": {+ "title": "KiCad Dashboard",+ "favicon": "docs/img/dock-kicad.svg"+ }, "launch": { "mode": "container_command", "console": "show", "steps": [- { "label": "Installing the KiCad bridge",+ {+ "label": "Installing the KiCad bridge", "run": "adom-wiki pkg install adom/kicad-bridge",- "skip_if": "command -v kicad-dashboard",- "timeout_seconds": 180 },- { "label": "Starting the KiCad Dashboard",- "run": "kicad-dashboard serve --print-url",+ "skip_if": "sh -c 'PATH=\"$HOME/.local/bin:$PATH\"; command -v kicad-dashboard'",+ "timeout_seconds": 180+ },+ {+ "label": "Starting the KiCad Dashboard",+ "run": "sh -c 'PATH=\"$HOME/.local/bin:$PATH\"; exec kicad-dashboard serve --print-url'", "await": "url",- "timeout_seconds": 60 }+ "timeout_seconds": 60+ } ], "on_ready": "navigate_placeholder", "ai": {@@ -23,4 +30,4 @@ "typed": false } }-}+}
install.sh+32@@ -12,4 +12,36 @@ cp -f "$H/skills/kicad-tour/tour_runner.py" "$D/kicad-tour/tour_runner.py" cp -f "$H/skills/kicad-bridge-hero/compose_hero.py" "$D/kicad-bridge-hero/compose_hero.py" cp -f "$H/skills/kicad-bridge-test/run_verb_tests.py" "$D/kicad-bridge-test/run_verb_tests.py" rm -rf "$D/adom-desktop-kicad-bridge" # legacy install dir from pre-rename versions+# ---------------------------------------------------------------------------+# The DASHBOARD and its launcher (wiki issue #43, John).+#+# dock.json runs `kicad-dashboard serve --print-url`, and this package never+# installed that command - it only delivered skills. It looked fine from a+# developer container because a hand-written 104-byte wrapper pointing at a+# local dev clone happened to exist there, so `skip_if: command -v+# kicad-dashboard` reported SKIP on the maintainer's machine and RUN on every+# user's. Clicking KiCad in the dock died with exit 127.+#+# The page repo already ships the dashboard, so this installs what it carries.+APPDIR="$HOME/.local/share/adom/kicad-bridge"+BINDIR="$HOME/.local/bin"+mkdir -p "$APPDIR/dashboard/ui" "$BINDIR"+cp -f "$H/dashboard/kicad_dashboard.py" "$APPDIR/dashboard/kicad_dashboard.py"+for asset in index.html kicad-logo.png; do+ [ -f "$H/dashboard/ui/$asset" ] && cp -f "$H/dashboard/ui/$asset" "$APPDIR/dashboard/ui/$asset"+done++cat > "$BINDIR/kicad-dashboard" <<LAUNCHER+#!/usr/bin/env bash+# Installed by adom/kicad-bridge. Points at the INSTALLED dashboard, never a+# developer's working copy - that distinction is the whole of wiki issue #43.+exec python3 "$APPDIR/dashboard/kicad_dashboard.py" "\$@"+LAUNCHER+chmod +x "$BINDIR/kicad-dashboard"++case ":$PATH:" in+ *":$BINDIR:"*) ;;+ *) echo "[kicad-bridge] NOTE: $BINDIR is not on your PATH; the dock passes it explicitly." ;;+esac+echo "[kicad-bridge] dashboard installed -> $BINDIR/kicad-dashboard" echo "[kicad-bridge] skills installed."
package.json+5−2@@ -1,6 +1,6 @@ { "slug": "kicad-bridge",- "version": "1.0.33",+ "version": "1.0.34", "type": "app", "description": "Skills for your container so your AI knows how to drive the KiCad bridge. The bridge runtime itself is the release zip; Adom Bridge loads that.", "tags": [@@ -36,6 +36,9 @@ "skills/kicad-bridge-hero/compose_hero.py", "skills/kicad-bridge-test/SKILL.md", "skills/kicad-bridge-test/run_verb_tests.py",- "skills/kicad-bridge-background/SKILL.md"+ "skills/kicad-bridge-background/SKILL.md",+ "dashboard/kicad_dashboard.py",+ "dashboard/ui/index.html",+ "dashboard/ui/kicad-logo.png" ] }