refactor: require Bun globally, add auto-install, remove Windows executable approach (#243)
- Delete BinaryManager.ts - no longer needed - Simplify ProcessManager.ts - single Bun spawn path for all platforms - Update smart-install.js - auto-install Bun if missing (Windows/Unix) - Update documentation to reflect Bun requirement This simplifies the codebase by: - Using Bun consistently across all platforms (hooks + worker) - Eliminating binary download/hosting complexity - Zero native dependencies with bun:sqlite - Auto-installing Bun on first run if not present Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,76 +0,0 @@
|
||||
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { DATA_DIR } from '../../shared/paths.js';
|
||||
|
||||
const BIN_DIR = join(DATA_DIR, 'bin');
|
||||
const VERSION_FILE = join(BIN_DIR, 'version.txt');
|
||||
const GITHUB_RELEASES = 'https://github.com/thedotmack/claude-mem/releases/download';
|
||||
|
||||
export class BinaryManager {
|
||||
static async getExecutablePath(): Promise<string> {
|
||||
if (process.platform !== 'win32') {
|
||||
throw new Error('BinaryManager only used on Windows');
|
||||
}
|
||||
|
||||
const version = this.getCurrentVersion();
|
||||
const binaryPath = join(BIN_DIR, 'worker-service.exe');
|
||||
|
||||
// Check if we have correct version
|
||||
if (existsSync(binaryPath)) {
|
||||
const installed = this.getInstalledVersion();
|
||||
if (installed === version) return binaryPath;
|
||||
}
|
||||
|
||||
// Download
|
||||
await this.downloadBinary(version);
|
||||
return binaryPath;
|
||||
}
|
||||
|
||||
private static async downloadBinary(version: string): Promise<void> {
|
||||
const url = `${GITHUB_RELEASES}/v${version}/worker-service-v${version}-win-x64.exe`;
|
||||
console.log(`Downloading worker binary v${version}...`);
|
||||
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Download failed: ${response.status}\n` +
|
||||
`URL: ${url}\n` +
|
||||
`Make sure the release exists with a Windows binary attached.`
|
||||
);
|
||||
}
|
||||
|
||||
const buffer = await response.arrayBuffer();
|
||||
mkdirSync(BIN_DIR, { recursive: true });
|
||||
|
||||
const binaryPath = join(BIN_DIR, 'worker-service.exe');
|
||||
writeFileSync(binaryPath, Buffer.from(buffer));
|
||||
|
||||
// Write version file
|
||||
writeFileSync(VERSION_FILE, version);
|
||||
|
||||
console.log('Download complete');
|
||||
}
|
||||
|
||||
private static getCurrentVersion(): string {
|
||||
// Read from package.json in the installed plugin location
|
||||
// This ensures we get the correct version even when running from marketplace
|
||||
try {
|
||||
const packageJson = JSON.parse(
|
||||
readFileSync(join(DATA_DIR, '..', '.claude', 'plugins', 'marketplaces', 'thedotmack', 'package.json'), 'utf-8')
|
||||
);
|
||||
return packageJson.version;
|
||||
} catch {
|
||||
// Fallback to environment variable
|
||||
return process.env.npm_package_version || 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
private static getInstalledVersion(): string | null {
|
||||
try {
|
||||
if (!existsSync(VERSION_FILE)) return null;
|
||||
return readFileSync(VERSION_FILE, 'utf-8').trim();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,21 +43,21 @@ export class ProcessManager {
|
||||
|
||||
const logFile = this.getLogFilePath();
|
||||
|
||||
// Platform-specific spawn
|
||||
if (process.platform === 'win32') {
|
||||
return this.startWindows(port);
|
||||
} else {
|
||||
return this.startUnix(workerScript, logFile, port);
|
||||
}
|
||||
// Use Bun on all platforms
|
||||
return this.startWithBun(workerScript, logFile, port);
|
||||
}
|
||||
|
||||
private static async startUnix(script: string, logFile: string, port: number): Promise<{ success: boolean; pid?: number; error?: string }> {
|
||||
private static async startWithBun(script: string, logFile: string, port: number): Promise<{ success: boolean; pid?: number; error?: string }> {
|
||||
try {
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
const child = spawn('bun', [script], {
|
||||
detached: true,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
env: { ...process.env, CLAUDE_MEM_WORKER_PORT: String(port) },
|
||||
cwd: MARKETPLACE_ROOT
|
||||
cwd: MARKETPLACE_ROOT,
|
||||
// Hide console window on Windows
|
||||
...(isWindows && { windowsHide: true })
|
||||
});
|
||||
|
||||
// Write logs
|
||||
@@ -89,48 +89,6 @@ export class ProcessManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static async startWindows(port: number): Promise<{ success: boolean; pid?: number; error?: string }> {
|
||||
// Windows: Use compiled exe from ~/.claude-mem/bin/
|
||||
// Import dynamically to avoid loading on non-Windows platforms
|
||||
const { BinaryManager } = await import('./BinaryManager.js');
|
||||
|
||||
try {
|
||||
const exePath = await BinaryManager.getExecutablePath();
|
||||
|
||||
const child = spawn(exePath, [], {
|
||||
detached: true,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
env: { ...process.env, CLAUDE_MEM_WORKER_PORT: String(port) },
|
||||
windowsHide: true
|
||||
});
|
||||
|
||||
const logFile = this.getLogFilePath();
|
||||
const logStream = createWriteStream(logFile, { flags: 'a' });
|
||||
child.stdout?.pipe(logStream);
|
||||
child.stderr?.pipe(logStream);
|
||||
|
||||
child.unref();
|
||||
|
||||
if (!child.pid) {
|
||||
return { success: false, error: 'Failed to get PID from spawned process' };
|
||||
}
|
||||
|
||||
this.writePidFile({
|
||||
pid: child.pid,
|
||||
port,
|
||||
startedAt: new Date().toISOString(),
|
||||
version: process.env.npm_package_version || 'unknown'
|
||||
});
|
||||
|
||||
return this.waitForHealth(child.pid, port);
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static async stop(timeout: number = PROCESS_STOP_TIMEOUT_MS): Promise<boolean> {
|
||||
const info = this.getPidInfo();
|
||||
if (!info) return true;
|
||||
|
||||
Reference in New Issue
Block a user