diff --git a/apps/desktop/src/app/DesktopApp.test.ts b/apps/desktop/src/app/DesktopApp.test.ts new file mode 100644 index 00000000000..9e46b137b0b --- /dev/null +++ b/apps/desktop/src/app/DesktopApp.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from "vite-plus/test"; +import * as Option from "effect/Option"; + +import { resolveDesktopBackendPortHint } from "./DesktopApp.ts"; + +describe("resolveDesktopBackendPortHint", () => { + it("keeps the renderer protocol aligned with a reused live backend", () => { + expect(resolveDesktopBackendPortHint("http://127.0.0.1:8080/", Option.some(3773))).toEqual( + Option.some(8080), + ); + }); + + it("uses the configured port when no live backend exists", () => { + expect(resolveDesktopBackendPortHint(undefined, Option.some(4949))).toEqual(Option.some(4949)); + }); + + it("ignores a malformed live backend marker", () => { + expect(resolveDesktopBackendPortHint("not a URL", Option.some(4949))).toEqual( + Option.some(4949), + ); + }); +}); diff --git a/apps/desktop/src/app/DesktopApp.ts b/apps/desktop/src/app/DesktopApp.ts index 7aba48bdd41..5d09a5c9446 100644 --- a/apps/desktop/src/app/DesktopApp.ts +++ b/apps/desktop/src/app/DesktopApp.ts @@ -21,6 +21,7 @@ import * as DesktopLifecycle from "./DesktopLifecycle.ts"; import * as DesktopObservability from "./DesktopObservability.ts"; import * as DesktopShutdown from "./DesktopShutdown.ts"; import * as DesktopServerExposure from "../backend/DesktopServerExposure.ts"; +import { readLiveExistingBackend } from "../backend/DesktopExistingBackend.ts"; import * as DesktopAppSettings from "../settings/DesktopAppSettings.ts"; import * as DesktopShellEnvironment from "../shell/DesktopShellEnvironment.ts"; import * as DesktopState from "./DesktopState.ts"; @@ -64,6 +65,24 @@ const { logInfo: logBootstrapInfo, logWarning: logBootstrapWarning } = const { logInfo: logStartupInfo, logError: logStartupError } = DesktopObservability.makeComponentLogger("desktop-startup"); +export function resolveDesktopBackendPortHint( + existingBackendHttpBaseUrl: string | undefined, + configuredPort: Option.Option, +): Option.Option { + if (existingBackendHttpBaseUrl !== undefined) { + try { + const port = Number.parseInt(new URL(existingBackendHttpBaseUrl).port, 10); + if (Number.isSafeInteger(port) && port > 0 && port <= MAX_TCP_PORT) { + return Option.some(port); + } + } catch { + // Fall through to the configured port when the live marker is malformed. + } + } + + return configuredPort; +} + const resolveDesktopBackendPort = Effect.fn("resolveDesktopBackendPort")(function* ( configuredPort: Option.Option, ) { @@ -152,7 +171,10 @@ const bootstrap = Effect.gen(function* () { return yield* new DesktopDevelopmentBackendPortRequiredError(); } - const backendPortSelection = yield* resolveDesktopBackendPort(environment.configuredBackendPort); + const existingBackend = readLiveExistingBackend(environment.stateDir); + const backendPortSelection = yield* resolveDesktopBackendPort( + resolveDesktopBackendPortHint(existingBackend?.httpBaseUrl, environment.configuredBackendPort), + ); const backendPort = backendPortSelection.port; yield* logBootstrapInfo( backendPortSelection.selectedByScan @@ -163,6 +185,13 @@ const bootstrap = Effect.gen(function* () { ...(backendPortSelection.selectedByScan ? { startPort: DEFAULT_DESKTOP_BACKEND_PORT } : {}), }, ); + if (existingBackend) { + yield* logBootstrapInfo("reusing existing backend for shared T3 home", { + pid: existingBackend.pid, + httpBaseUrl: existingBackend.httpBaseUrl, + stateDir: existingBackend.stateDir, + }); + } const settings = yield* desktopSettings.get; if (settings.serverExposureMode !== environment.defaultDesktopSettings.serverExposureMode) {