Add forge pipeline polish, simple forge UX, and fleet management upgrades.

Fusion copies prep icon and version info via go-winres; optional Garble obfuscation, Authenticode signing, and dry-run size estimates. Forge Simple mode with smart defaults; fleet roster gets compact expandable cards, filters, bulk commands, per-agent notes/tags, and typed WebSocket payloads.
This commit is contained in:
drjones
2026-05-28 21:48:20 -07:00
parent fda72041f0
commit c95a4373de
45 changed files with 2282 additions and 272 deletions

View File

@@ -1,9 +1,12 @@
import { useEffect, useRef, useCallback, useState } from 'react';
import type { WSMessage, Agent, Share, FleetAlert, PoolStatus, AIActivityEntry } from '../types';
interface DashboardInit {
agents: Agent[];
}
import type {
WSDashboardInit,
WSAgentOffline,
WSStatsUpdate,
WSCommandResult,
WSAgentLog,
} from '../types/ws';
import type { Agent, Share, FleetAlert, PoolStatus, AIActivityEntry, WSMessage } from '../types';
interface UseWebSocketReturn {
isConnected: boolean;
@@ -54,12 +57,12 @@ export function useWebSocket(): UseWebSocketReturn {
ws.onmessage = (event) => {
try {
const msg: WSMessage = JSON.parse(event.data);
const msg = JSON.parse(event.data) as WSMessage;
setLatestMessage(msg);
switch (msg.type) {
case 'init': {
const data = msg.payload as DashboardInit;
const data = msg.payload as WSDashboardInit;
if (data.agents) setAgents(data.agents);
break;
}
@@ -69,7 +72,7 @@ export function useWebSocket(): UseWebSocketReturn {
const idx = prev.findIndex((a) => a.id === agent.id);
if (idx >= 0) {
const updated = [...prev];
updated[idx] = agent;
updated[idx] = { ...updated[idx], ...agent };
return updated;
}
return [...prev, agent];
@@ -77,7 +80,7 @@ export function useWebSocket(): UseWebSocketReturn {
break;
}
case 'agent_offline': {
const { agent_id } = msg.payload as { agent_id: string };
const { agent_id } = msg.payload as WSAgentOffline;
setAgents((prev) =>
prev.map((a) =>
a.id === agent_id ? { ...a, status: 'offline' as const } : a
@@ -86,17 +89,7 @@ export function useWebSocket(): UseWebSocketReturn {
break;
}
case 'stats_update': {
const update = msg.payload as {
agent_id: string;
hashrate_15s: number;
hashrate_1m: number;
hashrate_15m: number;
cpu_usage_pct: number;
memory_usage_pct?: number;
uptime_seconds?: number;
shares_submitted?: number;
shares_accepted?: number;
};
const update = msg.payload as WSStatsUpdate;
setAgents((prev) =>
prev.map((a) =>
a.id === update.agent_id
@@ -115,6 +108,7 @@ export function useWebSocket(): UseWebSocketReturn {
(update.shares_submitted ?? a.shares_total) -
(update.shares_accepted ?? a.shares_good)
),
status: 'online' as const,
}
: a
)
@@ -150,17 +144,15 @@ export function useWebSocket(): UseWebSocketReturn {
break;
}
case 'command_result': {
const { agent_id } = msg.payload as { agent_id?: string };
if (agent_id && msg.payload && typeof msg.payload === 'object') {
const p = msg.payload as { action?: string; message?: string; success?: boolean };
if (p.action === 'get_log' && p.success && p.message) {
setAgentLogs((prev) => ({ ...prev, [agent_id]: p.message! }));
}
const p = msg.payload as WSCommandResult;
const agent_id = p.agent_id;
if (agent_id && p.action === 'get_log' && p.success && p.message) {
setAgentLogs((prev) => ({ ...prev, [agent_id]: p.message! }));
}
break;
}
case 'agent_log': {
const { agent_id, content } = msg.payload as { agent_id: string; content: string };
const { agent_id, content } = msg.payload as WSAgentLog;
if (agent_id) {
setAgentLogs((prev) => ({ ...prev, [agent_id]: content }));
}