plugins declare the host binaries they need, and the installer checks
Offscale was self-sufficient. Music is not — it shells out to ffmpeg and ffprobe — and the way it fails without them is the reason this is a check rather than a line in a README. It does not fail. Missing ffprobe means the indexer catches the spawn error and returns a track carrying its filename and nothing else: no title, artist, album, duration or embedded lyrics. It then walks the whole library, writes a complete cache tree and reports success. Five swallowed catches, no log, no counter, and the only tell is coversSaved: 0 in a report nobody reads. So `osDependencies` is a manifest field: the binary to probe on PATH, why it is needed, and a package name per package manager. The shape is taken from scripts/setup-old/setup.sh rather than invented — probe the binary, case on $PM — and the names are per-manager rather than canonical-with-overrides because lib/packages.sh already recorded why that indirection was rejected. Probing the binary is what makes "built-in on this OS" free: on PATH means the package map is never consulted. Four decisions worth naming. Missing and uninstallable REFUSES the install, first, before a table is created or a row written — so there is nothing to undo, and the alternative is a plugin that installs, answers 200 and quietly produces nothing. The status is on GET /api/plugins and rendered before the button, because the owner is deciding whether to let the server run a package manager as root and that needs answering first. Installing by hand and watching it flip to present is the escape hatch on a machine without passwordless sudo. Package names get a deliberately narrow regex and reach Bun.spawn as an argv ARRAY, never a shell. Both halves are load-bearing: the regex means a metacharacter cannot get there, argv means it would be an argument rather than syntax if it did. Narrower than package managers actually accept — no `:`, no `+` version pins — because a plugin needing one wants a conversation. Success is OBSERVED, not inferred: after installing, the binaries are re-probed. A package manager exiting 0 having installed something that does not provide the binary is exactly the failure this exists to catch. installCommand mirrors lib/packages.sh's pkg_install_now exactly, including apt's non-interactive environment, so there is one definition of "install a package" rather than two that drift. sudo always gets -n: under PM2 a password prompt is not a slow path, it is a hang. brew never escalates. Verified live. ffmpeg and ffprobe were absent on this machine all evening; the page showed both missing with the exact root command, the install streamed `dependencies: installing ffmpeg with apt` then `ffprobe, ffmpeg now on PATH`, and X-Audio-Duration appeared on a stream response for the first time. The refusal path was exercised against a temporary probe dependency: HTTP 400, steps: [], reason named. THIS CHANGED THE MACHINE: ffmpeg 6.1.1-3ubuntu5 is now installed via apt. Found on the way: a manifest is read once per process. Discovery does `await import()` and the module cache holds it, so editing a manifest changes nothing until pm2 restart officer — including `outdated`. Cost ten minutes and is now in the runbook. bunx tsgo clean. 797 tests, 787 pass, 7 fail — the same seven, +25 new.
This commit is contained in:
@@ -39,10 +39,10 @@ import type { PluginManifest } from '@@/plugins/manifest';
|
||||
// install and gone at uninstall. So the seam switches itself off with the plugin, with no code path
|
||||
// that knows why.
|
||||
//
|
||||
// ── Host dependencies a manifest cannot declare ──
|
||||
// ── Host dependencies ──
|
||||
//
|
||||
// `ffmpeg` and `ffprobe` must be on PATH: tag reading, cover compression and video poster frames. There
|
||||
// is no field for a host binary and inventing one for this would be a field nothing else reads.
|
||||
// Music is the plugin that made `osDependencies` exist. Offscale was self-sufficient, so until this one
|
||||
// there was nothing to declare and no reason to build the field — see ./PLUGIN.md.
|
||||
export const manifest: PluginManifest = {
|
||||
publisher: 'officerdev',
|
||||
version: '1.0.0',
|
||||
@@ -82,4 +82,25 @@ export const manifest: PluginManifest = {
|
||||
readOnlyWrites: ['/favorites', '/now-playing', '/playlists', '/queue'],
|
||||
},
|
||||
],
|
||||
|
||||
// Both come from one package everywhere, which is luck rather than a rule — hence a name per manager
|
||||
// rather than one canonical name. `packages.sh` records why that indirection was rejected.
|
||||
//
|
||||
// They are declared SEPARATELY even so, because the platform probes binaries and these two fail
|
||||
// differently. Losing `ffprobe` is the quiet one: the indexer catches the spawn error and returns a
|
||||
// track carrying its filename and nothing else — no title, artist, album, duration or embedded
|
||||
// lyrics — then reports success. Losing `ffmpeg` costs cover art and video poster frames, which is at
|
||||
// least visible. Naming both means the owner is told which of the two they are missing.
|
||||
osDependencies: [
|
||||
{
|
||||
binary: 'ffprobe',
|
||||
reason: 'Reads tags, duration and embedded lyrics. Without it every track indexes as a bare filename.',
|
||||
packages: { apt: 'ffmpeg', pacman: 'ffmpeg', dnf: 'ffmpeg', brew: 'ffmpeg' },
|
||||
},
|
||||
{
|
||||
binary: 'ffmpeg',
|
||||
reason: 'Compresses cover art for phones and grabs poster frames from videos.',
|
||||
packages: { apt: 'ffmpeg', pacman: 'ffmpeg', dnf: 'ffmpeg', brew: 'ffmpeg' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user