Added a whisper.cpp auto lookup

This commit is contained in:
WiseGuru 2026-06-16 12:25:15 -07:00
parent c74f74abc9
commit 7ab2bc287d
2 changed files with 50 additions and 3 deletions

View file

@ -120,7 +120,9 @@ Per-adapter behavior: [src/transcription/assemblyai.ts](src/transcription/assemb
## Local whisper.cpp host (desktop)
[src/whisper-host.ts](src/whisper-host.ts) exposes the `WhisperHost` class. The plugin instantiates one in `onload` and stops it in `onunload`. Configuration lives at `GlobalSettings.localWhisper = { binaryPath, modelPath, port, extraArgs }` — all user-supplied. No discovery, no PATH lookup, no auto-download.
[src/whisper-host.ts](src/whisper-host.ts) exposes the `WhisperHost` class. The plugin instantiates one in `onload` and stops it in `onunload`. Configuration lives at `GlobalSettings.localWhisper = { binaryPath, modelPath, port, extraArgs }` — all user-supplied. No automatic discovery, no PATH lookup at runtime, no auto-download. The one (user-initiated) convenience is the **Auto-detect** button beside the Binary path field in settings: `detectWhisperBinary()` in [src/settings/tab.ts](src/settings/tab.ts) lazy-requires `fs`/`os`/`path` (same `window.require` + `Platform.isDesktop` guard as `whisper-host.ts`) and returns the first existing candidate from the build script's install locations (`~/.local/bin/whisper-server`, then `~/.local/share/whisper.cpp/build/bin/whisper-server`) followed by common system/Homebrew paths (`/usr/local/bin`, `/opt/homebrew/bin`, `/usr/bin`); `.exe` suffix on Windows. It only checks existence when clicked and never runs the binary. The `binaryPath` default stays `''` (the setup-card gating depends on it), so this is a fill-in helper, not a baked default.
The companion CLI installer is [scripts/build-whisper-linux.sh](scripts/build-whisper-linux.sh): it clones/builds whisper.cpp into `~/.local/share/whisper.cpp` (override `--source-dir`), symlinks the binary to `~/.local/bin/whisper-server` (override `--prefix`, skip with `--no-symlink`), and prints the final Binary path to paste into settings. Keep the Auto-detect candidate list in sync with this script's default paths.
`start()` validates the binary and model paths exist via `fs.existsSync`, probes the port via `net.createServer().listen(port)` to detect conflicts, spawns the user's whisper-server with `child_process.spawn`, captures stdout/stderr into a 1 MB ring buffer, then polls `net.createConnection` against the port every 250 ms for up to 5 s before declaring `'running'`. Any failure transitions status to `'crashed'` with the log tail surfaced in the error message. `stop()` sends SIGTERM, waits up to 3 s, then SIGKILL.

View file

@ -27,6 +27,38 @@ import { PassphraseModal } from '../ui/passphrase-modal';
// Sentinel value for the "Custom..." entry in a model dropdown; never written to config.model.
const CUSTOM_MODEL_OPTION = '__rewrite_custom__';
// Probe the locations scripts/build-whisper-linux.sh installs whisper-server to,
// plus the common system/Homebrew paths, and return the first that exists.
// Desktop-only (lazy-requires fs/os via the same window.require pattern as whisper-host.ts);
// returns null on mobile, when require is unavailable, or when nothing is found.
function detectWhisperBinary(): string | null {
if (!Platform.isDesktop) return null;
try {
const req = (window as unknown as { require?: (m: string) => unknown }).require;
if (typeof req !== 'function') return null;
const fs = req('fs') as { existsSync(p: string): boolean };
const os = req('os') as { homedir(): string };
const path = req('path') as { join(...parts: string[]): string };
const home = os.homedir();
const exe = Platform.isWin ? 'whisper-server.exe' : 'whisper-server';
const candidates = [
// Build-script defaults: symlink first, then the built binary.
path.join(home, '.local', 'bin', exe),
path.join(home, '.local', 'share', 'whisper.cpp', 'build', 'bin', exe),
// Common system / Homebrew locations.
path.join('/usr', 'local', 'bin', exe),
path.join('/opt', 'homebrew', 'bin', exe),
path.join('/usr', 'bin', exe),
];
for (const candidate of candidates) {
if (fs.existsSync(candidate)) return candidate;
}
return null;
} catch {
return null;
}
}
// Cleanup output runs ~256 tokens per minute of speech (≈150 wpm × ~1.3 tokens/word,
// padded ~20% for headings, bullets, and Speaker labels in structured/diarized notes).
// The "Maximum note length" dropdown maps these minute presets onto config.maxTokens;
@ -718,14 +750,27 @@ export class ReWriteSettingTab extends PluginSettingTab {
new Setting(parent)
.setName('Binary path')
.setDesc('Absolute path to whisper-server (or whisper-server.exe on Windows).')
.setDesc('Absolute path to whisper-server (or whisper-server.exe on Windows). The Linux build script (scripts/build-whisper-linux.sh) installs it to ~/.local/bin/whisper-server by default; use Auto-detect to fill this in.')
.addText((t) => {
t.setValue(cfg.binaryPath);
t.setPlaceholder('/usr/local/bin/whisper-server');
t.setPlaceholder('~/.local/bin/whisper-server');
t.onChange(async (v) => {
cfg.binaryPath = v;
await this.commit();
});
})
.addExtraButton((b) => {
b.setIcon('search').setTooltip('Look for whisper-server in the build script\'s install locations').onClick(() => void this.runGuardedButton(b, async () => {
const found = detectWhisperBinary();
if (found) {
cfg.binaryPath = found;
await this.commit();
new Notice(`ReWrite: found whisper-server at ${found}`);
this.display();
} else {
new Notice('ReWrite: no whisper-server found in the usual install locations. Set the path manually.');
}
}));
});
new Setting(parent)