feat(FN-2168): add persistent dev-server dashboard flow

- Add a durable dev-server store and manager with persisted state/log history, process reconciliation, and SSE event buffering
- Wire new backend lifecycle APIs for status/history/start/stop/restart/stream and initialize/shutdown manager handling in server startup
- Add dashboard Dev Server view, hook-based hydration/stream handling, nav integration, and supporting styles
- Expand coverage with backend persistence/route tests and frontend DevServerView/useDevServer tests
- Document the dev-server persistence architecture in docs/architecture.md
This commit is contained in:
Fusion
2026-04-19 14:55:14 -07:00
committed by gsxdsm
parent 4bd7999153
commit 687c4ac0c9
20 changed files with 2822 additions and 1266 deletions

View File

@@ -14,7 +14,6 @@ import { rateLimit, RATE_LIMITS } from "./rate-limit.js";
import { ApiError, sendErrorResponse } from "./api-error.js";
import { getOrCreateProjectStore, evictAllProjectStores, setOnProjectFirstCreated } from "./project-store-resolver.js";
import { getTerminalService, STALE_SESSION_THRESHOLD_MS } from "./terminal-service.js";
import { destroyAllDevServerManagers, getDevServerManager } from "./dev-server-manager.js";
import { WebSocketServer, type WebSocket } from "ws";
import { terminalSessionManager } from "./terminal.js";
@@ -43,6 +42,7 @@ import {
rehydrateFromStore as rehydrateMilestoneSliceSessions,
} from "./milestone-slice-interview.js";
import { ChatManager } from "./chat.js";
import { loadDevServerManager, shutdownAllDevServerManagers } from "./dev-server-manager.js";
import type { SkillsAdapter } from "./skills-adapter.js";
import { createAuthMiddleware } from "./auth-middleware.js";
@@ -393,9 +393,6 @@ export function createServer(store: TaskStore, options?: ServerOptions): ReturnT
// Initialize terminal service with project root
getTerminalService(store.getRootDir());
// Initialize dev server manager for this project
getDevServerManager(store.getRootDir());
const isHeadless = options?.headless === true;
// Serve built React app
@@ -824,10 +821,18 @@ export function createServer(store: TaskStore, options?: ServerOptions): ReturnT
server = originalListen(...normalizedArgs);
}
void loadDevServerManager(store.getRootDir()).catch((error) => {
const message = error instanceof Error ? error.message : String(error);
console.warn(`[server] Failed to initialize dev-server manager: ${message}`);
});
server.once("close", () => {
clearAiSessionCleanupInterval();
aiSessionStore.stopScheduledCleanup();
destroyAllDevServerManagers();
void shutdownAllDevServerManagers().catch((error) => {
const message = error instanceof Error ? error.message : String(error);
console.warn(`[server] Failed to shutdown dev-server managers: ${message}`);
});
});
if (!dashboardApp.__fnWebSocketsAttached) {