import os from "node:os"; import si from "systeminformation"; export type HardwareTier = "gpu" | "apple_silicon" | "cpu_only"; export interface HardwareResult { tier: HardwareTier; totalGb: number; gpuName?: string; gpuVramGb?: number; } /** * Detect the hardware tier of the current machine. * * Priority: * 1. Apple Silicon (darwin + CPU model starts with "Apple") * 2. GPU with >= 4 GB VRAM (detected via systeminformation with 3s timeout) * 3. CPU-only fallback * * @param platform - Override process.platform for testing */ export async function detectHardware(platform?: string): Promise { const resolvedPlatform = platform ?? process.platform; const totalGb = Math.round((os.totalmem() / (1024 * 1024 * 1024)) * 10) / 10; // Apple Silicon path if (resolvedPlatform === "darwin") { const cpus = os.cpus(); if (cpus.length > 0 && cpus[0]?.model?.startsWith("Apple")) { return { tier: "apple_silicon", totalGb }; } } // GPU detection with 3-second timeout try { const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("GPU detection timeout")), 3000) ); const graphicsPromise = si.graphics(); const graphics = await Promise.race([graphicsPromise, timeoutPromise]); const controller = graphics.controllers?.[0]; const vram = controller?.vram ?? 0; if (controller && vram >= 4096) { return { tier: "gpu", totalGb, gpuName: controller.model, gpuVramGb: Math.round(vram / 1024), }; } } catch { // Timeout or error — fall through to cpu_only } return { tier: "cpu_only", totalGb }; }