Improve portable launch, forge persistence, and operator auth UX.

Persist build extra_files for Build Manager history, print dashboard login on every start, add libp2p for Mesh P2P forge, defer WebSocket until login, and split devrun.bat from LAUNCH.bat with USB deck auto-detection.
This commit is contained in:
AetherForge
2026-05-31 18:56:43 -07:00
parent 2f528229f2
commit feba06e008
80 changed files with 2897 additions and 675 deletions

View File

@@ -8,13 +8,19 @@ export function getStoredAuth(): string | null {
}
}
export function setStoredAuth(username: string, password: string) {
export function setStoredAuth(username: string, password: string, opts?: { silent?: boolean }) {
const token = btoa(`${username}:${password}`);
sessionStorage.setItem(AUTH_KEY, token);
if (!opts?.silent) {
window.dispatchEvent(new Event('aetherforge-auth'));
}
}
export function clearStoredAuth() {
export function clearStoredAuth(opts?: { silent?: boolean }) {
sessionStorage.removeItem(AUTH_KEY);
if (!opts?.silent) {
window.dispatchEvent(new Event('aetherforge-auth'));
}
}
export function authHeaders(): Record<string, string> {

View File

@@ -263,6 +263,16 @@ describe('api client', () => {
expect(JSON.parse(lastFetch().init.body as string)).toEqual({ username: 'alice', password: 'secret' });
});
it('rotateFleetSecret POSTs rotate endpoint', async () => {
fetchMock.mockResolvedValueOnce(jsonResponse({ ok: true, hint: 'abcd1234...' }));
const res = await api.rotateFleetSecret();
expect(res.ok).toBe(true);
expect(lastFetch().url).toBe('/api/v1/server/rotate-secret');
expect(lastFetch().init.method).toBe('POST');
});
it('getXmrPrice and getServerInfo', async () => {
fetchMock
.mockResolvedValueOnce(jsonResponse({ usd: 200, updated_at: 'now' }))

View File

@@ -3,6 +3,9 @@ import { authHeaders } from './auth';
const API_BASE = '/api/v1';
// Agent-only REST (/agent/decide, /agent/report, /agent/heartbeat) is intentionally
// omitted here — forged agents call those with X-Fleet-Secret, not dashboard Basic Auth.
async function fetchJSON<T>(url: string, options?: RequestInit): Promise<T> {
const { headers: extraHeaders, ...rest } = options ?? {};
const res = await fetch(`${API_BASE}${url}`, {
@@ -118,6 +121,8 @@ export const api = {
// Health / server
healthCheck: () => fetchJSON<{ status: string }>('/health'),
getServerInfo: () => fetchJSON<ServerInfo>('/server/info'),
rotateFleetSecret: () =>
fetchJSON<{ ok: boolean; hint?: string }>('/server/rotate-secret', { method: 'POST' }),
// Fleet ops
getAlerts: () => fetchJSON<FleetAlert[]>('/alerts'),