From 1d41a9c6ef03e4ac28729a12942ce88e51d870e7 Mon Sep 17 00:00:00 2001 From: collissimo <148455189+collissimo@users.noreply.github.com> Date: Fri, 13 Nov 2026 20:14:36 +0100 Subject: [PATCH] fix(dictation): CLI executable builds again with local dictation The single-executable build refuses package imports it cannot resolve, and local dictation imported koffi and transcribe-cpp that way, so the fork release failed once it started building the CLI archive. Both now load from disk through require, as node-pty does. Co-Authored-By: Claude Fable 5.2 --- apps/server/src/dictation/localDictation.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/server/src/dictation/localDictation.ts b/apps/server/src/dictation/localDictation.ts index 7edb793c18ee6..cd6d85d589ad3 100644 --- a/apps/server/src/dictation/localDictation.ts +++ b/apps/server/src/dictation/localDictation.ts @@ -7,6 +7,7 @@ import type * as TranscribeCpp from "transcribe-cpp"; import * as NodeServices from "@effect/platform-node/NodeServices"; import * as Effect from "effect/Effect"; import * as Path from "effect/Path"; +import * as NodeModule from "node:module"; import * as NodePath from "node:path"; import { CloudDictation } from "./cloudDictation.ts"; @@ -19,6 +20,9 @@ import { import { findModel, findVadModel, modelDirectories } from "./modelFiles.ts"; import { DEFAULT_STRUCTURE_CODEWORDS, removeFillers, splitCodeword } from "./transcriptText.ts"; +// A single-executable cannot resolve package imports; require reads both native packages from disk. +const requireNative = NodeModule.createRequire(import.meta.url); + const IDLE_UNLOAD_MS = 15 * 60_000; export const HOUSEKEEPING_INTERVAL_MS = 30_000; // oxlint-disable-next-line t3code/no-global-process-runtime -- Also runs in the desktop worker, which has no Effect runtime. @@ -46,7 +50,7 @@ const disableVulkanCoopmat = async (): Promise => { const name = "GGML_VK_DISABLE_COOPMAT"; if (process.env[name] !== undefined) return; if (PLATFORM === "win32") { - const koffi = (await import("koffi")).default; + const koffi = requireNative("koffi") as typeof import("koffi"); koffi.load("ucrtbase.dll").func("int _putenv_s(str, str)")(name, "1"); } process.env[name] = "1"; @@ -55,7 +59,12 @@ const disableVulkanCoopmat = async (): Promise => { // transcribe-cpp dlopens a native library; it is only loaded once someone dictates. const loadTranscribeCpp = async (): Promise => { await disableVulkanCoopmat(); - const module = (await import("transcribe-cpp")) as TranscribeModule; + // transcribe-cpp only exports an import entry, so require takes its file on disk. + const packagePath = NodeModule.findPackageJSON("transcribe-cpp", import.meta.url); + if (!packagePath) throw new Error("The transcribe-cpp package is not installed."); + const module = requireNative( + NodePath.join(NodePath.dirname(packagePath), "dist/index.js"), + ) as TranscribeModule; if (!process.env.TRANSCRIBE_LIBRARY) { // In the desktop app the package sits in an asar archive, which the OS loader cannot read; // its DLLs are unpacked beside the archive.