| .. | ||
| addon | ||
| bin | ||
| scripts | ||
| src | ||
| whisper.cpp@4979e04f5d | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| WHISPER_CPP_VERSION | ||
@amical/whisper-wrapper
This package wraps the whisper.cpp Node addon so the desktop app can call into
Whisper from a forked worker process. The build and runtime layers are tuned for
the desktop pipeline; the notes below capture the important knobs and the
reasoning behind them.
Build workflow
pnpm install(postinstall) runsbin/build-addon.jsvia CMake.js and drops the resultingwhisper.nodeintonative/<platform-arch(-tag)>/.pnpm --filter @amical/whisper-wrapper build:nativerebuilds the default variants for this platform (Metal + CPU on macOS, CPU elsewhere).pnpm --filter @amical/whisper-wrapper build:native:cudabuilds an extrawin32-x64-cudabinary alongside the regularwin32-x64fallback. Install the CUDA toolkit (12.x tested) before running it.- Every macOS build is ad-hoc signed (
codesign -s -) so Electron/Node can load it without crashing. - Each variant is produced as a single
.nodebinary. We force static libraries (GGML_STATIC=ON,BUILD_SHARED_LIBS=OFF) so all ggml/whisper code is linked directly into the addon—no sidecar.dylib/.dllfiles ship at runtime. - The full CMake build directory is deleted after each variant so Electron
Forge/Squirrel never sees the long
CMakeFiles/...paths that blew past Windows’ MAX_PATH limit during packaging.
GPU/CPU fallback
resolveBinding() in src/loader.ts no longer throws if the first candidate
fails. loadBinding() walks the list:
platform-arch-metalplatform-arch-openblasplatform-arch-cudaplatform-archcpu-fallback
If require() raises ERR_DLOPEN_FAILED (missing runtime, wrong driver, etc.)
it logs a warning and tries the next candidate. That lets us ship CUDA/Metal
binaries alongside CPU ones without breaking installs that lack the GPU stack.
GGML_NATIVE on macOS arm64
GitHub’s hosted macOS runners expose i8mm but clang refuses to emit the
vmmlaq_s32 intrinsic when -mcpu=native is passed, so the build dies in
ggml-cpu/arch/arm/quants.c. CI therefore exports GGML_NATIVE=OFF before
calling the build scripts. Locally you can flip it back on if your toolchain
supports those instructions:
GGML_NATIVE=ON pnpm --filter @amical/whisper-wrapper build:native
Leave it off in CI unless you control the runner.
Custom targets
WHISPER_TARGETS lets you override which variants to build. The value is a
comma-separated list of directory names that should map to native/<name>.
Examples:
WHISPER_TARGETS="linux-x64-gnu" pnpm --filter @amical/whisper-wrapper build:native
WHISPER_TARGETS="win32-x64-cuda,win32-x64" pnpm --filter @amical/whisper-wrapper build:native
Absent overrides the script builds the Metal variant (on macOS) followed by the plain CPU build.
Runtime API
src/index.ts exposes a minimal class that mirrors the desktop worker protocol:
new Whisper(modelPath, { gpu?: boolean })await whisper.load()(no-op placeholder)await whisper.transcribe(audioOrNull, options)await whisper.free()
If you pass null (and a fname_inp in options) the addon reads the audio
file directly, matching the CLI smoke tests.
Local expectations
whisper.cppis tracked as a submodule underpackages/whisper-wrapper/.cmake-js/node/pnpmmust be installed (the workspace root sets the required versions).- The build creates
.cmake-js/and.home/caches inside the package; they’re ignored in git.
For any tweaks (new build targets, additional fallbacks, etc.) update this file so the CI configuration stays discoverable.