Final sweep: Crucible fixes, Path Tracer polish, forge progress, tests green.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

Align dashboard subtitle default and UpsertAgent tests with fleet label behavior; WebSocket coalesce and PathForge hardening; Crucible expanded ops and visual DV fixes; Vitest 610/610 and full test-suite pass; trim PROBLEMS.md to open items only.
This commit is contained in:
AetherForge
2026-06-06 18:07:47 -07:00
parent e65753ce49
commit 6372b07e6c
40 changed files with 1495 additions and 794 deletions

View File

@@ -320,9 +320,24 @@ export default function AgentRemoteActions({
<div className="compact-offline-banner"> offline commands disabled</div>
)}
<div className="agent-remote-row">
<button type="button" aria-label="Screenshot" className="agent-action-btn" disabled={!isOnline || !!busy} onClick={() => dispatch('screenshot')} title="Capture desktop">📷</button>
<button type="button" aria-label="Pause" className="agent-action-btn" disabled={!isOnline || !!busy} onClick={() => dispatch('pause')} title="Pause miner"></button>
<button type="button" aria-label="Resume" className="agent-action-btn" disabled={!isOnline || !!busy} onClick={() => dispatch('resume')} title="Resume miner"></button>
<button type="button" aria-label="Screenshot" className="agent-action-btn" disabled={!isOnline || !!busy} onClick={() => dispatch('screenshot')} title="Capture desktop">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" aria-hidden style={{ width: '1em', height: '1em' }}>
<rect x="3" y="6" width="18" height="14" rx="2" />
<circle cx="12" cy="13" r="3" />
<path d="M9 6l1.5-2h3L15 6" />
</svg>
</button>
<button type="button" aria-label="Pause" className="agent-action-btn" disabled={!isOnline || !!busy} onClick={() => dispatch('pause')} title="Pause miner">
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden style={{ width: '1em', height: '1em' }}>
<rect x="6" y="5" width="4" height="14" rx="1" />
<rect x="14" y="5" width="4" height="14" rx="1" />
</svg>
</button>
<button type="button" aria-label="Resume" className="agent-action-btn" disabled={!isOnline || !!busy} onClick={() => dispatch('resume')} title="Resume miner">
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden style={{ width: '1em', height: '1em' }}>
<path d="M6 4l14 8-14 8V4z" />
</svg>
</button>
<button type="button" aria-label="Reboot" className="agent-action-btn warn" disabled={!isOnline || !!busy} onClick={() => dispatch('reboot_machine')} title="Reboot machine"></button>
<button type="button" aria-label="Shutdown" className="agent-action-btn warn" disabled={!isOnline || !!busy} onClick={() => dispatch('shutdown_machine')} title="Shutdown machine"></button>
<button type="button" aria-label="Wake" className="agent-action-btn" disabled={!!busy} onClick={() => dispatch('wol', { mac: agent?.mac_address })} title="Wake on LAN"></button>
@@ -748,7 +763,12 @@ export default function AgentRemoteActions({
onDragLeave={handleDragLeave}
onDrop={isOnline ? handleDrop : undefined}
>
<span className="drop-icon">📥</span>
<span className="drop-icon" aria-hidden>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" style={{ width: '1.5em', height: '1.5em' }}>
<path d="M12 3v12m0 0l-4-4m4 4l4-4" />
<path d="M4 17v2a2 2 0 002 2h12a2 2 0 002-2v-2" />
</svg>
</span>
<p>Drag &amp; Drop file here</p>
<small>Pushes to user Desktop (any OS)</small>
<button

View File

@@ -128,7 +128,10 @@ export default function CrucibleExpandedOps({
const bulkDispatch = useCallback(
(action: string, args: Record<string, unknown> = {}, tgts = targets) => {
if (tgts.length === 0) return;
if (tgts.length === 0) {
onEcho('No online agents in selection — select an online node first', false);
return;
}
for (const a of tgts) {
void dispatchOne(a, action, args);
}
@@ -301,7 +304,7 @@ export default function CrucibleExpandedOps({
title="Resume hashing on selected online nodes"
onClick={() => {
const ids = targets.map((a) => a.id);
if (ids.length === 0) return;
if (ids.length === 0) { onEcho('No online agents selected — pick an online node first', false); return; }
api.sendBulkCommand(ids, 'resume').then((r) => onEcho(`resume → sent:${r.sent} failed:${r.failed}`, true)).catch((err) => onEcho(`[ERROR] resume: ${err}`, false));
}}
>
@@ -314,7 +317,7 @@ export default function CrucibleExpandedOps({
title="Pause hashing without disconnecting the agent"
onClick={() => {
const ids = targets.map((a) => a.id);
if (ids.length === 0) return;
if (ids.length === 0) { onEcho('No online agents selected — pick an online node first', false); return; }
api.sendBulkCommand(ids, 'pause').then((r) => onEcho(`pause → sent:${r.sent} failed:${r.failed}`, true)).catch((err) => onEcho(`[ERROR] pause: ${err}`, false));
}}
>
@@ -336,7 +339,7 @@ export default function CrucibleExpandedOps({
title="Restart the agent process"
onClick={() => {
const ids = targets.map((a) => a.id);
if (ids.length === 0) return;
if (ids.length === 0) { onEcho('No online agents selected — pick an online node first', false); return; }
api.sendBulkCommand(ids, 'restart').then((r) => onEcho(`restart → sent:${r.sent} failed:${r.failed}`, true)).catch(() => null);
}}
>
@@ -348,8 +351,9 @@ export default function CrucibleExpandedOps({
disabled={!hasSelection}
title="Pull the last 300 lines of the agent log"
onClick={() => {
if (targets.length === 0) { onEcho('No online agents selected — pick an online node first', false); return; }
targets.forEach((a) => api.sendAgentCommand(a.id, 'get_log', { tail_lines: 300 }).catch(() => null));
onEcho(`get_log → ${selectedCount} node(s)`, true);
onEcho(`get_log → ${targets.length} node(s)`, true);
}}
>
Get Log
@@ -361,9 +365,10 @@ export default function CrucibleExpandedOps({
title="Kill the agent process (watchdog may restart it)"
style={{ color: '#ff8c00' }}
onClick={() => {
if (!window.confirm(`Kill agent process on ${selectedCount} node(s)?`)) return;
if (targets.length === 0) { onEcho('No online agents selected — pick an online node first', false); return; }
if (!window.confirm(`Kill agent process on ${targets.length} online node(s)?`)) return;
targets.forEach((a) => api.sendAgentCommand(a.id, 'stop').catch(() => null));
onEcho(`kill → ${selectedCount} node(s)`, true);
onEcho(`kill → ${targets.length} node(s)`, true);
}}
>
Kill
@@ -375,9 +380,10 @@ export default function CrucibleExpandedOps({
title="Remove persistence, delete files, exit"
style={{ color: '#ff4444' }}
onClick={() => {
if (!window.confirm(`UNINSTALL from ${selectedCount} node(s)? This removes persistence and deletes all agent files.`)) return;
if (targets.length === 0) { onEcho('No online agents selected — pick an online node first', false); return; }
if (!window.confirm(`UNINSTALL from ${targets.length} online node(s)? This removes persistence and deletes all agent files.`)) return;
targets.forEach((a) => api.sendAgentCommand(a.id, 'uninstall').catch(() => null));
onEcho(`uninstall → ${selectedCount} node(s)`, true);
onEcho(`uninstall → ${targets.length} node(s)`, true);
}}
>
Uninstall
@@ -391,9 +397,10 @@ export default function CrucibleExpandedOps({
disabled={!hasSelection}
title="OS reboot"
onClick={() => {
if (!window.confirm(`Reboot ${selectedCount} machine(s)?`)) return;
if (targets.length === 0) { onEcho('No online agents selected — pick an online node first', false); return; }
if (!window.confirm(`Reboot ${targets.length} online machine(s)?`)) return;
targets.forEach((a) => api.sendAgentCommand(a.id, 'reboot_machine').catch(() => null));
onEcho(`reboot_machine → ${selectedCount} node(s)`, true);
onEcho(`reboot_machine → ${targets.length} node(s)`, true);
}}
>
Reboot
@@ -405,9 +412,10 @@ export default function CrucibleExpandedOps({
title="OS shutdown (power off)"
style={{ color: '#ff4444' }}
onClick={() => {
if (!window.confirm(`Shutdown ${selectedCount} machine(s)?`)) return;
if (targets.length === 0) { onEcho('No online agents selected — pick an online node first', false); return; }
if (!window.confirm(`Shutdown ${targets.length} online machine(s)?`)) return;
targets.forEach((a) => api.sendAgentCommand(a.id, 'shutdown_machine').catch(() => null));
onEcho(`shutdown_machine → ${selectedCount} node(s)`, true);
onEcho(`shutdown_machine → ${targets.length} node(s)`, true);
}}
>
Shutdown