/** * Cross-platform detection utilities for NanoClaw setup. */ import { execSync } from 'child_process'; import fs from 'fs'; import os from 'os'; export type Platform = 'macos' | 'linux' | 'unknown'; export type ServiceManager = 'launchd' | 'systemd' | 'none'; export function getPlatform(): Platform { const platform = os.platform(); if (platform === 'darwin') return 'macos'; if (platform === 'linux') return 'linux'; return 'unknown'; } export function isWSL(): boolean { if (os.platform() !== 'linux') return false; try { const release = fs.readFileSync('/proc/version', 'utf-8').toLowerCase(); return release.includes('microsoft') || release.includes('wsl'); } catch { return false; } } export function isRoot(): boolean { return process.getuid?.() === 0; } export function isHeadless(): boolean { // No display server available if (getPlatform() === 'linux') { return !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY; } // macOS is never headless in practice (even SSH sessions can open URLs) return false; } export function hasSystemd(): boolean { if (getPlatform() !== 'linux') return false; try { // Check if systemd is PID 1 const init = fs.readFileSync('/proc/1/comm', 'utf-8').trim(); return init === 'systemd'; } catch { return false; } } /** * Open a URL in the default browser, cross-platform. * Returns true if the command was attempted, false if no method available. */ export function openBrowser(url: string): boolean { try { const platform = getPlatform(); if (platform === 'macos') { execSync(`open ${JSON.stringify(url)}`, { stdio: 'ignore' }); return true; } if (platform === 'linux') { // Try xdg-open first, then wslview for WSL if (commandExists('xdg-open')) { execSync(`xdg-open ${JSON.stringify(url)}`, { stdio: 'ignore' }); return true; } if (isWSL() && commandExists('wslview')) { execSync(`wslview ${JSON.stringify(url)}`, { stdio: 'ignore' }); return true; } // WSL without wslview: try cmd.exe if (isWSL()) { try { execSync(`cmd.exe /c start "" ${JSON.stringify(url)}`, { stdio: 'ignore', }); return true; } catch { // cmd.exe not available } } } } catch { // Command failed } return false; } export function getServiceManager(): ServiceManager { const platform = getPlatform(); if (platform === 'macos') return 'launchd'; if (platform === 'linux') { if (hasSystemd()) return 'systemd'; return 'none'; } return 'none'; } export function getNodePath(): string { try { return execSync('command -v node', { encoding: 'utf-8' }).trim(); } catch { return process.execPath; } } export function commandExists(name: string): boolean { try { execSync(`command -v ${name}`, { stdio: 'ignore' }); return true; } catch { return false; } } export function getNodeVersion(): string | null { try { const version = execSync('node --version', { encoding: 'utf-8' }).trim(); return version.replace(/^v/, ''); } catch { return null; } } export function getNodeMajorVersion(): number | null { const version = getNodeVersion(); if (!version) return null; const major = parseInt(version.split('.')[0], 10); return isNaN(major) ? null : major; }