import type { PluginManifest } from '@@/plugins/manifest'; /** * Transcribe — Officer's speech-to-text, talking to a self-hosted whisper.cpp server. * * ── What this plugin is, and what it is not ── * * It does NOT run Whisper, and it does not ship a model. You point it at a whisper.cpp `/inference` * server you already run (the same container the platform used to talk to directly) and, optionally, at * a diarization worker beside it. Nothing is transcribed here — the sidecar's job is the request shape * (detect the language, then transcribe faithfully in it, tuned for one person talking into a phone) * and the pipeline around it: probe whether the recording is a conversation, label the speakers when it * is, and queue multi-user traffic fairly across the two servers. * * Named for the feature, not the implementation — the same reasoning as `photos` not `immich`. Swapping * the backend for a different `/inference`-compatible server would not move the mount point or the route * shape. * * ── Where the connection lives ── * * A single URL, in a JSON file under `DATA_PATH/transcribe/`, not in Postgres and not in this * repository. There is only ever one Whisper server for the whole install — unlike `photos_config` or * `jellyfin_servers`, nobody has a reason to keep more than one — so a file the owner can also edit by * hand is simpler than a table with one row. See `sidecar/config.ts`. * * ── No `osDependencies` ── * * Nothing shells out. The sidecar reaches whisper.cpp with `fetch`, the same as the code it replaces did. * * ── No `web/`, and no settings screen of its own ── * * This plugin ships no dashboard route and no dock tile — `plugin.web` gates the tile, and a * backend-only plugin has nothing to pin one to. It is reached from a task's right-click menu and by an * agent that has read its skill, not by a screen. */ export const manifest: PluginManifest = { publisher: 'officerdev', version: '1.0.0', platform: '>=1.0.0', label: 'Transcribe', summary: 'Speech-to-text over your own whisper.cpp server', icon: 'AudioLines', color: '#f97316', /** * The whisper.cpp server this talks to. Declared rather than rendered: this plugin ships no `web/`, so * without a declaration there is nowhere to set it and the only way in is an HTTP call somebody has to * know about — which is the state it shipped in. * * The platform renders the form and PUTs to this plugin's own `/config`. It stores nothing: the URL * lives where this plugin keeps it, and validation against the server still happens here. */ settings: [ { key: 'url', label: 'Whisper server', hint: 'The whisper.cpp server to transcribe through. Checked before it is saved.', type: 'url', required: true, placeholder: 'http://127.0.0.1:8178', }, { key: 'workerUrl', label: 'Diarization worker', hint: 'The torch stage worker that detects conversations and labels speakers. Optional — ' + 'without it, recordings still transcribe but nothing is speaker-labeled.', type: 'url', required: false, placeholder: 'http://127.0.0.1:8183', }, ], permissions: [ { key: 'transcribe', label: 'Transcribe', description: 'Turn audio into text over your own whisper.cpp server', }, ], };