diff --git a/internal/backend-dev-plugin.mjs b/internal/backend-dev-plugin.mjs index 46993c632..5a7dd82ca 100644 --- a/internal/backend-dev-plugin.mjs +++ b/internal/backend-dev-plugin.mjs @@ -32,10 +32,28 @@ export function devBackendProxy() { export function backendDevPlugin(root) { const children = [] + const alive = new Set() // backends whose process is running right now return { name: 'dx-backend-dev', apply: 'serve', configureServer(server) { + // ENGINE NOT RUNNING, QUIETLY (dx-shepherd, R500): with no backend process the proxy answered + // 500 ECONNREFUSED, which every page showed as console errors. While a backend is down its routes get + // well-formed answers instead: /events streams ONE status event saying the engine is offline (the + // plugin renders its own "engine not running" state from it), anything else returns an empty JSON + // list. Registered here, so it runs before vite's proxy. + for (const b of DEV_BACKENDS) { + server.middlewares.use(b.route, (req, res, next) => { + if (alive.has(b.name)) return next() + if ((req.url || '').startsWith('/events')) { + res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', Connection: 'keep-alive' }) + res.write(`data: ${JSON.stringify({ type: 'status', status: { engine: 'offline', error: 'The desktop engine is not running.' } })}\n\n`) + return // held open, so the EventSource does not reconnect in a loop + } + res.writeHead(200, { 'Content-Type': 'application/json' }) + res.end('[]') + }) + } for (const b of DEV_BACKENDS) { const bin = resolve(root, b.bin) if (!existsSync(bin)) { @@ -47,8 +65,9 @@ export function backendDevPlugin(root) { const out = (d) => String(d).trim().split('\n').forEach((l) => l && server.config.logger.info(`${tag} ${l}`)) child.stdout.on('data', out) child.stderr.on('data', out) - child.on('exit', (code) => server.config.logger.warn(`${tag} exited (${code})`)) + child.on('exit', (code) => { alive.delete(b.name); server.config.logger.warn(`${tag} exited (${code})`) }) children.push(child) + alive.add(b.name) server.config.logger.info(`${tag} started → ${b.route} on 127.0.0.1:${b.port}`) } const stop = () => children.forEach((c) => { try { c.kill('SIGTERM') } catch {} })