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
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:
@@ -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 & Drop file here</p>
|
||||
<small>Pushes to user Desktop (any OS)</small>
|
||||
<button
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user