Ported from the host's in-tree scanner (crates/punktfunk-host/src/library/epic.rs). Reads
the machine-wide `%ProgramData%\Epic\EpicGamesLauncher\Data`: the `.item` manifests for
titles, and `Catalog\catcache.bin` for cover art.
The launch VALUE (the `namespace:catalogItemId:appName` triple, falling back to the bare
appName when a manifest lacks the ids) and the `epic` launch kind are unchanged, so the
host still owns turning that into a `com.epicgames.launcher://` URI — this plugin never
constructs a command line (D1). `external_id` is the appName, so the host composes
`epic:<appName>` byte-identically to the built-in scanner and every entry id, GameStream
app id and client art cache survives the migration.
Carried over deliberately, because the parity gate compares exactly these sets:
* Playnite's EXCLUSION filter — skip `UE_*` engine components, skip a DLC/addon unless
it is `addons/launchable` — rather than a positive `games`-category match, which drops
legitimate titles.
* Stale records whose install dir is gone are dropped.
* `LaunchExecutable` is used as the detect hint when the file is really there, else the
install dir. Epic hands off to its own launcher and exits, so without this the host
loses the game the moment the shim returns.
* Both reads are size-capped BEFORE the base64 and JSON decodes each make another copy:
Epic's data dir is Users-writable, and this runs on the host (security-review S4).
* Only `http(s)` art URLs reach a client.
Runs under the runner's principal — NT AUTHORITY\LocalService on Windows. Probed on
hardware 2026-08-06: the `%ProgramData%` manifest dir IS readable there, which is what
makes this port viable de-privileged; nothing here reaches for HKCU or a user profile.
No launcher tile yet: the host has no verified `launcher_ui` activation for Epic, so one
would be refused with a 400. The config switch is present and off, so enabling it later is
a one-line change.
Gates: tsc --noEmit clean, 9 tests pass, biome clean, `bun run build` produces both entry
points, and the CLI answers honestly off-platform (`detect` -> absent, `scan` -> []).
Still owed: `parity --compare` on a Windows box with Epic installed, under the runner.