Skip to content

feat(cli): fbuild port scan — enumerate ports + resolve VID:PID to vendor/product via FastLED/boards #741

Description

@zackees

TL;DR

Add a new fbuild port scan CLI command that walks every visible serial port and renders two rows per port:

  1. Row 1 — port path + raw VID:PID + the OS descriptor string
  2. Row 2 — resolved vendor + product name from the FastLED/boards SQLite-over-HTTP database

The point is "what is plugged in?" answered without leaving the terminal. Today the closest thing is fbuild serial probe list (#686) which annotates each port from a tiny hardcoded BOARD_FINGERPRINTS table — useful for boards we ship support for, blank for everything else. port scan consults the full FastLED/boards aggregate instead, so an unrecognized USB device shows the actual vendor + product name (e.g. Adafruit / Feather M4 Express rather than [empty]).

Where the data comes from

The canonical lookup source moved out of fbuild's own online-data branch into the public FastLED/boards repo's published portal: https://fastled.github.io/boards/.

Endpoints (from FastLED/boards README.md):

Endpoint URL
Demo query UI https://fastled.github.io/boards/
Raw SQLite database https://fastled.github.io/boards/site.db
Build provenance + counts (_meta) https://fastled.github.io/boards/_meta.json

The portal at fastled.github.io/boards/ does VID → vendor + VID:PID → product lookups client-side over site.db via sql.js (WASM). For fbuild's CLI use, we should pull the same data — either by mirroring site.db through the existing nightly data pipeline, OR by hitting site.db directly with a tiny rusqlite-over-http path.

In the meantime, the existing tiered resolver at fbuild_core::usb::resolve(vid, pid) (#712 + #717 + #719) already returns UsbInfo { vendor, product } via:

  • Tier 1 — embedded vendor archive (compile-time include_bytes!) — vendor names only
  • Tier 2 — runtime overlay loaded from the daemon-managed cache — full {vendor, product}
  • Tier 3 — synthetic Unknown vendor 0xVVVV placeholder

So the first cut of fbuild port scan can ship on the existing resolver and the tier-2 cache backing it. Pointing tier-2 at the FastLED/boards site is a follow-up wire-up question (separate issue) — this command's contract is "use whatever the resolver returns."

Proposed output shape

$ fbuild port scan
COM25    303A:1001    USB Serial Device (COM25)    ser=80:F1:B2:D1:DF:B1
         └─ Espressif Systems / ESP32-S3

COM20    16C0:0483    USB Serial Device (COM20)    ser=15821020
         └─ PJRC / Teensy 4.0

COM10    1FC9:0132    USB Serial Device (COM10)    ser=0B03400A
         └─ NXP / LPC-Link2 CMSIS-DAP

COM3     0000:0000    [Unknown]
         └─ Unknown vendor 0x0000 / Unknown product 0x0000

3 USB ports, 1 non-USB

The └─ continuation prefix makes the second row visually subordinate to the first. Unknown VID:PID still emits the second row (with the synthetic fallback from the resolver) — every port always gets exactly two rows so the table is uniform.

Why a new command and not an extension of serial probe list

  • serial probe list is the small-footprint hardcoded-table fingerprint path — fast, no I/O, useful when an agent is debugging a known FastLED-ecosystem board. Keep it as-is.
  • port scan is the richer 'use the canonical database' path. Different intent, different data source — they're complementary.

The naming also follows the FastLED side's bash autoresearch <board> precedent: scan reads as "go look harder."

Acceptance criteria

  • fbuild port scan subcommand exists in fbuild-cli.
  • Output has the two-rows-per-port shape (port + VID:PID + descriptor, then └─ vendor / product).
  • Per-port resolution goes through fbuild_core::usb::resolve(vid, pid), NOT the small hardcoded board fingerprints table.
  • Output is deterministic and unit-testable — the formatter takes a Vec<PortInfo> and emits a String so tests can pin the row shape without hardware.
  • Unknown VID:PID falls through to the synthetic Unknown vendor 0x… / Unknown product 0x… row instead of being skipped — every port always gets exactly two rows.
  • Summary line at the bottom: total USB ports, total non-USB ports (PCI / Bluetooth / Unknown).

Out of scope (follow-ups)

  • Pointing tier-2 of the resolver at FastLED/boards site.db directly (today it consumes fbuild's own online-data branch). Separate issue.
  • A --json output mode (likely a quick follow-up; not blocking the first cut).
  • Filtering / sorting flags (--vid, --vendor, --unknown-only).

Refs

Filed via Claude Code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions