feat(soak): wire --watch=dashboard in runner

Starts DashboardServer on port 7777 (or --dashboard-port), uses its
reporter as ctx.dashboard, auto-opens the URL via xdg-open/open/start.
Cleans up on exit. WS client connections logged as info events so
you can see when the browser attaches.

Verified: 2-account populate run with --watch=dashboard serves the
static page on :7777, accepts WS connections, cleanly shuts down
when the run completes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
adlee-was-taken
2026-04-11 19:05:44 -04:00
parent a35e789eb9
commit 796de876b7

View File

@@ -10,10 +10,12 @@
*/
import * as path from 'path';
import { spawn } from 'child_process';
import { parseArgs, mergeConfig, CliArgs } from './config';
import { createLogger } from './core/logger';
import { SessionPool } from './core/session-pool';
import { RoomCoordinator } from './core/room-coordinator';
import { DashboardServer } from './dashboard/server';
import { getScenario, listScenarios } from './scenarios';
import type { DashboardReporter, ScenarioContext } from './core/types';
@@ -96,11 +98,6 @@ async function main(): Promise<void> {
return;
}
if (watch !== 'none') {
logger.warn('watch_mode_not_yet_implemented', { watch });
console.warn(`Watch mode "${watch}" not yet implemented — falling back to "none".`);
}
// Build dependencies
const credFile = path.resolve(__dirname, '.env.stresstest');
const pool = new SessionPool({
@@ -110,7 +107,41 @@ async function main(): Promise<void> {
logger,
});
const coordinator = new RoomCoordinator();
const dashboard = noopDashboard();
// Optional dashboard server
let dashboardServer: DashboardServer | null = null;
let dashboard: DashboardReporter = noopDashboard();
if (watch === 'dashboard') {
const port = Number(config.dashboardPort ?? 7777);
dashboardServer = new DashboardServer(port, logger, {
onStartStream: (key) => {
logger.info('stream_start_requested', { sessionKey: key });
// Wired in Task 23
},
onStopStream: (key) => {
logger.info('stream_stop_requested', { sessionKey: key });
},
});
await dashboardServer.start();
dashboard = dashboardServer.reporter();
const url = `http://localhost:${port}`;
console.log(`Dashboard: ${url}`);
try {
const opener =
process.platform === 'darwin'
? 'open'
: process.platform === 'win32'
? 'start'
: 'xdg-open';
spawn(opener, [url], { stdio: 'ignore', detached: true }).unref();
} catch {
// If auto-open fails, the URL is already printed.
}
} else if (watch === 'tiled') {
logger.warn('tiled_not_yet_implemented');
console.warn('Watch mode "tiled" not yet implemented (Task 24). Falling back to none.');
}
const abortController = new AbortController();
const onSignal = (sig: string) => {
@@ -161,6 +192,9 @@ async function main(): Promise<void> {
exitCode = 1;
} finally {
await pool.release();
if (dashboardServer) {
await dashboardServer.stop();
}
}
if (abortController.signal.aborted && exitCode === 0) exitCode = 2;