import { execSync } from "node:child_process"; import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { homedir, hostname, platform } from "node:os"; import { join } from "node:path"; const SCRIBE_DIR = join(homedir(), ".scribe"); const DEVICE_ID_FILE = join(SCRIBE_DIR, "device-id"); /** * Get the system's hardware machine ID * - macOS: IOPlatformUUID from ioreg * - Linux: /etc/machine-id * - Windows: MachineGuid from registry * - Fallback: hostname-based hash */ function getSystemMachineId(): string | null { try { const os = platform(); if (os === "darwin") { // macOS: Get IOPlatformUUID const output = execSync( "ioreg -rd1 -c IOPlatformExpertDevice | grep IOPlatformUUID", { encoding: "utf-8" }, ); const match = output.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/); if (match) return match[1]; } else if (os === "linux") { // Linux: Read /etc/machine-id if (existsSync("/etc/machine-id")) { return readFileSync("/etc/machine-id", "utf-8").trim(); } // Fallback to dbus machine-id if (existsSync("/var/lib/dbus/machine-id")) { return readFileSync("/var/lib/dbus/machine-id", "utf-8").trim(); } } else if (os === "win32") { // Windows: Get MachineGuid from registry const output = execSync( 'reg query "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography" /v MachineGuid', { encoding: "utf-8" }, ); const match = output.match(/MachineGuid\s+REG_SZ\s+([^\s]+)/); if (match) return match[1]; } } catch { // Fall through to null } return null; } /** * Get a persistent device ID for this machine. * Checks ~/.scribe/device-id first, then system machine ID, then generates one. */ export function getMachineId(): string { // Check for cached ID first if (existsSync(DEVICE_ID_FILE)) { try { const cached = readFileSync(DEVICE_ID_FILE, "utf-8").trim(); if (cached) return cached; } catch { // Fall through } } // Try to get system machine ID let machineId = getSystemMachineId(); // Fallback to hostname-based ID if (!machineId) { const host = hostname() || "unknown"; let hash = 0; for (let i = 0; i < host.length; i++) { const char = host.charCodeAt(i); hash = (hash << 5) - hash + char; hash = hash & hash; } machineId = `fallback_${Math.abs(hash).toString(36)}_${host.replace(/[^a-zA-Z0-9]/g, "")}`; } // Cache the ID for future use try { if (!existsSync(SCRIBE_DIR)) { mkdirSync(SCRIBE_DIR, { recursive: true }); } writeFileSync(DEVICE_ID_FILE, machineId, "utf-8"); } catch { // Caching failed, but we still have the ID } return machineId; } /** * Get a friendly device name (hostname) */ export function getDeviceName(): string { return hostname() || "Unknown Device"; }