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
    No fields configured for issues without a type.

    Projects

    Status
    Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions