Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **fix(runtime):** eliminate hardcoded 20128 port remnants and make loopback URLs dynamic ([#13533](https://github.com/diegosouzapw/OmniRoute/pull/13533)) — thanks @ggdayup
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
"lint:json": "node scripts/quality/run-eslint-json.mjs",
"lint:md": "npx --yes markdownlint-cli2 \"docs/**/*.md\" \"*.md\" \"!docs/i18n\" \"!docs/research\"",
"lint:prose": "vale docs",
"electron:dev": "concurrently \"npm run dev\" \"wait-on http://localhost:20128 && cd electron && npm run dev\"",
"electron:dev": "concurrently \"npm run dev\" \"wait-on http://localhost:${PORT:-20128} && cd electron && npm run dev\"",
"electron:build": "npm run build && cd electron && npm run build",
"electron:build:win": "npm run build && cd electron && npm run build:win",
"electron:build:mac": "npm run build && cd electron && npm run build:mac",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,9 @@ export default function APIPageClient({ machineId }: Readonly<APIPageClientProps
const [ngrokToken, setNgrokToken] = useState("");
const [showNgrokTunnel, setShowNgrokTunnel] = useState(true);
const [expandedTunnel, setExpandedTunnel] = useState<string | null>(null);
const [localApiUrl, setLocalApiUrl] = useState("http://localhost:20128/v1");
const [localApiUrl, setLocalApiUrl] = useState(
typeof window !== "undefined" ? `${window.location.origin}/v1` : "http://localhost:20128/v1"
);
const [lanUrls, setLanUrls] = useState<string[]>([]);
const [tailscaleIpUrl, setTailscaleIpUrl] = useState<string | null>(null);
const [activeEndpointTab, setActiveEndpointTab] = useState<EndpointTab>("apis");
Expand Down
4 changes: 3 additions & 1 deletion src/app/(dashboard)/dashboard/relay/RelayProxyClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Card from "@/shared/components/Card";
import Badge from "@/shared/components/Badge";
import Button from "@/shared/components/Button";
import { useNotificationStore } from "@/store/notificationStore";
import { useDisplayBaseUrl } from "@/shared/hooks";

interface RelayToken {
id: string;
Expand All @@ -23,6 +24,7 @@ interface RelayToken {

export default function RelayProxyClient() {
const t = useTranslations("relay");
const displayBaseUrl = useDisplayBaseUrl();
const [tokens, setTokens] = useState<RelayToken[]>([]);
const [loading, setLoading] = useState(true);
const [showCreate, setShowCreate] = useState(false);
Expand Down Expand Up @@ -193,7 +195,7 @@ export default function RelayProxyClient() {
<h2 className="text-sm font-semibold">{t("usage")}</h2>
<p className="text-xs text-text-muted">{t("usageDescription")}</p>
<pre className="text-xs bg-surface/50 border border-border rounded-lg p-3 overflow-x-auto">
{`curl http://localhost:20128/v1/relay/chat/completions \\
{`curl ${displayBaseUrl}/v1/relay/chat/completions \\
-H "Authorization: Bearer relay_..." \\
-H "Content-Type: application/json" \\
-d '{"model":"claude-sonnet-4","messages":[{"role":"user","content":"Hello"}]}'`}
Expand Down
3 changes: 2 additions & 1 deletion src/app/(dashboard)/dashboard/tools/agent-bridge/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ export default async function AgentBridgePage() {
try {
const base =
process.env.OMNIROUTE_BASE_URL ??
`http://127.0.0.1:${process.env.PORT ?? 20128}`;
process.env.BASE_URL ??
`http://127.0.0.1:${process.env.DASHBOARD_PORT ?? process.env.PORT ?? 20128}`;
const res = await fetch(`${base}/api/tools/agent-bridge/state`, {
cache: "no-store",
headers: { "x-internal-fetch": "1" },
Expand Down
20 changes: 16 additions & 4 deletions src/app/api/assess/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,18 @@ import {
import { validateBody } from "@/shared/validation/helpers";
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error";

function getAssessBaseUrl(): string {
return (
process.env.OMNIROUTE_BASE_URL ??
process.env.OMNIROUTe_BASE_URL ??
process.env.BASE_URL ??
`http://localhost:${process.env.API_PORT ?? process.env.PORT ?? 20128}/v1`
);
}

const assessor = new Assessor(
process.env.OMNIROUTe_API_KEY ?? process.env.API_KEY ?? "",
process.env.OMNIROUTe_BASE_URL ?? "http://localhost:20128/v1"
process.env.OMNIROUTE_API_KEY ?? process.env.OMNIROUTe_API_KEY ?? process.env.API_KEY ?? "",
getAssessBaseUrl()
);

const categorizer = new Categorizer();
Expand Down Expand Up @@ -142,9 +151,12 @@ export async function GET(request: NextRequest) {

async function getAllModels(): Promise<Array<{ providerId: string; modelId: string }>> {
try {
const resp = await fetch("http://localhost:20128/v1/models", {
const baseUrl = getAssessBaseUrl();
const apiKey =
process.env.OMNIROUTE_API_KEY ?? process.env.OMNIROUTe_API_KEY ?? process.env.API_KEY ?? "";
const resp = await fetch(`${baseUrl}/models`, {
headers: {
Authorization: `Bearer ${process.env.OMNIROUTe_API_KEY ?? process.env.API_KEY ?? ""}`,
Authorization: `Bearer ${apiKey}`,
},
});
const data = (await resp.json()) as { data?: unknown };
Expand Down
8 changes: 7 additions & 1 deletion src/app/api/cli-tools/apply/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,14 @@ export async function POST(request: Request) {
const { toolId, baseUrl, apiKey, model, dryRun } = parsed.data;
const canonicalToolId = normalizeCliToolId(toolId);

const defaultPort = process.env.API_PORT || process.env.PORT || 20128;
const defaultBaseUrl =
process.env.OMNIROUTE_BASE_URL ||
process.env.BASE_URL ||
`http://localhost:${defaultPort}/v1`;

const result = await generateConfig(canonicalToolId, {
baseUrl: baseUrl || "http://localhost:20128/v1",
baseUrl: baseUrl || defaultBaseUrl,
apiKey,
model,
});
Expand Down
12 changes: 10 additions & 2 deletions src/app/api/cli-tools/config/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ export async function GET(request: Request) {
if (authError) return authError;

const { searchParams } = new URL(request.url);
const baseUrl = searchParams.get("baseUrl") || "http://localhost:20128/v1";
const defaultPort = process.env.API_PORT || process.env.PORT || 20128;
const defaultBaseUrl =
process.env.OMNIROUTE_BASE_URL || process.env.BASE_URL || `http://localhost:${defaultPort}/v1`;
const baseUrl = searchParams.get("baseUrl") || defaultBaseUrl;
const apiKey = searchParams.get("apiKey") || "";

if (!apiKey) {
Expand Down Expand Up @@ -46,9 +49,14 @@ export async function POST(request: Request) {
);
}
const { toolId, baseUrl, apiKey, model } = parsed.data;
const defaultPort = process.env.API_PORT || process.env.PORT || 20128;
const defaultBaseUrl =
process.env.OMNIROUTE_BASE_URL ||
process.env.BASE_URL ||
`http://localhost:${defaultPort}/v1`;

const result = await generateConfig(toolId, {
baseUrl: baseUrl || "http://localhost:20128/v1",
baseUrl: baseUrl || defaultBaseUrl,
apiKey,
model,
});
Expand Down
23 changes: 10 additions & 13 deletions src/app/api/cli-tools/letta-settings/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,13 @@ const readAuthFile = async () => {
// ── Check if a base_url points to OmniRoute ──────────────────────────────
const isOmniRouteUrl = (baseUrl) => {
if (!baseUrl) return false;
return baseUrl.includes(":20128") || baseUrl.includes(":3000") || baseUrl.includes("omniroute");
const port = process.env.PORT || process.env.DASHBOARD_PORT;
return (
baseUrl.includes(":20128") ||
baseUrl.includes(":3000") ||
(!!port && baseUrl.includes(`:${port}`)) ||
baseUrl.includes("omniroute")
);
};

// ── Check if OmniRoute is configured ─────────────────────────────────────
Expand Down Expand Up @@ -122,10 +128,7 @@ export async function GET(request: Request) {
backendMode: settings.preferredBackendMode || "api",
});
} catch (error) {
return NextResponse.json(
{ error: { message: sanitizeErrorMessage(error) } },
{ status: 500 }
);
return NextResponse.json({ error: { message: sanitizeErrorMessage(error) } }, { status: 500 });
}
}

Expand Down Expand Up @@ -246,10 +249,7 @@ export async function POST(request: Request) {
needsRestart: true,
});
} catch (error) {
return NextResponse.json(
{ error: { message: sanitizeErrorMessage(error) } },
{ status: 500 }
);
return NextResponse.json({ error: { message: sanitizeErrorMessage(error) } }, { status: 500 });
}
}

Expand Down Expand Up @@ -321,9 +321,6 @@ export async function DELETE(request: Request) {
needsRestart: true,
});
} catch (error) {
return NextResponse.json(
{ error: { message: sanitizeErrorMessage(error) } },
{ status: 500 }
);
return NextResponse.json({ error: { message: sanitizeErrorMessage(error) } }, { status: 500 });
}
}
2 changes: 1 addition & 1 deletion src/app/api/playground/improve-prompt/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export async function POST(request: Request): Promise<Response> {
const chatBody = buildImproveChatBody(body);

// 5. Call /v1/chat/completions on ourselves (D8)
const port = process.env.PORT ?? "20128";
const port = process.env.API_PORT ?? process.env.PORT ?? "20128";
const baseUrl = process.env.OMNIROUTE_BASE_URL ?? `http://127.0.0.1:${port}`;
const upstreamUrl = `${baseUrl}/v1/chat/completions`;

Expand Down
3 changes: 2 additions & 1 deletion src/app/api/sync/cloud/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ async function handleDisable(machineId: string, request: any) {
}

// Update Claude CLI settings to use local endpoint
const host = request.headers.get("host") || "localhost:20128";
const defaultPort = process.env.PORT || process.env.DASHBOARD_PORT || "20128";
const host = request.headers.get("host") || `localhost:${defaultPort}`;
await updateClaudeSettingsToLocal(machineId, host);

return NextResponse.json({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@ interface Params {
params: Promise<{ id: string }>;
}

const OMNIROUTE_BASE = process.env.OMNIROUTE_BASE_URL ?? "http://127.0.0.1:20128";
function getOmnirouteBaseUrl(): string {
const port = process.env.API_PORT || process.env.PORT || 20128;
return (
process.env.OMNIROUTE_BASE_URL ||
process.env.BASE_URL ||
`http://127.0.0.1:${port}`
).replace(/\/+$/, "");
}

export async function POST(_request: Request, { params }: Params): Promise<Response> {
const { id } = await params;
Expand All @@ -27,7 +34,7 @@ export async function POST(_request: Request, { params }: Params): Promise<Respo
});
}

const url = `${OMNIROUTE_BASE}${entry.path}`;
const url = `${getOmnirouteBaseUrl()}${entry.path}`;

const replayHeaders: Record<string, string> = {
"content-type": "application/json",
Expand Down
4 changes: 3 additions & 1 deletion src/app/docs/components/ApiExplorerClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ export function ApiExplorerClient() {
const t = useTranslations("docs");
const te = useTranslations("endpoint");
const [selected, setSelected] = useState<OpenApiEndpoint | null>(null);
const [baseUrl, setBaseUrl] = useState("http://localhost:20128");
const [baseUrl, setBaseUrl] = useState(
typeof window !== "undefined" ? window.location.origin : "http://localhost:20128"
);
const [apiKey, setApiKey] = useState("");
const [requestBody, setRequestBody] = useState("");
const [response, setResponse] = useState<string | null>(null);
Expand Down
4 changes: 3 additions & 1 deletion src/domain/assessment/assessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ export class Assessor {

constructor(
apiKey: string,
baseUrl: string = "http://localhost:20128/v1",
baseUrl: string = process.env.OMNIROUTE_BASE_URL ??
process.env.BASE_URL ??
`http://localhost:${process.env.API_PORT ?? process.env.PORT ?? 20128}/v1`,
config: Partial<AssessmentConfig> = {}
) {
this.apiKey = apiKey;
Expand Down
7 changes: 6 additions & 1 deletion src/lib/cli-helper/log-streamer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ export interface LogStream {
}

export function createLogStream(options: LogStreamOptions = {}): LogStream {
const baseUrl = options.baseUrl || "http://localhost:20128";
const port = process.env.PORT || process.env.DASHBOARD_PORT || 20128;
const baseUrl =
options.baseUrl ||
process.env.OMNIROUTE_BASE_URL ||
process.env.BASE_URL ||
`http://localhost:${port}`;
const filters = options.filters || [];
const follow = options.follow ?? false;
const timeout = options.timeout || 30000;
Expand Down
10 changes: 8 additions & 2 deletions src/lib/cli-helper/tool-detector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,11 @@ function expandHome(p: string): string {

function isConfigured(content: string, baseUrl: string): boolean {
const normalized = baseUrl.replace(/\/+$/, "");
const port = process.env.PORT || process.env.DASHBOARD_PORT;
return (
content.includes(normalized) ||
content.includes("localhost:20128") ||
(!!port && content.includes(`localhost:${port}`)) ||
content.includes("OMNIROUTE_BASE_URL")
);
}
Expand Down Expand Up @@ -170,7 +172,9 @@ export async function detectTool(id: string): Promise<DetectedTool | null> {
: getCliPrimaryConfigPath(tool.id) ||
(tool.id === "opencode" ? resolveOpencodeConfigPath() : "");
const configContents = await readConfigFile(configPath);
const configured = !!configContents && isConfigured(configContents, "http://localhost:20128");
const defaultPort = process.env.PORT || process.env.DASHBOARD_PORT || 20128;
const configured =
!!configContents && isConfigured(configContents, `http://localhost:${defaultPort}`);

const result: DetectedTool = {
id: canonicalId,
Expand All @@ -187,12 +191,14 @@ export async function detectTool(id: string): Promise<DetectedTool | null> {
try {
const roles = await getCurrentHermesAgentRoles();
const richRoles: Record<string, any> = {};
const currentPort = String(process.env.PORT || process.env.DASHBOARD_PORT || 20128);

Object.entries(roles).forEach(([role, info]) => {
const usingOmni =
info?.provider === "omniroute" ||
(info?.base_url || "").includes("20128") ||
(info?.base_url || "").includes("localhost:20128");
(info?.base_url || "").includes(currentPort) ||
(info?.base_url || "").includes("localhost");

richRoles[role] = {
model: info.model,
Expand Down
3 changes: 2 additions & 1 deletion src/lib/wellKnown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ export function getBaseUrl(request?: NextRequest | null): string {
if (process.env.OMNIROUTE_BASE_URL) return process.env.OMNIROUTE_BASE_URL;
// Direct route-handler invocation (unit tests, programmatic calls) passes no
// Request — fall back to the default local gateway origin instead of crashing.
return request?.nextUrl?.origin ?? "http://localhost:20128";
const defaultPort = process.env.PORT || process.env.DASHBOARD_PORT || 20128;
return request?.nextUrl?.origin ?? `http://localhost:${defaultPort}`;
}
4 changes: 3 additions & 1 deletion src/mitm/handlers/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,9 @@ export abstract class MitmHandlerBase {
path: string,
headers: IncomingHttpHeaders,
): Promise<Response> {
const base = process.env.OMNIROUTE_BASE_URL ?? "http://127.0.0.1:20128";
const port = process.env.API_PORT || process.env.PORT || 20128;
const base =
process.env.OMNIROUTE_BASE_URL ?? process.env.BASE_URL ?? `http://127.0.0.1:${port}`;
const url = `${base.replace(/\/+$/, "")}${path}`;
const apiKey = process.env.ROUTER_API_KEY ?? "";

Expand Down
2 changes: 1 addition & 1 deletion src/mitm/server.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const MITM_IDLE_TIMEOUT_MS =
const ROUTER_BASE_URL = (
process.env.OMNIROUTE_BASE_URL ||
process.env.BASE_URL ||
"http://localhost:20128"
`http://localhost:${process.env.API_PORT || process.env.PORT || 20128}`
)
.trim()
.replace(/\/+$/, "");
Expand Down
10 changes: 4 additions & 6 deletions src/server/origin/publicOrigin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ import { PEER_IP_HEADER } from "@/server/authz/headers";
import { resolveStampedPeer } from "@/server/authz/peerStamp";

export type PublicOriginSource =
| "configured"
| "trusted-forwarded"
| "request-url"
| "direct-local-host";
"configured" | "trusted-forwarded" | "request-url" | "direct-local-host";

export interface PublicOriginCandidate {
origin: string;
Expand Down Expand Up @@ -200,7 +197,7 @@ function directLocalHostOrigin(request: Request): string | null {
if (classifyHostLocality(peer) === "remote") return null;

const rawHost = trustsForwardedHeaders(request)
? firstHeaderValue(request.headers.get("x-forwarded-host")) ?? request.headers.get("host")
? (firstHeaderValue(request.headers.get("x-forwarded-host")) ?? request.headers.get("host"))
: request.headers.get("host");
const host = sanitizeForwardedHost(rawHost);
if (!host) return null;
Expand Down Expand Up @@ -246,7 +243,8 @@ export function resolvePublicOrigin(request: Request): PublicOriginCandidate {
const requestOrigin = requestUrlOrigin(request);
if (requestOrigin) return { origin: requestOrigin, source: "request-url" };

return { origin: "http://localhost:20128", source: "request-url" };
const defaultPort = process.env.PORT || process.env.DASHBOARD_PORT || "20128";
return { origin: `http://localhost:${defaultPort}`, source: "request-url" };
}

export function validateBrowserMutationOrigin(request: Request): BrowserMutationOriginVerdict {
Expand Down
12 changes: 11 additions & 1 deletion src/server/ws/liveServerAllowList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,17 @@ export function parseCsvEnv(value: string | undefined | null): Set<string> {
*/
export function buildAllowedOrigins(env: NodeJS.ProcessEnv = process.env): Set<string> {
const extra = parseCsvEnv(env.LIVE_WS_ALLOWED_ORIGINS);
return new Set([...DEFAULT_ALLOWED_ORIGINS, ...extra]);
const runtimePort = env.PORT || env.DASHBOARD_PORT;
const dynamicDefaults: string[] = [];
if (runtimePort && runtimePort !== "20128") {
dynamicDefaults.push(
`http://127.0.0.1:${runtimePort}`,
`http://localhost:${runtimePort}`,
`http://[::1]:${runtimePort}`,
`http://0.0.0.0:${runtimePort}`
);
}
return new Set([...DEFAULT_ALLOWED_ORIGINS, ...dynamicDefaults, ...extra]);
}

/**
Expand Down
6 changes: 5 additions & 1 deletion src/shared/hooks/useDisplayBaseUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,11 @@ export function resolveDisplayBaseUrl(
return joinOriginAndBasePath(configuredUrl, basePath);
}

const fallback = currentOrigin ?? configuredUrl ?? DEFAULT_DISPLAY_BASE_URL;
const portFallback =
typeof process !== "undefined" && (process.env.NEXT_PUBLIC_PORT || process.env.PORT)
? `http://localhost:${process.env.NEXT_PUBLIC_PORT || process.env.PORT}`
: DEFAULT_DISPLAY_BASE_URL;
const fallback = currentOrigin ?? configuredUrl ?? portFallback;
return joinOriginAndBasePath(fallback, basePath);
}

Expand Down
Loading
Loading