swictation
Version:
Cross-platform voice-to-text dictation for Linux and macOS with GPU acceleration (NVIDIA CUDA/CoreML), Secretary Mode (60+ natural language commands), Context-Aware Meta-Learning, and pure Rust performance. Meta-package that automatically installs platfor
284 lines (254 loc) • 8.56 kB
JavaScript
/**
* Binary Path Resolution Module
*
* Finds the correct binaries and libraries from the installed platform package.
* Works with postinstall.js GPU detection to ensure correct library paths.
*
* Platform packages:
* - @agidreams/linux-x64: Linux x86_64 binaries + ONNX Runtime
* - @agidreams/darwin-arm64: macOS ARM64 binaries + CoreML ONNX Runtime
*
* After platform package is found, postinstall.js will:
* - Detect GPU (NVIDIA/AMD/Intel)
* - Check VRAM and system RAM
* - Download GPU-specific libraries to platformPackage.libDir
* - Recommend appropriate model sizes
*/
const fs = require('fs');
const path = require('path');
const os = require('os');
const { execSync } = require('child_process');
/**
* Platform detection
*/
function detectPlatform() {
const platform = os.platform();
const arch = os.arch();
if (platform === 'linux' && arch === 'x64') {
return {
platform: 'linux',
arch: 'x64',
packageName: '@agidreams/linux-x64',
binaries: {
daemon: 'swictation-daemon',
ui: 'swictation-ui'
}
};
}
if (platform === 'darwin' && arch === 'arm64') {
return {
platform: 'darwin',
arch: 'arm64',
packageName: '@agidreams/darwin-arm64',
binaries: {
daemon: 'swictation-daemon',
ui: 'swictation-ui'
}
};
}
// Unsupported platform
return {
platform,
arch,
supported: false,
error: `Unsupported platform: ${platform}-${arch}. Swictation supports linux-x64 and darwin-arm64 only.`
};
}
/**
* Find the platform package in node_modules
*
* Searches multiple locations to handle:
* - Global installs: npm install -g swictation (sibling packages)
* - Local installs: npm install swictation (nested in node_modules)
* - Development: working within the repo
* - nvm installations
* - Custom npm prefix configurations
*/
function findPlatformPackage(packageName) {
const packageShortName = packageName.replace('@agidreams/', '');
// Helper to check if package exists at a given node_modules path
function checkNodeModules(nodeModulesDir) {
const scopeDir = path.join(nodeModulesDir, '@agidreams');
if (fs.existsSync(scopeDir)) {
const packageDir = path.join(scopeDir, packageShortName);
if (fs.existsSync(packageDir)) {
const packageJsonPath = path.join(packageDir, 'package.json');
if (fs.existsSync(packageJsonPath)) {
return packageDir;
}
}
}
return null;
}
// Strategy 1: Use npm root -g to find the definitive global path
// This handles nvm, custom prefixes, and all npm configurations correctly
try {
const npmRoot = execSync('npm root -g', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
if (npmRoot && fs.existsSync(npmRoot)) {
const result = checkNodeModules(npmRoot);
if (result) return result;
}
} catch (e) {
// npm root -g failed, continue with other strategies
}
// Strategy 2: Check for sibling package based on __dirname
// If we're at /path/to/node_modules/swictation/src,
// the platform package would be at /path/to/node_modules/@agidreams/linux-x64
let currentDir = __dirname;
for (let i = 0; i < 5; i++) {
const parentDir = path.dirname(currentDir);
// Check if parent is a node_modules directory
if (path.basename(parentDir) === 'node_modules') {
const result = checkNodeModules(parentDir);
if (result) return result;
}
currentDir = parentDir;
if (parentDir === currentDir) break;
}
// Strategy 3: Search upward for nested node_modules (local installs)
currentDir = __dirname;
for (let i = 0; i < 10; i++) {
const nodeModulesDir = path.join(currentDir, 'node_modules');
if (fs.existsSync(nodeModulesDir)) {
const result = checkNodeModules(nodeModulesDir);
if (result) return result;
}
const parentDir = path.dirname(currentDir);
if (parentDir === currentDir) break;
currentDir = parentDir;
}
// Strategy 4: Check common global npm locations and nvm paths
const globalPaths = [
'/usr/local/lib/node_modules',
'/usr/lib/node_modules',
path.join(os.homedir(), '.npm-global', 'lib', 'node_modules'),
path.join(os.homedir(), 'node_modules')
];
// Add nvm paths if NVM_DIR is set
if (process.env.NVM_DIR) {
// Try to find the current node version's node_modules
try {
const nodeVersion = process.version;
globalPaths.unshift(path.join(process.env.NVM_DIR, 'versions', 'node', nodeVersion, 'lib', 'node_modules'));
} catch (e) {
// Ignore
}
}
// Also check paths containing literal $HOME (broken npm config edge case)
const literalHomePath = path.join(os.homedir(), '$HOME', '.npm-global', 'lib', 'node_modules');
if (fs.existsSync(literalHomePath)) {
globalPaths.unshift(literalHomePath);
}
for (const globalPath of globalPaths) {
if (fs.existsSync(globalPath)) {
const result = checkNodeModules(globalPath);
if (result) return result;
}
}
return null;
}
/**
* Verify binaries exist in the platform package
*/
function verifyBinaries(binDir, binaries) {
const missing = [];
for (const [name, filename] of Object.entries(binaries)) {
const binaryPath = path.join(binDir, filename);
if (!fs.existsSync(binaryPath)) {
missing.push(filename);
}
}
return missing;
}
/**
* Resolve all paths for the current platform
*
* Returns:
* {
* platform: 'linux' | 'darwin',
* arch: 'x64' | 'arm64',
* packageName: '@agidreams/linux-x64' | '@agidreams/darwin-arm64',
* packageDir: '/path/to/node_modules/@agidreams/linux-x64',
* binDir: '/path/to/node_modules/@agidreams/linux-x64/bin',
* libDir: '/path/to/node_modules/@agidreams/linux-x64/lib',
* daemon: '/path/to/node_modules/@agidreams/linux-x64/bin/swictation-daemon',
* ui: '/path/to/node_modules/@agidreams/linux-x64/bin/swictation-ui'
* }
*
* libDir is where postinstall.js will download GPU-specific libraries:
* - Linux: libcudart.so, libcublas.so, etc. (based on GPU detection)
* - macOS: CoreML libraries (already included in ONNX Runtime)
*/
function resolveBinaryPaths() {
// Detect platform
const platformInfo = detectPlatform();
if (platformInfo.supported === false) {
throw new Error(platformInfo.error);
}
// Find platform package
const packageDir = findPlatformPackage(platformInfo.packageName);
if (!packageDir) {
throw new Error(
`Platform package ${platformInfo.packageName} not found.\n` +
`This usually means:\n` +
` 1. The package failed to install (check npm install output)\n` +
` 2. You're on an unsupported platform (${platformInfo.platform}-${platformInfo.arch})\n` +
` 3. npm's optionalDependencies installation failed\n\n` +
`Try reinstalling: npm install -g swictation --force`
);
}
// Build paths
const binDir = path.join(packageDir, 'bin');
const libDir = path.join(packageDir, 'lib');
// Verify binaries exist
const missingBinaries = verifyBinaries(binDir, platformInfo.binaries);
if (missingBinaries.length > 0) {
throw new Error(
`Platform package ${platformInfo.packageName} is installed but binaries are missing:\n` +
` Missing: ${missingBinaries.join(', ')}\n` +
` Expected in: ${binDir}\n\n` +
`This means the platform package was not built correctly.\n` +
`Try reinstalling: npm install -g swictation --force`
);
}
return {
platform: platformInfo.platform,
arch: platformInfo.arch,
packageName: platformInfo.packageName,
packageDir,
binDir,
libDir, // GPU libraries will be downloaded here by postinstall.js
daemon: path.join(binDir, platformInfo.binaries.daemon),
ui: path.join(binDir, platformInfo.binaries.ui)
};
}
/**
* Get library directory for GPU libraries
*
* Used by postinstall.js to determine where to download:
* - CUDA libraries (Linux with NVIDIA GPU)
* - ROCm libraries (Linux with AMD GPU)
* - OpenVINO libraries (Linux with Intel GPU)
* - CoreML libraries (macOS - already in ONNX Runtime)
*/
function getLibraryDirectory() {
const paths = resolveBinaryPaths();
return paths.libDir;
}
/**
* Check if platform package is installed (for postinstall checks)
*/
function isPlatformPackageInstalled() {
try {
resolveBinaryPaths();
return true;
} catch (err) {
return false;
}
}
module.exports = {
resolveBinaryPaths,
getLibraryDirectory,
isPlatformPackageInstalled,
detectPlatform
};