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:
@@ -1,4 +1,4 @@
|
||||
import type { Agent, Share, HashrateSample, BuildRecord, ServerConfig, BuildRequest, BuildResponse, ServerInfo, BlueprintInfo, FleetAlert, PoolStatus, AIActivityEntry, EarningsEstimate, FusionEstimate, XmrPrice } from '../types';
|
||||
import type { Agent, Share, HashrateSample, BuildRecord, ServerConfig, BuildRequest, BuildResponse, ServerInfo, BlueprintInfo, FleetAlert, PoolStatus, AIActivityEntry, EarningsEstimate, FusionEstimate, XmrPrice, PathTraceHop } from '../types';
|
||||
import { authHeaders } from './auth';
|
||||
|
||||
const API_BASE = '/api/v1';
|
||||
@@ -136,6 +136,10 @@ export const api = {
|
||||
|
||||
// Fleet ops
|
||||
getAlerts: () => fetchJSON<FleetAlert[]>('/alerts'),
|
||||
testAlerts: () =>
|
||||
fetchJSON<Record<string, { sent: boolean; error?: string }>>('/alerts/test', {
|
||||
method: 'POST',
|
||||
}),
|
||||
getPoolStatus: () => fetchJSON<PoolStatus[]>('/pools/status'),
|
||||
getAIActivity: () => fetchJSON<AIActivityEntry[]>('/ai/activity'),
|
||||
getEarningsEstimate: (hashrate: number) =>
|
||||
@@ -157,6 +161,22 @@ export const api = {
|
||||
getAgentLog: (id: string, refresh = false) =>
|
||||
fetchJSON<{ agent_id: string; content: string }>(`/agents/${id}/log${refresh ? '?refresh=1' : ''}`),
|
||||
|
||||
downloadAgentLog: async (id: string): Promise<void> => {
|
||||
const res = await fetch(`${API_BASE}/agents/${id}/log?download=1`, {
|
||||
headers: { ...authHeaders() },
|
||||
});
|
||||
if (!res.ok) throw new Error(`Log download failed: ${res.status}`);
|
||||
const blob = await res.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `agent-${id.slice(0, 8)}.log`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
},
|
||||
|
||||
updateAgentMeta: (id: string, notes: string, tags: string[]) =>
|
||||
fetchJSON<{ success: boolean; agent: Agent }>(`/agents/${id}/meta`, {
|
||||
method: 'PUT',
|
||||
@@ -187,9 +207,44 @@ export const api = {
|
||||
// XMR market price (server-side CoinGecko cache, refreshed every 10 min)
|
||||
getXmrPrice: () => fetchJSON<XmrPrice>('/market/xmr'),
|
||||
|
||||
// Path Tracer — WireGuard VPN chain sessions
|
||||
startTrace: (agentIds: string[]) =>
|
||||
fetchJSON<{ session_id: string; hops: PathTraceHop[] }>('/pathtrace/start', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ agent_ids: agentIds }),
|
||||
}),
|
||||
getTraceStatus: (id: string) =>
|
||||
fetchJSON<{ session_id: string; ready: boolean; error?: string; hops: PathTraceHop[] }>(`/pathtrace/${id}/status`),
|
||||
getTraceQR: (id: string) =>
|
||||
fetchJSON<{ config: string; qr_png_b64: string }>(`/pathtrace/${id}/qr`),
|
||||
deleteTrace: (id: string) =>
|
||||
fetchJSON<{ ok: boolean }>(`/pathtrace/${id}`, { method: 'DELETE' }),
|
||||
|
||||
// Cancel an in-progress forge build by its cancel token.
|
||||
cancelBuild: (cancelToken: string) =>
|
||||
fetchJSON<{ cancelled: boolean }>(`/builder/cancel/${encodeURIComponent(cancelToken)}`, {
|
||||
method: 'DELETE',
|
||||
}),
|
||||
|
||||
// Full deck backup — downloads a zip containing config.json, users.json, miner.db.
|
||||
downloadBackup: async (): Promise<void> => {
|
||||
const res = await fetch(`${API_BASE}/backup`, {
|
||||
method: 'GET',
|
||||
headers: { ...authHeaders() },
|
||||
});
|
||||
if (!res.ok) {
|
||||
const err = await res.text();
|
||||
throw new Error(`Backup failed ${res.status}: ${err}`);
|
||||
}
|
||||
const blob = await res.blob();
|
||||
const disposition = res.headers.get('Content-Disposition') ?? '';
|
||||
const match = disposition.match(/filename="([^"]+)"/);
|
||||
const filename = match ? match[1] : 'aetherforge-backup.zip';
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user