feat(plugins): punktfunk-host plugins CLI — add/remove/list/enable/disable/status

One-liner plugin management replacing the manual scripting-dir + bunfig +
bun-add ritual: package ops forward to the bun runner (new sdk plugins
module + runner-cli subcommands, 11 tests green), enable/disable/status
drive the systemd unit on Linux and the PunktfunkScripting scheduled task
on Windows (installer support in the ISS). Docs page rewritten as .mdx
with per-platform Tabs (registered in mdx.tsx).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 16:34:19 +02:00
parent f250db96f4
commit f0c511c8fa
11 changed files with 1034 additions and 105 deletions
+2 -1
View File
@@ -149,7 +149,8 @@ directory of scripts and installed plugins as one service: crash-restarts with b
for the five-line quickstart and unit templates.
For ready-made plugins — sync your ROM collection or your Playnite library into the game library,
with a console page to manage them — see [Plugins](/docs/plugins).
with a console page to manage them — see [Plugins](/docs/plugins). Installing one is two commands:
`punktfunk-host plugins add <name>`, then `punktfunk-host plugins enable`.
The canonical "decide, don't just observe" pattern — approve pairing from your phone: watch
`pairing.pending`, send yourself a notification, and call
-100
View File
@@ -1,100 +0,0 @@
---
title: Plugins
description: First-party plugins that sync your ROM collection or your Playnite library into the game library — and how to install them.
---
Plugins extend the host through the **scripting runner** (see [Events & hooks](/docs/automation)). A
plugin runs alongside the host, reconciles titles into your **game library** as a provider — so they
appear in the grid on every client — and can add its own page to the [web console](/docs/web-console).
Two first-party plugins today:
| Plugin | What it does |
|---|---|
| **ROM Manager** | Scans your ROM directories, matches each platform to an installed emulator, and syncs them into the library with box art. |
| **Playnite** | Mirrors your [Playnite](https://playnite.link) library — every store and emulator it manages — into the library, launched back through Playnite. |
## Installing a plugin
Plugins install as packages into the runner's plugins directory, and the runner
(`punktfunk-scripting`) supervises them. It's a one-time registry setup, then `bun add`:
```sh
# The runner's plugins directory:
# Linux ~/.config/punktfunk/plugins
# Windows %ProgramData%\punktfunk\plugins
cd ~/.config/punktfunk/plugins # create it if it doesn't exist yet
# Point the @punktfunk scope at the registry (once):
cat > bunfig.toml <<'EOF'
[install.scopes]
"@punktfunk" = "https://git.unom.io/api/packages/unom/npm/"
EOF
bun add @punktfunk/plugin-rom-manager # or @punktfunk/plugin-playnite
```
Then enable the runner — it's opt-in, off until you turn it on:
```sh
systemctl --user enable --now punktfunk-scripting # Linux
Enable-ScheduledTask PunktfunkScripting # Windows
```
Open the [web console](/docs/web-console) and the plugin's page appears in the nav automatically —
that's the whole install. Each plugin is also fully configurable from a `config.json` for headless
hosts (see its README).
> Plugins are operator-installed code that runs as the host user — they can launch games and run
> commands. Install only plugins you trust, from a registry you control.
## ROM Manager
`@punktfunk/plugin-rom-manager` — point it at your ROM directories and it scans them, matches each
platform to an installed emulator, fetches box art (SteamGridDB, or the keyless libretro thumbnails),
and reconciles the result into your library as the `rom-manager` provider. ~25 built-in platforms
(NES through Switch, PS1/2/PSP, Dreamcast, and more), per-game overrides, and a console page to
configure it all.
Install it as [above](#installing-a-plugin), then add a root or two — from the console's **ROM
Manager** page, or in `~/.config/punktfunk/rom-manager/config.json`:
```jsonc
{
"roots": [
{ "dir": "/mnt/roms/snes", "platform": "snes" },
{ "dir": "/mnt/roms/ps1", "platform": "ps1", "excludes": ["*.sav"] }
],
"art": { "provider": "auto", "steamGridDbKey": "" }
}
```
Full options and the platform/emulator list are in
[the plugin's repo](https://git.unom.io/unom/punktfunk-plugin-rom-manager).
## Playnite
`@punktfunk/plugin-playnite` — mirrors your **[Playnite](https://playnite.link)** library (Steam,
GOG, Epic, Xbox, itch, emulators, manually-added games — everything Playnite manages) into your
library. Launching a title hands it back to Playnite, which performs the real launch, so there are no
per-store launch commands to maintain. Covers are served by the host, so it scales to large libraries.
Because Playnite keeps its library locked while running, this plugin has **two parts**, both on the
Windows host:
1. **The plugin** (on the host) — install `@punktfunk/plugin-playnite` exactly as
[above](#installing-a-plugin).
2. **The Punktfunk Sync extension** (in Playnite) — download `punktfunk-sync.pext` from the
[plugin's builds](https://git.unom.io/unom/punktfunk-plugin-playnite/actions) and **double-click
it** to install it in Playnite like any add-on, then restart Playnite once.
Open the console's **Playnite** page — it shows "Exporter connected", and your games sync within
seconds of any library change. Filters (installed-only, per-store, hidden) live on that page or in
`~/.config/punktfunk/playnite/config.json`. Details are in
[the plugin's repo](https://git.unom.io/unom/punktfunk-plugin-playnite).
## Writing your own
A plugin is a small TypeScript module built on `@punktfunk/host` (`definePlugin`), supervised by the
runner. Start from the [SDK README](https://git.unom.io/unom/punktfunk/src/branch/main/sdk) — or the
two plugins above, which are worked examples of the same shape.
+185
View File
@@ -0,0 +1,185 @@
---
title: Plugins
description: First-party plugins that sync your ROM collection or your Playnite library into the game library — and how to install them.
---
Plugins extend the host through the **scripting runner** (see [Events & hooks](/docs/automation)). A
plugin runs alongside the host, reconciles titles into your **game library** as a provider — so they
appear in the grid on every client — and can add its own page to the [web console](/docs/web-console).
Two first-party plugins today:
| Plugin | What it does |
|---|---|
| **ROM Manager** | Scans your ROM directories, matches each platform to an installed emulator, and syncs them into the library with box art. |
| **Playnite** | Mirrors your [Playnite](https://playnite.link) library — every store and emulator it manages — into the library, launched back through Playnite. |
## Installing a plugin
Two commands: install the plugin, then turn the runner on. The host CLI handles the rest — creating
the plugins directory, pointing it at the package registry, and starting the supervisor.
<Tabs items={['Linux', 'Windows']}>
<Tab value="Linux">
```sh
punktfunk-host plugins add playnite # or: rom-manager
punktfunk-host plugins enable # turn the runner on (once)
```
</Tab>
<Tab value="Windows">
Run these from an **elevated** PowerShell — right-click **PowerShell** → **Run as administrator**.
The plugins directory lives under `%ProgramData%\punktfunk`, which is admin-owned, and the runner
task runs as SYSTEM.
```powershell
punktfunk-host plugins add playnite # or: rom-manager
punktfunk-host plugins enable # turn the runner on (once)
```
If `punktfunk-host` isn't found, open a **new** terminal (the installer adds it to `PATH`), or use
the full path: `& "$env:ProgramFiles\punktfunk\punktfunk-host.exe" plugins add playnite`.
</Tab>
</Tabs>
Open the [web console](/docs/web-console) and the plugin's page appears in the nav automatically —
that's the whole install.
The runner is **opt-in**: `plugins add` installs, `plugins enable` turns it on. You only need
`enable` once — plugins you add later are picked up automatically.
### The rest of the commands
| Command | What it does |
|---|---|
| `punktfunk-host plugins add <name…>` | Install one or more plugins. |
| `punktfunk-host plugins remove <name…>` | Uninstall. |
| `punktfunk-host plugins list` | List what's installed, with versions. |
| `punktfunk-host plugins enable` | Enable + start the runner. |
| `punktfunk-host plugins disable` | Stop + disable the runner. |
| `punktfunk-host plugins status` | Is the runner enabled and running? |
A bare name resolves to the first-party package — `playnite` installs `@punktfunk/plugin-playnite`.
A scoped (`@scope/pkg`) or `punktfunk-plugin-*` name is used verbatim, so third-party plugins work
the same way.
> Plugins are operator-installed code that runs as the host user — they can launch games and run
> commands. Install only plugins you trust, from a registry you control.
## ROM Manager
`@punktfunk/plugin-rom-manager` — point it at your ROM directories and it scans them, matches each
platform to an installed emulator, fetches box art (SteamGridDB, or the keyless libretro thumbnails),
and reconciles the result into your library as the `rom-manager` provider. ~25 built-in platforms
(NES through Switch, PS1/2/PSP, Dreamcast, and more), per-game overrides, and a console page to
configure it all.
```sh
punktfunk-host plugins add rom-manager
```
Then add a root or two — from the console's **ROM Manager** page, or in the config file:
<Tabs items={['Linux', 'Windows']}>
<Tab value="Linux">
`~/.config/punktfunk/rom-manager/config.json`:
```jsonc
{
"roots": [
{ "dir": "/mnt/roms/snes", "platform": "snes" },
{ "dir": "/mnt/roms/ps1", "platform": "ps1", "excludes": ["*.sav"] }
],
"art": { "provider": "auto", "steamGridDbKey": "" }
}
```
</Tab>
<Tab value="Windows">
`%ProgramData%\punktfunk\rom-manager\config.json`:
```jsonc
{
"roots": [
{ "dir": "D:\\roms\\snes", "platform": "snes" },
{ "dir": "D:\\roms\\ps1", "platform": "ps1", "excludes": ["*.sav"] }
],
"art": { "provider": "auto", "steamGridDbKey": "" }
}
```
</Tab>
</Tabs>
Full options and the platform/emulator list are in
[the plugin's repo](https://git.unom.io/unom/punktfunk-plugin-rom-manager).
## Playnite
`@punktfunk/plugin-playnite` — mirrors your **[Playnite](https://playnite.link)** library (Steam,
GOG, Epic, Xbox, itch, emulators, manually-added games — everything Playnite manages) into your
library. Launching a title hands it back to Playnite, which performs the real launch, so there are no
per-store launch commands to maintain. Covers are served by the host, so it scales to large libraries.
Playnite is Windows-only, so both halves of this one live on the **Windows host**. Because Playnite
keeps its library locked while running, there are **two parts**:
1. **The plugin** — from an elevated PowerShell:
```powershell
punktfunk-host plugins add playnite
punktfunk-host plugins enable
```
2. **The Punktfunk Sync extension** (in Playnite) — download `punktfunk-sync.pext` from the
[plugin's builds](https://git.unom.io/unom/punktfunk-plugin-playnite/actions) and **double-click
it** to install it in Playnite like any add-on, then restart Playnite once.
Open the console's **Playnite** page — it shows "Exporter connected", and your games sync within
seconds of any library change. Filters (installed-only, per-store, hidden) live on that page or in
`%ProgramData%\punktfunk\playnite\config.json`. Details are in
[the plugin's repo](https://git.unom.io/unom/punktfunk-plugin-playnite).
## Troubleshooting
**`punktfunk-host: command not found`** — on Windows, open a new terminal so it picks up the
installer's `PATH` change, or call the exe by full path. On Linux the host package installs it to
`/usr/bin/punktfunk-host`.
**"the plugin runner isn't installed"** — the runner ships as its own package. On Debian/Ubuntu:
`sudo apt install punktfunk-scripting`. On Windows, re-run the installer and keep the scripting
component.
**The plugin doesn't show up in the console** — check the runner is actually running with
`punktfunk-host plugins status`, then look at its log:
<Tabs items={['Linux', 'Windows']}>
<Tab value="Linux">
```sh
journalctl --user -u punktfunk-scripting -f
```
</Tab>
<Tab value="Windows">
The runner task doesn't write a log file, so run it in the foreground to watch it start your
plugins (stop it with <kbd>Ctrl</kbd>+<kbd>C</kbd>):
```powershell
& "$env:ProgramFiles\punktfunk\bun\bun.exe" "$env:ProgramFiles\punktfunk\scripting\runner-cli.js"
```
</Tab>
</Tabs>
## Writing your own
A plugin is a small TypeScript module built on `@punktfunk/host` (`definePlugin`), supervised by the
runner. Start from the [SDK README](https://git.unom.io/unom/punktfunk/src/branch/main/sdk) — or the
two plugins above, which are worked examples of the same shape.
+4
View File
@@ -1,4 +1,5 @@
import defaultMdxComponents from 'fumadocs-ui/mdx'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
import type { MDXComponents } from 'mdx/types'
import BitrateCalculator from '@/components/BitrateCalculator'
@@ -7,6 +8,9 @@ export function getMDXComponents(components?: MDXComponents) {
...defaultMdxComponents,
// Custom components usable in any .md/.mdx without a per-file import.
BitrateCalculator,
// Per-platform instructions: <Tabs items={['Linux', 'Windows']}><Tab value="Linux">…
Tabs,
Tab,
...components,
} satisfies MDXComponents
}