The host's in-tree Heroic scanner, lifted out into its own installable plugin
(design/library-scanner-plugins.md M5/WP5.2). One source covers all three of
Heroic's backends — legendary (Epic), gog, nile (Amazon) — because that is how
Heroic presents them and how the built-in scanner did.
The package is a config schema, a detect, a scan and a watchDirs. The store
claim, sync engine, __config surface, console registration and the
detect/scan/parity/uninstall CLI verbs all come from
@punktfunk/plugin-kit/library.
Claiming the `heroic` store keeps the entry ids the built-in scanner produced
(`heroic:<runner>:<appName>`), so GameStream app ids, Moonlight pins, cached art
and the operator's enabled/disabled state all survive the migration.
Ported behaviours worth naming, each covered by a test:
* a title counts as installed only if Heroic says so AND its install directory
still exists. The second check is the workaround for Heroic's GOG
is_installed bug (#2691), without which uninstalled games linger.
* only http(s) art is emitted. A sideloaded title can carry a local file://
path, which — unlike a path this plugin resolved itself — is not one the host
should proxy, and the client cannot fetch either. It degrades to the title
card, as in-host.
* both detect signals ride along: the install dir and the HEROIC_APP_NAME env
marker. Heroic hands off to legendary/gogdl/nile, so the host never sees the
game's own process any other way, and the env marker is what works under
Proton (verified on-glass 2026-07-27). They are a union, so a Heroic version
that stops setting it degrades rather than breaking.
The launch VALUE and the `heroic` launch kind are unchanged — the host still owns
turning `<runner>:<appName>` into a heroic://launch command, so this plugin never
constructs a command line.
Verified locally against the kit: tsc clean, biome clean, 5 tests, bundle builds,
CLI runs (detect -> absent, scan -> 0 games).
As with lutris: no lockfile and CI cannot be green until
@punktfunk/plugin-kit@0.3.0 is published. The parity gate has NOT been run — that
needs a box with Heroic installed.
3.4 KiB
@punktfunk/plugin-heroic
Adds your installed Heroic Games Launcher titles to a Punktfunk host's library — Epic, GOG and Amazon in one source, with their cover art, and with the session ending when you quit the game.
Install it from the host's plugin store (Library → Game sources → Add a source), or:
punktfunk-host plugins add heroic
Nothing to configure: it finds Heroic's config folder in the usual places ($XDG_CONFIG_HOME/heroic,
~/.config/heroic, Flatpak) and reads the library caches from there. If yours lives somewhere else,
set the path in Library → Game sources → Heroic → Settings.
What it replaces
Punktfunk used to scan Heroic from inside the host. This plugin is that scanner, lifted out — so you install it only if you use Heroic, and it can be updated without updating the host.
It claims the heroic store, which means your games keep exactly the ids they had before
(heroic:legendary:<appName>): Moonlight pins, cached box art, and your enabled/disabled state all
carry over, and the host's built-in scanner switches itself off while this is installed. Remove the
plugin and the built-in comes straight back.
What gets listed
Only titles Heroic reports as installed and whose install directory still exists — the second
check works around Heroic's GOG is_installed bug
(#2691), which otherwise
leaves uninstalled games sitting in your library.
Art comes straight from Heroic's cached CDN URLs, so the client fetches it directly. A sideloaded
title whose art is a local file:// path is listed without art rather than with a picture nothing
can load.
Commands
punktfunk-plugin-heroic detect # is Heroic installed on this host?
punktfunk-plugin-heroic scan --preview # what WOULD be synced, as JSON (works with no host)
punktfunk-plugin-heroic uninstall # drop the games and release the store claim
Parity — the release gate
Unit tests pin the parsing. What actually has to hold is that this plugin reproduces the in-host
scanner it replaces, down to the ids — one that parses perfectly but emits a subtly different
external_id breaks every Moonlight pin on the host, and no unit test notices.
So, on a box with Heroic installed, before installing this plugin:
punktfunk-plugin-heroic parity --snapshot before.json # host still on its built-in scanner
punktfunk-plugin-heroic parity --compare before.json # offline; exits non-zero on any difference
--compare runs this plugin's own scan and diffs it: ids, titles, launch recipes, roles and
metadata exactly; art by presence, not value.
Worth re-checking on-glass specifically: a game launched under Proton. Heroic hands off to
legendary/gogdl/nile, so the HEROIC_APP_NAME environment marker is what lets the host recognise
the running game and end the session when you quit it.
Do not publish a version whose parity run is red.
Development
bun install
bun run typecheck && bun test && bun run build
The framework — store claim, sync engine, __config, CLI verbs, and the launcher-file parsers —
lives in @punktfunk/plugin-kit/library.
This package is the Heroic-specific part: find the config root, read the three store_cache
libraries, map entries. See design/library-scanner-plugins.md in the main repo.
License
MIT OR Apache-2.0.