Recognising a running game is guesswork the host has to do from the outside: it scans for an install directory, an executable, a process name. A title with none of those — an emulated game, a manually added one, a library plugin that records no install directory — could not be recognised at all, and Playnite's launch is a playnite:// hand-off, so the host held no process either. Quitting such a game never ended the streaming session, and "End game" had nothing to aim at.
PR #7 narrowed this by sending every detect signal Playnite had. It could not close it: some games genuinely have none. Playnite, meanwhile, has known the whole time — it starts the game, tracks it in whatever mode the person configured, and raises an event on both edges carrying the pid.
Exporter 0.4.0
Hooks OnGameStarted / OnGameStopped / OnGameStartupCancelled and writes punktfunk-running.json beside the library export, in both drop points:
{"schema":1,"generatedAt":"2026-08-20T21:00:00Z",// re-stamped every 30s while anything runs
"running":[{"id":"e3b0c442-…","pid":4242}]}
Its own document, not a field on each game. The library export is the whole catalogue — rewriting a thousand games on every launch would be absurd — and the plugin fingerprints that document to decide whether to reconcile, so folding in a fact that changes on every launch would turn each launch into a full library PUT. A new file also needs no Schema bump, where a bumped library schema is refused outright by every installed plugin.
Never debounced (a stop delayed is a session delayed), unlike the library export.
Seeded at startup from Game.IsRunning, so a Playnite restart — or installing this extension — mid-game converges instead of claiming everything stopped.
Deleted when Playnite closes, not emptied. A game Playnite launched keeps running perfectly well after Playnite quits, so "nothing is running" would be a lie; no document at all is honestly "no information".
The plugin (0.4.5)
Polls that file every 3 s and PUTs the set to PUT /library/provider/playnite/running, restating it every 30 s so the host's claim stays alive.
⭐The distinction the whole thing turns on: an empty report says "everything stopped" and ends the streaming session; silence says "no information" and lets the host's claim expire so it falls back to process scanning. Every unusable document — missing, malformed, too new, or older than 90 s with nobody restating it — produces silence. Pinned by tests.
sync.reportRunning (default on) turns it off, surfaced in Settings → Sync rather than left config-file-only: the host acts on this, so the way back out has to be findable by whoever hits the problem.
Compatibility
The call goes through the kit's untyped host seam, not ProviderClient.reportRunning. That method landed in plugin-kit 0.4.4 and depending on it makes this repo bun install-broken until the kit publishes (No version matching "^0.4.4"), for the identical request. Kit dep stays ^0.4.3.
An older host has no such route: it 404s, the plugin logs it at debug and carries on with detect-hint tracking. So minHost does not need to move — 0.25.0 still holds.
Needs a host carrying the route (punktfunk PR #361) for the feature to do anything.
Gates (all green, against the published kit 0.4.3)
bun run typecheck ×3 · bun test70 pass / 0 fail · bunx biome check · bun run build (backend + SPA) · dotnet build exporter -c Release0 warnings, 0 errors.
The staleness guard was proved non-vacuous the honest way: backing it out turns the stale-document test red.
⚠ No on-glass yet — this needs a Windows box with real Playnite. The exporter half in particular has only been compiled, not run.
Recognising a running game is guesswork the host has to do from the outside: it scans for an install directory, an executable, a process name. A title with **none** of those — an emulated game, a manually added one, a library plugin that records no install directory — could not be recognised at all, and Playnite's launch is a `playnite://` hand-off, so the host held no process either. Quitting such a game never ended the streaming session, and "End game" had nothing to aim at.
PR #7 narrowed this by sending every detect signal Playnite had. It could not close it: some games genuinely have none. Playnite, meanwhile, has known the whole time — it starts the game, tracks it in whatever mode the person configured, and raises an event on both edges carrying the pid.
## Exporter 0.4.0
Hooks `OnGameStarted` / `OnGameStopped` / `OnGameStartupCancelled` and writes `punktfunk-running.json` beside the library export, in both drop points:
```jsonc
{ "schema": 1,
"generatedAt": "2026-08-20T21:00:00Z", // re-stamped every 30s while anything runs
"running": [ { "id": "e3b0c442-…", "pid": 4242 } ] }
```
- **Its own document, not a field on each game.** The library export is the whole catalogue — rewriting a thousand games on every launch would be absurd — and the plugin *fingerprints* that document to decide whether to reconcile, so folding in a fact that changes on every launch would turn each launch into a full library PUT. A new file also needs no `Schema` bump, where a bumped library schema is refused outright by every installed plugin.
- **Never debounced** (a stop delayed is a session delayed), unlike the library export.
- **Seeded at startup** from `Game.IsRunning`, so a Playnite restart — or installing this extension — mid-game converges instead of claiming everything stopped.
- **Deleted when Playnite closes**, not emptied. A game Playnite launched keeps running perfectly well after Playnite quits, so "nothing is running" would be a lie; no document at all is honestly "no information".
## The plugin (0.4.5)
Polls that file every 3 s and PUTs the set to `PUT /library/provider/playnite/running`, restating it every 30 s so the host's claim stays alive.
⭐ **The distinction the whole thing turns on:** an **empty** report says "everything stopped" and ends the streaming session; **silence** says "no information" and lets the host's claim expire so it falls back to process scanning. Every unusable document — missing, malformed, too new, or older than 90 s with nobody restating it — produces **silence**. Pinned by tests.
`sync.reportRunning` (default on) turns it off, surfaced in **Settings → Sync** rather than left config-file-only: the host *acts* on this, so the way back out has to be findable by whoever hits the problem.
## Compatibility
- The call goes through the kit's **untyped host seam**, not `ProviderClient.reportRunning`. That method landed in plugin-kit 0.4.4 and depending on it makes this repo `bun install`-broken until the kit publishes (`No version matching "^0.4.4"`), for the identical request. Kit dep stays `^0.4.3`.
- An older host has no such route: it 404s, the plugin logs it at debug and carries on with detect-hint tracking. So **`minHost` does not need to move** — 0.25.0 still holds.
- Needs a host carrying the route (punktfunk PR #361) for the feature to do anything.
## Gates (all green, against the published kit 0.4.3)
`bun run typecheck` ×3 · `bun test` **70 pass / 0 fail** · `bunx biome check` · `bun run build` (backend + SPA) · `dotnet build exporter -c Release` **0 warnings, 0 errors**.
The staleness guard was proved non-vacuous the honest way: backing it out turns the stale-document test red.
⚠ **No on-glass yet** — this needs a Windows box with real Playnite. The exporter half in particular has only been compiled, not run.
Recognising a running game is guesswork the host has to do from the outside:
it scans for an install directory, an executable, a process name. A title with
none of those — an emulated game, a manually added one, a library plugin that
records no install directory — could not be recognised at all, and Playnite's
launch is a `playnite://` hand-off, so the host held no process either. Quitting
such a game never ended the streaming session, and "End game" had nothing to
aim at. Playnite knew the whole time: it starts the game, tracks it in whatever
mode the person configured, and raises an event on both edges carrying the pid.
**Exporter 0.4.0** hooks `OnGameStarted` / `OnGameStopped` /
`OnGameStartupCancelled` and writes `punktfunk-running.json` beside the library
export, in both drop points. Its own document, not a field on each game: the
library export is the whole catalogue, the plugin fingerprints it to decide
whether to reconcile, and a new file needs no `Schema` bump — where a bumped
library schema would be refused outright by every installed plugin.
Never debounced (a stop delayed is a session delayed), seeded at startup from
`Game.IsRunning` so a restart mid-game converges instead of claiming everything
stopped, re-stamped every 30s while anything runs, and DELETED when Playnite
closes. Deleting rather than emptying is the point: a game Playnite launched
keeps running after Playnite quits, so "nothing is running" would be a lie,
where no document at all is honestly "no information".
**The plugin** polls that file every 3s and PUTs the set to
`/library/provider/playnite/running` when it changes, restating it every 30s so
the host's claim stays alive. An unusable document — missing, malformed, too
new, or older than 90s with nobody restating it — makes it go QUIET rather than
report an empty set: silence lets the host's claim expire and hand tracking back
to its process scan, where an empty report would assert every game had stopped
and end a live session. `sync.reportRunning` (Settings → Sync) turns it off.
The call goes through the kit's untyped host seam rather than
`ProviderClient.reportRunning`, which is the same request: that landed in
plugin-kit 0.4.4 and depending on it would make this plugin unbuildable until
the kit publishes, for no gain.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Recognising a running game is guesswork the host has to do from the outside: it scans for an install directory, an executable, a process name. A title with none of those — an emulated game, a manually added one, a library plugin that records no install directory — could not be recognised at all, and Playnite's launch is a
playnite://hand-off, so the host held no process either. Quitting such a game never ended the streaming session, and "End game" had nothing to aim at.PR #7 narrowed this by sending every detect signal Playnite had. It could not close it: some games genuinely have none. Playnite, meanwhile, has known the whole time — it starts the game, tracks it in whatever mode the person configured, and raises an event on both edges carrying the pid.
Exporter 0.4.0
Hooks
OnGameStarted/OnGameStopped/OnGameStartupCancelledand writespunktfunk-running.jsonbeside the library export, in both drop points:Schemabump, where a bumped library schema is refused outright by every installed plugin.Game.IsRunning, so a Playnite restart — or installing this extension — mid-game converges instead of claiming everything stopped.The plugin (0.4.5)
Polls that file every 3 s and PUTs the set to
PUT /library/provider/playnite/running, restating it every 30 s so the host's claim stays alive.⭐ The distinction the whole thing turns on: an empty report says "everything stopped" and ends the streaming session; silence says "no information" and lets the host's claim expire so it falls back to process scanning. Every unusable document — missing, malformed, too new, or older than 90 s with nobody restating it — produces silence. Pinned by tests.
sync.reportRunning(default on) turns it off, surfaced in Settings → Sync rather than left config-file-only: the host acts on this, so the way back out has to be findable by whoever hits the problem.Compatibility
ProviderClient.reportRunning. That method landed in plugin-kit 0.4.4 and depending on it makes this repobun install-broken until the kit publishes (No version matching "^0.4.4"), for the identical request. Kit dep stays^0.4.3.minHostdoes not need to move — 0.25.0 still holds.Gates (all green, against the published kit 0.4.3)
bun run typecheck×3 ·bun test70 pass / 0 fail ·bunx biome check·bun run build(backend + SPA) ·dotnet build exporter -c Release0 warnings, 0 errors.The staleness guard was proved non-vacuous the honest way: backing it out turns the stale-document test red.
⚠ No on-glass yet — this needs a Windows box with real Playnite. The exporter half in particular has only been compiled, not run.