@punktfunk/plugin-heroic (0.2.0)
Installation
@punktfunk:registry=https://git.unom.io/api/packages/unom/npm/npm install @punktfunk/plugin-heroic@0.2.0"@punktfunk/plugin-heroic": "0.2.0"About this package
@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 the host's own scanner
gave them (heroic:legendary:<appName>): Moonlight pins, cached box art, and your enabled/disabled
state all carry over. Hosts through v0.27.x also had the scanner compiled in and switched it off
while this plugin was installed; from v0.28.0 the built-ins are gone and this plugin is the only way
to get Heroic titles into the library.
What gets listed
Titles Heroic has installed, whose install directory still exists. The directory check works
around Heroic's GOG is_installed bug
(#2691) and prunes a
library cache that has gone stale, either of which would otherwise leave uninstalled games sitting in
your library.
Where "installed" is read from matters, and getting it wrong is what made this plugin list Epic titles and nothing else before v0.2.0. Heroic keeps two files per backend:
library cache (store_cache/…) |
installed state | |
|---|---|---|
| Epic (legendary) | every owned title + art; install state baked in on refresh | legendaryConfig/legendary/installed.json |
| GOG | every owned title + art; install state deliberately stripped out | gog_store/installed.json |
| Amazon (nile) | every owned title + art; install state baked in on refresh | nile_config/nile/installed.json |
Heroic writes the GOG cache from a copy with the install data removed on purpose and merges the real
state back in when it reads it, so every GOG row on disk says is_installed: false. A scan that
believed the cache dropped your whole GOG library. Epic and Amazon do bake it in, but only when a
full library refresh rewrites the file — installing a game leaves the cache stale until then. So this
plugin takes each runner's own installed.json as the authority and uses the cache for the title and
the art, which also means a game you just installed appears without waiting for Heroic to refresh.
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.
Heroic's sideloaded titles are not listed. They live in a different file again
(sideload_apps/library.json) and record an executable rather than an install directory, and the
host's heroic launch kind does not accept a sideload runner — so listing them would only produce
tiles that refuse to start.
The launcher tiles
Two tiles open Heroic itself rather than a game, both on by default and each switchable in Library → Game sources → Heroic → Settings:
- Heroic Console Mode — Heroic's fullscreen gamepad UI, the one worth having on a stream. Needs Heroic 2.21 or newer; an older Heroic ignores the flag and opens fullscreen on the normal UI.
- Heroic — the ordinary window.
The host builds both command lines; the plugin only names which UI to open. Console mode in
particular needs two flags (--console routes the UI, --fullscreen fills the screen) and cannot
be reached through a heroic:// URI at all, which is why that knowledge lives in the host.
Packaging note. The console tile publishes
launch.value = "heroic-console", which a host older than the release that added it refuses — and a refused reconcile takes the whole batch with it. The plugin index entry for any version from 0.2.0 on must carry aminHostat or above that release.
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 an upgrade does not silently change
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.
parity --snapshot captures the host's CURRENT library for the heroic store — whatever produced
it. So, on a box with Heroic installed, snapshot with the old version still running, upgrade,
then compare:
punktfunk-plugin-heroic parity --snapshot before.json # host still on the previous version
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.
Read the 0.2.0 diff rather than requiring it to be empty — it is expected to be non-empty, and the
additions are the point: every GOG title, every Amazon title, and the new heroic:console tile.
What must NOT appear is a changed or removed heroic:legendary:* id.
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. And the console tile itself — that it opens
Heroic's gamepad UI fullscreen, on both a native and a Flatpak Heroic.
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 and the three installed.json files behind them, map entries. See
design/library-scanner-plugins.md in the main repo.
License
MIT OR Apache-2.0.
Dependencies
Dependencies
| ID | Version |
|---|---|
| @punktfunk/plugin-kit | ^0.4.4 |
| effect | 4.0.0-beta.99 |
Development Dependencies
| ID | Version |
|---|---|
| @biomejs/biome | ^2.5.2 |
| @types/bun | ^1.3.0 |
| typescript | ^5.9.3 |