cliamp moves into the plugin, and the platform loses its last music file
The owner read the code and asked why `plugins/music/api/router.ts` was three
lines importing `@@/api/music/router` — platform code that knows the string
'music'. He was right, and tracing it found the justification was hollow.
The chain: server.tsx:20 imported the cliamp relay's two exports, which are
used only on commented-out lines; so the relay's functions were never invoked;
so its call to getMusicServerWsUrl never ran; and the file's other export,
getMusicServerUrl, had no consumers at all. A dead import held a music-named
file in the platform, and I documented that as a "seam" last night after
checking the import existed and stopping there.
Everything cliamp now lives in plugins/music/cliamp/:
sidecar/music/{cliamp-ws,pulse-audio}.ts, asoundrc, the test
api/cliamp/relay.ts
apps/FileBrowser/{CliampPanel,AudioStreamPlayer}.tsx
src/servers/sidecar/music/, src/servers/api/cliamp/ and src/servers/api/music/
are gone. server.tsx has no cliamp import, provider name, handler entry or
route. The platform contains no file named for music or cliamp.
Two of the things that moved were live, not inert.
The file browser's `Play` context-menu item, on any audio file or folder, set
?play= and rendered a cliamp terminal pointed at /api/cliamp/ws — a route that
upgraded into a handlers entry that was commented out, so handlers[provider]!
asserted non-null on undefined. Using that menu item crashed the socket
handler. Removed: the action, the layout, the panel wiring and both menu
entries. Verified the routes now 404 rather than crash.
That closed the totality drift as a side effect. server.tsx's route table and
its handlers map agree again for the first time since 2026-08-13, and
registry.test.ts now asserts it rather than pinning the hole.
The proxy is built in the plugin now, and its prefix is DERIVED. It was the
literal '/api/music', which the proxy uses to strip characters off the path —
correct only because mountPrefix returns /music for a first-party publisher.
The same plugin published by anyone else mounts at /api/p/<publisher>/music and
would have forwarded /alice/music/stream to a sidecar expecting /stream. A
latent bug only third parties would ever hit, and a quiet violation of the rule
that mountPrefix is the one function allowed to know about provenance. Offscale
has the identical hardcode and still needs it.
Still open there: appName is passed as a literal, because a plugin's router
cannot see its own directory name — the platform imports the module and reads
`router`, so there is nowhere to inject it. The fix is a factory the installer
calls with the plugin's identity.
Plugin backend coupling is down to 7 imports, all of them "a plugin talks to
its host": data-path, sidecar/connect, sidecar/protocol, officer-url, the
manifest type, officerdb/db and the users.id FK. Nothing music-shaped left.
bunx tsgo clean. 797 tests, 787 pass, same 7 pre-existing failures. Verified
live: manifest 200, favorites 200, stream 206, /api/cliamp/ws 404.
This commit is contained in:
+41
-13
@@ -1,18 +1,46 @@
|
||||
import { musicRouter } from '@@/api/music/router';
|
||||
import { createSidecarProxy } from '@@/sidecar/create-proxy';
|
||||
import { mountPrefix } from '@@/plugins/manifest';
|
||||
import { manifest } from '../manifest';
|
||||
|
||||
// /api/music/* — auth, then forward to officer-music.
|
||||
// /api/music/* — auth, then forward to officer-music. No routes of its own and no music knowledge here:
|
||||
// the whole contract lives in ../sidecar/index.ts, which is where the routes actually are.
|
||||
//
|
||||
// ── Why this re-exports the platform's proxy instead of creating its own ──
|
||||
// ── This used to live in the platform, and that was the bug ──
|
||||
//
|
||||
// `src/servers/api/music/router.ts` has to stay behind: `api/cliamp/relay.ts` imports
|
||||
// `getMusicServerWsUrl` from it to pipe the cliamp player socket to this sidecar, and cliamp is
|
||||
// deliberately out of scope — it is a second playback path that the platform still owns.
|
||||
// Until 2026-08-15 the proxy was constructed in `src/servers/api/music/router.ts` — PLATFORM code that
|
||||
// knew the string 'music' — and this file merely re-exported it. The justification was that
|
||||
// `api/cliamp/relay.ts` imported `getMusicServerWsUrl` from it, so it could not move.
|
||||
//
|
||||
// So the proxy already exists, and building a SECOND `createSidecarProxy({ name: 'music' })` here would
|
||||
// mean two subscribers to the one-shot `music:server` port announcement. Both would work today, and the
|
||||
// first reconnect where only one of them was listening would produce a 503 nobody could explain. One
|
||||
// proxy, one subscription, mounted by whoever needs it.
|
||||
// That justification was three layers of nothing. The relay's functions were only reachable through
|
||||
// `handlers` entries in server.tsx that were commented out, and its own import there was unused. A dead
|
||||
// import held a music-named file in the platform, and the second export on it (`getMusicServerUrl`) had
|
||||
// no callers at all. The relay now lives in ../cliamp/ and takes its URL from here.
|
||||
//
|
||||
// The seam is one file, and it is inert when this plugin is not installed: the router is only reachable
|
||||
// once `mountPrefix()` puts it under `/api/music`, which only happens for an installed, enabled plugin.
|
||||
export const router = musicRouter;
|
||||
// ── The prefix is DERIVED, not written ──
|
||||
//
|
||||
// It was the literal '/api/music', and that is wrong in a way that only shows up for someone else's
|
||||
// plugin. The proxy strips `prefix.length` characters to build the sidecar path, so a hardcoded
|
||||
// '/api/music' (10 chars) is correct only because `mountPrefix` happens to return `/music` for a
|
||||
// first-party publisher. The same plugin published by anyone else mounts at `/api/p/<publisher>/music`
|
||||
// and would forward `/alice/music/stream` to a sidecar expecting `/stream`.
|
||||
//
|
||||
// `mountPrefix` is the ONE function allowed to know about provenance, so the prefix comes from it. A
|
||||
// literal here is that rule being broken quietly, which is exactly how first-party and third-party
|
||||
// become two systems with only one of them tested.
|
||||
//
|
||||
// `appName` is passed as a literal because this file cannot see its own directory name. That is a real
|
||||
// gap — the platform imports `router.ts` and reads `router`, so there is nowhere to inject it — and the
|
||||
// day a plugin's router needs its own identity for anything else, `api/router.ts` should export a
|
||||
// factory the installer calls instead. Recorded rather than worked around.
|
||||
const proxy = createSidecarProxy({
|
||||
name: 'music',
|
||||
prefix: `/api${mountPrefix({ appName: 'music', manifest })}`,
|
||||
// A from-scratch reindex holds the connection open for minutes with no bytes flowing; the default 60s
|
||||
// idle drop would kill it. Applied to the whole prefix — the proxy must not know which routes are slow.
|
||||
timeoutSeconds: 1800,
|
||||
});
|
||||
|
||||
export const router = proxy.router;
|
||||
|
||||
/** The sidecar as a `ws://` base. Used by ../cliamp/relay.ts, and by nothing else. */
|
||||
export const getMusicServerWsUrl = proxy.getWsUrl;
|
||||
|
||||
Reference in New Issue
Block a user