fix: audit fallout — tunnel port, orphaned dev processes, scrollback clear

Auditing for repeats of the `pnpm dev --tunnel` bugs turned up the same
mistakes elsewhere.

Remote tunnels assumed 4040. ProjectEngine's Cloudflare quick tunnel
hardcoded http://localhost:4040, so a dashboard on an explicit --port, a PORT
override, or runDashboard's EADDRINUSE rebind published a PUBLIC tunnel to
whatever else held 4040. The dashboard now records its bound port
(setLocalDashboardPort, from both runDashboard and headless serve) and the
tunnel reads it, keeping 4040 only as the pre-report default.
register-discovery-routes already derived its port from req.socket.localPort
and is untouched.

Stopping the dev wrapper orphaned everything it started. It installed no
signal handlers, so teardown only ran from the child's close handler:
signalling the wrapper left the dev server AND its cloudflared alive —
observed twice, four surviving processes each time, including a public
trycloudflare URL still serving a dev server believed to be down. Ctrl-C hid
it by signalling the whole process group.

SessionTerminal appended scrollback instead of clearing first, though the
server sends it as a separate frame precisely so the client can clear. Latent
today because every reattach builds a fresh xterm; a duplicated-history bug
the moment an in-place reconnect appears.

And BackupManager's centralDbPath is gone: written, never read, and a
leftover of the removed SQLite backup — the same class of stale artifact that
onboarding was using as evidence about a Postgres install.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-08-18 21:09:03 -07:00
parent b67e3aa8bc
commit 16e63462cc
14 changed files with 257 additions and 13 deletions

View File

@@ -100,6 +100,7 @@ import {
createFusionAuthStorage,
createFusionModelRegistry,
refreshFusionModelRegistry,
setLocalDashboardPort,
} from "@fusion/engine";
import { setHostTaskStore, clearHostTaskStores } from "../extension.js";
import { DefaultPackageManager, SettingsManager, discoverAndLoadExtensions, createExtensionRuntime } from "@earendil-works/pi-coding-agent";
@@ -2964,6 +2965,14 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
logSink.warn(`Port ${selectedPort} in use, using ${actualPort} instead`, "dashboard");
}
/*
FNXC:RemoteAccess 2026-08-19-04:00:
Publish the bound port to the engine so remote tunnels target THIS dashboard. Before this they
pointed at a hardcoded localhost:4040, so a dashboard on any other port (explicit --port, PORT,
or the EADDRINUSE rebind just above) tunnelled whatever else owned 4040.
*/
setLocalDashboardPort(actualPort);
/*
FNXC:DevTunnel 2026-08-19-02:05: report the REAL port to the dev supervisor (no-op without an
IPC channel, i.e. every non-`pnpm dev` launch). See DEV_SERVER_LISTENING_MESSAGE.

View File

@@ -35,6 +35,7 @@ import {
createFusionAuthStorage,
createFusionModelRegistry,
refreshFusionModelRegistry,
setLocalDashboardPort,
} from "@fusion/engine";
import { setHostTaskStore, clearHostTaskStores } from "../extension.js";
import { resolveServeDaemonToken } from "./serve-daemon-token.js";
@@ -1105,6 +1106,9 @@ export async function runServe(
});
const actualPort = (server.address() as AddressInfo).port;
// FNXC:RemoteAccess 2026-08-19-04:00: headless serve must publish its bound port too, or a remote
// tunnel started from it targets a hardcoded 4040. See local-dashboard-port.
setLocalDashboardPort(actualPort);
logPhase(`startup phase time-to-listen: ${Date.now() - serveStartedAt}ms`);
/*