From 1b02af101923cfb515aee3ce77b78e6de6379c5c Mon Sep 17 00:00:00 2001 From: huxcrux Date: Sun, 5 Jul 2026 13:50:27 +0200 Subject: [PATCH 1/3] Fix primary environment origin on LAN web UI Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../environments/primary/bootstrap.test.ts | 28 +++++++++++- apps/web/src/environments/primary/target.ts | 44 ++++++++++++++++--- 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/apps/web/src/environments/primary/bootstrap.test.ts b/apps/web/src/environments/primary/bootstrap.test.ts index e200ecf2ff8..6cfac25cda5 100644 --- a/apps/web/src/environments/primary/bootstrap.test.ts +++ b/apps/web/src/environments/primary/bootstrap.test.ts @@ -60,6 +60,9 @@ describe("environmentBootstrap", () => { beforeEach(() => { vi.restoreAllMocks(); vi.useRealTimers(); + vi.stubEnv("VITE_HTTP_URL", ""); + vi.stubEnv("VITE_WS_URL", ""); + vi.stubEnv("VITE_DEV_SERVER_URL", ""); installTestBrowser("http://localhost/"); }); @@ -165,6 +168,22 @@ describe("environmentBootstrap", () => { ); }); + it("uses the current network origin over a configured loopback descriptor target", async () => { + vi.stubEnv("VITE_HTTP_URL", "http://127.0.0.1:4173"); + vi.stubEnv("VITE_WS_URL", "ws://127.0.0.1:4173"); + installTestBrowser("http://192.168.1.20:4173/"); + await installDescriptorApi(); + + await expect(resolveInitialPrimaryEnvironmentDescriptor()).resolves.toEqual(BASE_ENVIRONMENT); + expect(resolvePrimaryEnvironmentHttpUrl("/.well-known/t3/environment")).toBe( + "http://192.168.1.20:4173/.well-known/t3/environment", + ); + expect(getPrimaryKnownEnvironment()?.target).toEqual({ + httpBaseUrl: "http://192.168.1.20:4173/", + wsBaseUrl: "ws://192.168.1.20:4173/", + }); + }); + it("uses the vite proxy for desktop-managed loopback descriptor requests during local dev", async () => { vi.stubEnv("VITE_DEV_SERVER_URL", "http://127.0.0.1:5733"); vi.stubGlobal("window", { @@ -190,6 +209,13 @@ describe("environmentBootstrap", () => { expect(resolvePrimaryEnvironmentHttpUrl("/.well-known/t3/environment")).toBe( "http://127.0.0.1:5733/.well-known/t3/environment", ); + expect(readPrimaryEnvironmentTarget()).toEqual({ + source: "desktop-managed", + target: { + httpBaseUrl: "http://127.0.0.1:3773/", + wsBaseUrl: "ws://127.0.0.1:3773/", + }, + }); }); it("retains the URL parser cause without exposing the configured URL in its message", () => { @@ -212,7 +238,7 @@ describe("environmentBootstrap", () => { it("describes which desktop bootstrap endpoint is missing", () => { vi.stubGlobal("window", { - location: new URL("http://127.0.0.1:5733/"), + location: new URL("t3code-dev://app/"), history: { replaceState: vi.fn() }, desktopBridge: { getLocalEnvironmentBootstraps: () => [ diff --git a/apps/web/src/environments/primary/target.ts b/apps/web/src/environments/primary/target.ts index cc002419fe7..0e2b88456b7 100644 --- a/apps/web/src/environments/primary/target.ts +++ b/apps/web/src/environments/primary/target.ts @@ -267,6 +267,32 @@ function resolveDesktopPrimaryTarget(): PrimaryEnvironmentTarget | null { }; } +function isHttpWindowOrigin(): boolean { + return window.location.protocol === "http:" || window.location.protocol === "https:"; +} + +function shouldPreferWindowOriginOverConfiguredTarget(target: PrimaryEnvironmentTarget): boolean { + if (!isHttpWindowOrigin()) { + return false; + } + + const currentUrl = parseTargetUrl({ + rawValue: window.location.href, + source: "window-origin", + urlKind: "window-location-url", + }); + if (isLoopbackHostname(currentUrl.hostname)) { + return false; + } + + const targetUrl = parseTargetUrl({ + rawValue: target.target.httpBaseUrl, + source: target.source, + urlKind: "http-base-url", + }); + return isLoopbackHostname(targetUrl.hostname); +} + export function resolvePrimaryEnvironmentHttpUrl( pathname: string, searchParams?: Record, @@ -286,9 +312,17 @@ export function resolvePrimaryEnvironmentHttpUrl( } export function readPrimaryEnvironmentTarget(): PrimaryEnvironmentTarget { - return ( - resolveDesktopPrimaryTarget() ?? - resolveConfiguredPrimaryTarget() ?? - resolveWindowOriginPrimaryTarget() - ); + const configuredTarget = resolveConfiguredPrimaryTarget(); + if (configuredTarget) { + if (shouldPreferWindowOriginOverConfiguredTarget(configuredTarget)) { + return resolveWindowOriginPrimaryTarget(); + } + return configuredTarget; + } + + if (isHttpWindowOrigin() && !isLoopbackHostname(window.location.hostname)) { + return resolveWindowOriginPrimaryTarget(); + } + + return resolveDesktopPrimaryTarget() ?? resolveWindowOriginPrimaryTarget(); } From 879b84a99eceb2ac528274222767ae023ad538e7 Mon Sep 17 00:00:00 2001 From: huxcrux Date: Mon, 6 Jul 2026 10:55:31 +0200 Subject: [PATCH 2/3] Stabilize Cursor cancellation log wait Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- apps/server/src/provider/Layers/CursorAdapter.test.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/server/src/provider/Layers/CursorAdapter.test.ts b/apps/server/src/provider/Layers/CursorAdapter.test.ts index 89e9c56eb8a..2b5e652d096 100644 --- a/apps/server/src/provider/Layers/CursorAdapter.test.ts +++ b/apps/server/src/provider/Layers/CursorAdapter.test.ts @@ -2,6 +2,7 @@ import * as NodePath from "node:path"; import * as NodeOS from "node:os"; import * as NodeFSP from "node:fs/promises"; +import * as NodeTimersPromises from "node:timers/promises"; import * as NodeURL from "node:url"; import * as NodeServices from "@effect/platform-node/NodeServices"; @@ -116,17 +117,21 @@ async function waitForFileContent(filePath: string, attempts = 40) { function waitForJsonLogMatch( filePath: string, predicate: (entry: Record) => boolean, - attempts = 40, + attempts = 100, ) { return Effect.gen(function* () { + let latestRequests: ReadonlyArray> = []; for (let attempt = 0; attempt < attempts; attempt += 1) { const requests = yield* Effect.promise(() => readJsonLines(filePath)); + latestRequests = requests; if (requests.some(predicate)) { return requests; } - yield* Effect.yieldNow; + yield* Effect.promise(() => NodeTimersPromises.setTimeout(10)); } - return yield* Effect.promise(() => readJsonLines(filePath)); + throw new Error( + `Timed out waiting for JSON log entry in ${filePath}; observed ${latestRequests.length} entries.`, + ); }); } From d4b0be32f1868c4b808bb73c3faec705b76ecd01 Mon Sep 17 00:00:00 2001 From: huxcrux Date: Mon, 6 Jul 2026 11:03:24 +0200 Subject: [PATCH 3/3] Revert Cursor cancellation log wait change Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- apps/server/src/provider/Layers/CursorAdapter.test.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/apps/server/src/provider/Layers/CursorAdapter.test.ts b/apps/server/src/provider/Layers/CursorAdapter.test.ts index 2b5e652d096..89e9c56eb8a 100644 --- a/apps/server/src/provider/Layers/CursorAdapter.test.ts +++ b/apps/server/src/provider/Layers/CursorAdapter.test.ts @@ -2,7 +2,6 @@ import * as NodePath from "node:path"; import * as NodeOS from "node:os"; import * as NodeFSP from "node:fs/promises"; -import * as NodeTimersPromises from "node:timers/promises"; import * as NodeURL from "node:url"; import * as NodeServices from "@effect/platform-node/NodeServices"; @@ -117,21 +116,17 @@ async function waitForFileContent(filePath: string, attempts = 40) { function waitForJsonLogMatch( filePath: string, predicate: (entry: Record) => boolean, - attempts = 100, + attempts = 40, ) { return Effect.gen(function* () { - let latestRequests: ReadonlyArray> = []; for (let attempt = 0; attempt < attempts; attempt += 1) { const requests = yield* Effect.promise(() => readJsonLines(filePath)); - latestRequests = requests; if (requests.some(predicate)) { return requests; } - yield* Effect.promise(() => NodeTimersPromises.setTimeout(10)); + yield* Effect.yieldNow; } - throw new Error( - `Timed out waiting for JSON log entry in ${filePath}; observed ${latestRequests.length} entries.`, - ); + return yield* Effect.promise(() => readJsonLines(filePath)); }); }