Use dev proxy for loopback auth and environment requests#1853
Use dev proxy for loopback auth and environment requests#1853juliusmarminge merged 2 commits intomainfrom
Conversation
- Route local desktop-managed bootstrap and auth requests through the Vite proxy - Preserve terminal events for draft-only threads and avoid reopening drawers on stable refs - Add tests for the new runtime terminal-event gating and proxy behavior
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Duplicate loopback hostname check across files
- Extracted the duplicated isLoopbackHostname logic into a shared utility at @t3tools/shared/hostname and updated all three consumers (apps/server/src/http.ts, apps/web/src/environments/primary/target.ts, apps/web/src/components/settings/ConnectionsSettings.tsx) to import from it.
Or push these changes by commenting:
@cursor push a07754f7f2
Preview (a07754f7f2)
diff --git a/apps/server/src/http.test.ts b/apps/server/src/http.test.ts
--- a/apps/server/src/http.test.ts
+++ b/apps/server/src/http.test.ts
@@ -1,7 +1,9 @@
import { describe, expect, it } from "vitest";
-import { isLoopbackHostname, resolveDevRedirectUrl } from "./http.ts";
+import { isLoopbackHostname } from "@t3tools/shared/hostname";
+import { resolveDevRedirectUrl } from "./http.ts";
+
describe("http dev routing", () => {
it("treats localhost and loopback addresses as local", () => {
expect(isLoopbackHostname("127.0.0.1")).toBe(true);
diff --git a/apps/server/src/http.ts b/apps/server/src/http.ts
--- a/apps/server/src/http.ts
+++ b/apps/server/src/http.ts
@@ -11,6 +11,8 @@
} from "effect/unstable/http";
import { OtlpTracer } from "effect/unstable/observability";
+import { isLoopbackHostname } from "@t3tools/shared/hostname";
+
import {
ATTACHMENTS_ROUTE_PREFIX,
normalizeAttachmentRelativePath,
@@ -28,7 +30,6 @@
const PROJECT_FAVICON_CACHE_CONTROL = "public, max-age=3600";
const FALLBACK_PROJECT_FAVICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="#6b728080" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-fallback="project-favicon"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-8l-2-2H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2Z"/></svg>`;
const OTLP_TRACES_PROXY_PATH = "/api/observability/v1/traces";
-const LOOPBACK_HOSTNAMES = new Set(["127.0.0.1", "::1", "localhost"]);
export const browserApiCorsLayer = HttpRouter.cors({
allowedMethods: ["GET", "POST", "OPTIONS"],
@@ -36,14 +37,6 @@
maxAge: 600,
});
-export function isLoopbackHostname(hostname: string): boolean {
- const normalizedHostname = hostname
- .trim()
- .toLowerCase()
- .replace(/^\[(.*)\]$/, "$1");
- return LOOPBACK_HOSTNAMES.has(normalizedHostname);
-}
-
export function resolveDevRedirectUrl(devUrl: URL, requestUrl: URL): string {
const redirectUrl = new URL(devUrl.toString());
redirectUrl.pathname = requestUrl.pathname;
diff --git a/apps/web/src/components/settings/ConnectionsSettings.tsx b/apps/web/src/components/settings/ConnectionsSettings.tsx
--- a/apps/web/src/components/settings/ConnectionsSettings.tsx
+++ b/apps/web/src/components/settings/ConnectionsSettings.tsx
@@ -46,6 +46,8 @@
import { Button } from "../ui/button";
import { Textarea } from "../ui/textarea";
import { setPairingTokenOnUrl } from "../../pairingUrl";
+import { isLoopbackHostname } from "@t3tools/shared/hostname";
+
import {
createServerPairingCredential,
fetchSessionState,
@@ -253,16 +255,6 @@
return setPairingTokenOnUrl(url, credential).toString();
}
-function isLoopbackHostname(hostname: string): boolean {
- const normalized = hostname.trim().toLowerCase();
- return (
- normalized === "localhost" ||
- normalized === "127.0.0.1" ||
- normalized === "::1" ||
- normalized === "[::1]"
- );
-}
-
type PairingLinkListRowProps = {
pairingLink: ServerPairingLinkRecord;
endpointUrl: string | null | undefined;
diff --git a/apps/web/src/environments/primary/target.ts b/apps/web/src/environments/primary/target.ts
--- a/apps/web/src/environments/primary/target.ts
+++ b/apps/web/src/environments/primary/target.ts
@@ -1,13 +1,12 @@
import type { DesktopEnvironmentBootstrap } from "@t3tools/contracts";
import type { KnownEnvironment } from "@t3tools/client-runtime";
+import { isLoopbackHostname } from "@t3tools/shared/hostname";
export interface PrimaryEnvironmentTarget {
readonly source: KnownEnvironment["source"];
readonly target: KnownEnvironment["target"];
}
-const LOOPBACK_HOSTNAMES = new Set(["127.0.0.1", "::1", "localhost"]);
-
function getDesktopLocalEnvironmentBootstrap(): DesktopEnvironmentBootstrap | null {
return window.desktopBridge?.getLocalEnvironmentBootstrap() ?? null;
}
@@ -16,17 +15,6 @@
return new URL(rawValue, window.location.origin).toString();
}
-function normalizeHostname(hostname: string): string {
- return hostname
- .trim()
- .toLowerCase()
- .replace(/^\[(.*)\]$/, "$1");
-}
-
-function isLoopbackHostname(hostname: string): boolean {
- return LOOPBACK_HOSTNAMES.has(normalizeHostname(hostname));
-}
-
function resolveHttpRequestBaseUrl(httpBaseUrl: string): string {
const configuredDevServerUrl = import.meta.env.VITE_DEV_SERVER_URL?.trim();
if (!configuredDevServerUrl) {
diff --git a/packages/shared/package.json b/packages/shared/package.json
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -51,6 +51,10 @@
"./projectScripts": {
"types": "./src/projectScripts.ts",
"import": "./src/projectScripts.ts"
+ },
+ "./hostname": {
+ "types": "./src/hostname.ts",
+ "import": "./src/hostname.ts"
}
},
"scripts": {
diff --git a/packages/shared/src/hostname.ts b/packages/shared/src/hostname.ts
new file mode 100644
--- /dev/null
+++ b/packages/shared/src/hostname.ts
@@ -1,0 +1,12 @@
+const LOOPBACK_HOSTNAMES = new Set(["127.0.0.1", "::1", "localhost"]);
+
+function normalizeHostname(hostname: string): string {
+ return hostname
+ .trim()
+ .toLowerCase()
+ .replace(/^\[(.*)\]$/, "$1");
+}
+
+export function isLoopbackHostname(hostname: string): boolean {
+ return LOOPBACK_HOSTNAMES.has(normalizeHostname(hostname));
+}You can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit 04e211a. Configure here.
ApprovabilityA prior Macroscope approval was dismissed. Re-evaluating f4d89f8… |
Dismissing prior approval to re-evaluate f4d89f8
…threadId (#2) * Raise slow RPC ack warning threshold to 15s (pingdotgg#1760) * Use active worktree path for workspace saves (pingdotgg#1762) * Stream git status updates over WebSocket (pingdotgg#1763) Co-authored-by: codex <codex@users.noreply.github.com> * fix(web): unwrap windows shell command wrappers (pingdotgg#1719) * Rename "Chat" to "Build" in interaction mode toggle (pingdotgg#1769) Co-authored-by: Julius Marminge <julius0216@outlook.com> * Assign default capabilities to Codex custom models (pingdotgg#1793) * Add project rename support in the sidebar (pingdotgg#1798) * Support multi-select pending user inputs (pingdotgg#1797) * Add Zed support to Open actions via editor command aliases (pingdotgg#1303) Co-authored-by: codex <codex@users.noreply.github.com> Co-authored-by: Julius Marminge <julius0216@outlook.com> * Closes pingdotgg#1795 - Support building and developing in a devcontainer (pingdotgg#1791) * Add explicit timeouts to CI and release workflows (pingdotgg#1825) * fix(web): distinguish singular/plural in pending action submit label (pingdotgg#1826) * Refactor web stores into atomic slices ready to split ChatView (pingdotgg#1708) * Add VSCode Insiders and VSCodium icons (pingdotgg#1847) * Prepare datamodel for multi-environment (pingdotgg#1765) Co-authored-by: justsomelegs <145564979+justsomelegs@users.noreply.github.com> Co-authored-by: codex <codex@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com> * Implement server auth bootstrap and pairing flow (pingdotgg#1768) Co-authored-by: codex <codex@users.noreply.github.com> Co-authored-by: Julius Marminge <julius@macmini.local> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com> * Use dev proxy for loopback auth and environment requests (pingdotgg#1853) * Refresh local git status on turn completion (pingdotgg#1821) Co-authored-by: codex <codex@users.noreply.github.com> * fix(desktop): add Copy Link action for chat links (pingdotgg#1835) * fix: map runtime modes to correct permission levels (pingdotgg#1587) Co-authored-by: Julius Marminge <julius0216@outlook.com> Co-authored-by: codex <codex@users.noreply.github.com> * Fix persisted composer image hydration typo (pingdotgg#1831) * Clarify environment and workspace picker labels (pingdotgg#1854) * Scope git toast state by thread ref (pingdotgg#1855) * fix build (pingdotgg#1859) * Stabilize keybindings toast stream setup (pingdotgg#1860) Co-authored-by: Julius Marminge <julius@macmini.local> * feat(web): add embeddable thread route for canvas tile hosts Adds /embed/thread/:environmentId/:threadId — a standalone route that renders the existing ChatView without the app sidebar chrome. This is the iframe target for t3-canvas agent shapes (see rororowyourboat/t3-canvas#3). - New file-based route embed.thread.\$environmentId.\$threadId.tsx - __root.tsx bypasses AppSidebarLayout for any /embed/* pathname so the environment connection + websocket surface + toasts still initialize but the sidebar/diff/plan chrome does not render - minimal=1 search param is parsed and wired to a data attribute on the container for future targeted CSS; chrome hiding (BranchToolbar, PlanSidebar, ThreadTerminalDrawer) stays as a follow-up pass - routeTree.gen.ts regenerated by the @tanstack/router-plugin --------- Co-authored-by: Julius Marminge <julius0216@outlook.com> Co-authored-by: codex <codex@users.noreply.github.com> Co-authored-by: legs <145564979+justsomelegs@users.noreply.github.com> Co-authored-by: sonder <168988030+heysonder@users.noreply.github.com> Co-authored-by: Adem Ben Abdallah <96244394+AdemBenAbdallah@users.noreply.github.com> Co-authored-by: Kyle Gottfried <6462596+Spitfire1900@users.noreply.github.com> Co-authored-by: Jacob <589761+jvzijp@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com> Co-authored-by: Julius Marminge <julius@macmini.local> Co-authored-by: Klemencina <56873773+Klemencina@users.noreply.github.com> Co-authored-by: Oskar Sekutowicz <me.oski646@gmail.com> Co-authored-by: Noxire <59626436+noxire-dev@users.noreply.github.com>


Summary
/.well-knownalongside/apiso primary environment discovery works through the dev server.Testing
bun fmtbun lintbun typecheckbun run testNote
Medium Risk
Changes primary environment URL resolution and terminal event handling, which can impact local auth/bootstrap connectivity and terminal UX if the new loopback/dev-server detection is wrong. Scope is contained to local-dev proxying and terminal state updates, with added tests to reduce regressions.
Overview
Routes local-dev loopback bootstrap/auth HTTP calls through the Vite dev server proxy when appropriate.
resolvePrimaryEnvironmentHttpUrlnow rewrites loopbackdesktop-managedtargets to the current dev-server origin whenVITE_DEV_SERVER_URLmatches the current origin and both hosts are loopback, andvite.config.tsadds a proxy rule for/.well-knownto support environment discovery.Stabilizes terminal rendering and event replay behavior.
TerminalViewportno longer tears down/reopens when thethreadRefobject identity changes (only whenenvironmentIdchanges),ChatViewmemoizes persistent terminal drawers and pre-parses mounted thread refs, and runtime terminal events are now applied for draft-only threads while still dropping events for archived server threads viashouldApplyTerminalEvent.Adds test coverage for the new dev-proxy URL behavior, terminal viewport stability on rerender, and terminal event filtering.
Reviewed by Cursor Bugbot for commit f4d89f8. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Route loopback auth and environment requests through the Vite dev server proxy
resolveHttpRequestBaseUrlutility in target.ts./api/auth/session) and primary environment descriptor fetches, routing them through the Vite proxy instead of directly to the loopback server./.well-knownto the Vite dev server proxy rules in vite.config.ts alongside existing/apiand/attachmentsproxies.isLoopbackHostnamefrom the primary environments module and removes a duplicate local implementation in ConnectionsSettings.tsx.TerminalViewportby depending onenvironmentIdvalue rather thanthreadRefobject identity, preventing unnecessary terminal remounts.Macroscope summarized f4d89f8.