feat: Telegram fleet alerts, forge sigil scramble, UI polish, agent ops

- Calibrate: per-event Telegram/SMTP toggles, test notification, chat ID help
- Notify on agent connect/reconnect, offline/hashrate/rejection, forge complete
- Sigil scramble post-forge uniquification and Dispense Reveal ceremony
- Full system check, desktop push, BITS/host-binary persistence, Path Tracer
- Dashboard/Crucible visual polish, haptics, sacred geometry, mobile nav
- README documents alerts, sigil scramble, and pack-usb workflow
- USB bundle repacked via pack-usb.bat (AetherForge.exe + synced agent source)
This commit is contained in:
AetherForge
2026-06-03 20:32:59 -07:00
parent 03937edba7
commit d52479c9a6
139 changed files with 10611 additions and 369 deletions

View File

@@ -4,6 +4,7 @@ import { useWebSocket } from '../hooks/useWebSocket';
import type { Agent, HashrateSample, ServerInfo } from '../types';
import LatencyBadge from '../components/Fleet/LatencyBadge';
import HashrateChart from '../components/Charts/HashrateChart';
import { resolveChartSeries } from '../help/chartSampleData';
import NeonCard from '../components/NeonCard/NeonCard';
import AgentRemoteActions from '../components/Fleet/AgentRemoteActions';
import AgentListItem from '../components/Fleet/AgentListItem';
@@ -95,6 +96,7 @@ export default function AgentsPage() {
const [loadError, setLoadError] = useState('');
const [logContent, setLogContent] = useState('');
const [logLoading, setLogLoading] = useState(false);
const [logDownloading, setLogDownloading] = useState(false);
const [notesDraft, setNotesDraft] = useState('');
const [tagsDraft, setTagsDraft] = useState('');
const [metaSaving, setMetaSaving] = useState(false);
@@ -587,19 +589,28 @@ export default function AgentsPage() {
</div>
</div>
{hashrateHistory.length > 0 && (
{selectedAgent && (
<div className="detail-section">
<h3 className="font-tech">HASHRATE TELEMETRY</h3>
<HashrateChart
title=""
color="#00f5ff"
unit="H/s"
height={240}
data={[...hashrateHistory].reverse().map((s) => ({
{(() => {
const live = [...hashrateHistory].reverse().map((s) => ({
time: new Date(s.timestamp).toLocaleTimeString(),
value: s.hashrate,
}))}
/>
}));
const chart = resolveChartSeries(live, 'hashrate', {
tailValue: selectedAgent.hashrate_15m,
});
return (
<HashrateChart
title=""
color="#00f5ff"
unit="H/s"
height={240}
data={chart.data}
displayMode={chart.mode}
/>
);
})()}
</div>
)}
@@ -620,7 +631,29 @@ export default function AgentsPage() {
</div>
<div className="detail-section">
<h3>Agent Log <button type="button" className="agent-action-btn" onClick={() => refreshLog(true)} disabled={logLoading || selectedAgent.status !== 'online'}>{logLoading ? '…' : 'Refresh'}</button></h3>
<h3>
Agent Log{' '}
<button type="button" className="agent-action-btn" onClick={() => refreshLog(true)} disabled={logLoading || selectedAgent.status !== 'online'}>{logLoading ? '…' : 'Refresh'}</button>
<button
type="button"
className="agent-action-btn"
disabled={logDownloading || selectedAgent.status !== 'online'}
title="Download full agent log as a file"
style={{ marginLeft: '0.4rem' }}
onClick={async () => {
setLogDownloading(true);
try {
await api.downloadAgentLog(selectedAgent.id);
} catch (err) {
alert(err instanceof Error ? err.message : 'Download failed');
} finally {
setLogDownloading(false);
}
}}
>
{logDownloading ? '…' : '⬇ Download'}
</button>
</h3>
<p className="form-hint">Streams miner.log when file_logging is enabled (non-stealth builds).</p>
<pre className="log-viewer">{logContent || (selectedAgent.status === 'online' ? 'Click Fetch Log or Refresh' : 'Agent offline')}</pre>
</div>