diff --git a/.env.example b/.env.example index 15fceaea12..379c72b5f9 100644 --- a/.env.example +++ b/.env.example @@ -23,7 +23,11 @@ SQLITE_MAX_SIZE_MB=2048 SQLITE_CLEAN_LEGACY_FILES=true # Recommended runtime variables +# Canonical/base port (keeps backward compatibility) PORT=20128 +# Optional split ports: +# API_PORT=20128 +# DASHBOARD_PORT=20128 NODE_ENV=production INSTANCE_NAME=omniroute diff --git a/Dockerfile b/Dockerfile index 308389163a..ffb9c07364 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,13 +27,14 @@ RUN mkdir -p /app/data COPY --from=builder /app/public ./public COPY --from=builder /app/.next/static ./.next/static COPY --from=builder /app/.next/standalone ./ +COPY --from=builder /app/scripts/run-standalone.mjs ./run-standalone.mjs EXPOSE 20128 HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ - CMD node -e "fetch('http://127.0.0.1:20128/api/settings').then(r=>{if(!r.ok)throw r.status}).catch(()=>process.exit(1))" + CMD node -e "const p=process.env.DASHBOARD_PORT||process.env.PORT||'20128';fetch('http://127.0.0.1:'+p+'/api/settings').then(r=>{if(!r.ok)throw r.status}).catch(()=>process.exit(1))" -CMD ["node", "server.js"] +CMD ["node", "run-standalone.mjs"] FROM runner-base AS runner-cli @@ -45,4 +46,3 @@ RUN apt-get update \ # Install CLI tools globally. Separate layer from apt for better cache reuse. RUN npm install -g --no-audit --no-fund @openai/codex @anthropic-ai/claude-code droid openclaw@latest - diff --git a/README.md b/README.md index 8d4c466b82..51619853fd 100644 --- a/README.md +++ b/README.md @@ -168,12 +168,22 @@ omniroute πŸŽ‰ Dashboard opens at `http://localhost:20128` -| Command | Description | -| ----------------------- | --------------------------------- | -| `omniroute` | Start server (default port 20128) | -| `omniroute --port 3000` | Use custom port | -| `omniroute --no-open` | Don't auto-open browser | -| `omniroute --help` | Show help | +| Command | Description | +| ----------------------- | ----------------------------------------------------------- | +| `omniroute` | Start server (`PORT=20128`, API and dashboard on same port) | +| `omniroute --port 3000` | Set canonical/API port to 3000 | +| `omniroute --no-open` | Don't auto-open browser | +| `omniroute --help` | Show help | + +Optional split-port mode: + +```bash +PORT=20128 DASHBOARD_PORT=20129 omniroute +# API: http://localhost:20128/v1 +# Dashboard: http://localhost:20129 +``` + +When ports are split, the API port serves only OpenAI-compatible routes (`/v1`, `/chat/completions`, `/responses`, `/models`, `/codex/*`). **2. Connect a FREE provider:** @@ -195,7 +205,7 @@ Claude Code/Codex/Gemini CLI/OpenClaw/Cursor/Cline Settings: ```bash cp .env.example .env npm install -PORT=20128 NEXT_PUBLIC_BASE_URL=http://localhost:20128 npm run dev +PORT=20128 DASHBOARD_PORT=20129 NEXT_PUBLIC_BASE_URL=http://localhost:20129 npm run dev ``` --- @@ -978,9 +988,12 @@ Se nΓ£o quiser criar credenciais prΓ³prias agora, ainda Γ© possΓ­vel usar o flux - Switch primary model to GLM/MiniMax - Use free tier (Gemini CLI, iFlow) for non-critical tasks -**Dashboard opens on wrong port** +**Dashboard/API ports are wrong** -- Set `PORT=20128` and `NEXT_PUBLIC_BASE_URL=http://localhost:20128` +- `PORT` is the canonical base port (and API port by default) +- `API_PORT` overrides only OpenAI-compatible API listener +- `DASHBOARD_PORT` overrides only dashboard/Next.js listener +- Set `NEXT_PUBLIC_BASE_URL` to your dashboard/public URL (for OAuth callbacks) **Cloud sync errors** diff --git a/bin/omniroute.mjs b/bin/omniroute.mjs index a655e248d1..e9f1bc94cf 100755 --- a/bin/omniroute.mjs +++ b/bin/omniroute.mjs @@ -30,18 +30,18 @@ if (args.includes("--help") || args.includes("-h")) { \x1b[1mUsage:\x1b[0m omniroute Start the server - omniroute --port Use custom port (default: 20128) + omniroute --port Use custom API port (default: 20128) omniroute --no-open Don't open browser automatically omniroute --help Show this help omniroute --version Show version \x1b[1mAfter starting:\x1b[0m - Dashboard: http://localhost: - API: http://localhost:/v1 + Dashboard: http://localhost: + API: http://localhost:/v1 \x1b[1mConnect your tools:\x1b[0m Set your CLI tool (Cursor, Cline, Codex, etc.) to use: - \x1b[33mhttp://localhost:20128/v1\x1b[0m + \x1b[33mhttp://localhost:/v1\x1b[0m `); process.exit(0); } @@ -58,17 +58,26 @@ if (args.includes("--version") || args.includes("-v")) { process.exit(0); } -// Parse --port -let port = 20128; +function parsePort(value, fallback) { + const parsed = parseInt(String(value), 10); + return Number.isFinite(parsed) && parsed > 0 && parsed <= 65535 ? parsed : fallback; +} + +// Parse --port (canonical/base port) +let port = parsePort(process.env.PORT || "20128", 20128); const portIdx = args.indexOf("--port"); if (portIdx !== -1 && args[portIdx + 1]) { - port = parseInt(args[portIdx + 1], 10); - if (isNaN(port)) { + const parsed = parseInt(args[portIdx + 1], 10); + if (isNaN(parsed)) { console.error("\x1b[31mβœ– Invalid port number\x1b[0m"); process.exit(1); } + port = parsed; } +const apiPort = parsePort(process.env.API_PORT || String(port), port); +const dashboardPort = parsePort(process.env.DASHBOARD_PORT || String(port), port); + const noOpen = args.includes("--no-open"); // ── Banner ───────────────────────────────────────────────── @@ -85,13 +94,8 @@ console.log(` const serverJs = join(APP_DIR, "server.js"); if (!existsSync(serverJs)) { - console.error( - "\x1b[31mβœ– Server not found at:\x1b[0m", - serverJs, - ); - console.error( - " This usually means the package was not built correctly.", - ); + console.error("\x1b[31mβœ– Server not found at:\x1b[0m", serverJs); + console.error(" This usually means the package was not built correctly."); console.error(" Try reinstalling: npm install -g omniroute"); process.exit(1); } @@ -101,7 +105,10 @@ console.log(` \x1b[2m⏳ Starting server...\x1b[0m\n`); const env = { ...process.env, - PORT: String(port), + OMNIROUTE_PORT: String(port), + PORT: String(dashboardPort), + DASHBOARD_PORT: String(dashboardPort), + API_PORT: String(apiPort), HOSTNAME: "0.0.0.0", NODE_ENV: "production", }; @@ -119,7 +126,10 @@ server.stdout.on("data", (data) => { process.stdout.write(text); // Detect server ready - if (!started && (text.includes("Ready") || text.includes("started") || text.includes("listening"))) { + if ( + !started && + (text.includes("Ready") || text.includes("started") || text.includes("listening")) + ) { started = true; onReady(); } @@ -156,16 +166,17 @@ process.on("SIGTERM", shutdown); // ── On ready ─────────────────────────────────────────────── async function onReady() { - const url = `http://localhost:${port}`; + const dashboardUrl = `http://localhost:${dashboardPort}`; + const apiUrl = `http://localhost:${apiPort}`; console.log(` \x1b[32mβœ” OmniRoute is running!\x1b[0m - \x1b[1m Dashboard:\x1b[0m ${url} - \x1b[1m API Base:\x1b[0m ${url}/v1 + \x1b[1m Dashboard:\x1b[0m ${dashboardUrl} + \x1b[1m API Base:\x1b[0m ${apiUrl}/v1 \x1b[2m Point your CLI tool (Cursor, Cline, Codex) to:\x1b[0m - \x1b[33m ${url}/v1\x1b[0m + \x1b[33m ${apiUrl}/v1\x1b[0m \x1b[2m Press Ctrl+C to stop\x1b[0m `); @@ -173,7 +184,7 @@ async function onReady() { if (!noOpen) { try { const open = await import("open"); - await open.default(url); + await open.default(dashboardUrl); } catch { // open is optional β€” if not available, just skip } diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 486afaafeb..f129888275 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -35,7 +35,7 @@ services: "CMD", "node", "-e", - "fetch('http://127.0.0.1:20128/api/settings').then(r=>{if(!r.ok)throw r.status}).catch(()=>process.exit(1))", + "const p=process.env.DASHBOARD_PORT||process.env.PORT||'20128';fetch('http://127.0.0.1:'+p+'/api/settings').then(r=>{if(!r.ok)throw r.status}).catch(()=>process.exit(1))", ] interval: 30s timeout: 5s diff --git a/docker-compose.yml b/docker-compose.yml index 0b936ffb08..40441ace65 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,7 +28,7 @@ x-common: &common "CMD", "node", "-e", - "fetch('http://127.0.0.1:20128/api/settings').then(r=>{if(!r.ok)throw r.status}).catch(()=>process.exit(1))", + "const p=process.env.DASHBOARD_PORT||process.env.PORT||'20128';fetch('http://127.0.0.1:'+p+'/api/settings').then(r=>{if(!r.ok)throw r.status}).catch(()=>process.exit(1))", ] interval: 30s timeout: 5s diff --git a/package.json b/package.json index 1116ed6770..b57b582870 100644 --- a/package.json +++ b/package.json @@ -42,10 +42,10 @@ }, "homepage": "https://omniroute.online", "scripts": { - "dev": "next dev --webpack --port 20128", + "dev": "OMNIROUTE_PORT=${PORT:-20128} next dev --webpack --port ${DASHBOARD_PORT:-${PORT:-20128}}", "build": "next build --webpack", "build:cli": "node scripts/prepublish.mjs", - "start": "next start --port 20128", + "start": "OMNIROUTE_PORT=${PORT:-20128} next start --port ${DASHBOARD_PORT:-${PORT:-20128}}", "lint": "eslint .", "test": "node --test tests/unit/*.test.mjs", "test:unit": "node --import tsx/esm --test tests/unit/*.test.mjs", diff --git a/playwright.config.ts b/playwright.config.ts index 491b3eb6f1..15e3706b82 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,5 +1,8 @@ import { defineConfig, devices } from "@playwright/test"; +const dashboardPort = process.env.DASHBOARD_PORT || process.env.PORT || "20128"; +const dashboardBaseUrl = `http://localhost:${dashboardPort}`; + export default defineConfig({ testDir: "./tests/e2e", fullyParallel: true, @@ -8,7 +11,7 @@ export default defineConfig({ workers: process.env.CI ? 1 : undefined, reporter: process.env.CI ? "github" : "html", use: { - baseURL: "http://localhost:20128", + baseURL: dashboardBaseUrl, trace: "on-first-retry", screenshot: "only-on-failure", }, @@ -20,7 +23,7 @@ export default defineConfig({ ], webServer: { command: process.env.CI ? "npm start" : "npm run dev", - url: "http://localhost:20128", + url: dashboardBaseUrl, reuseExistingServer: !process.env.CI, timeout: 120_000, }, diff --git a/scripts/run-standalone.mjs b/scripts/run-standalone.mjs new file mode 100644 index 0000000000..b48ccf3eb2 --- /dev/null +++ b/scripts/run-standalone.mjs @@ -0,0 +1,34 @@ +#!/usr/bin/env node + +import { spawn } from "node:child_process"; + +function parsePort(value, fallback) { + const parsed = Number.parseInt(String(value), 10); + return Number.isFinite(parsed) && parsed > 0 && parsed <= 65535 ? parsed : fallback; +} + +const basePort = parsePort(process.env.PORT || "20128", 20128); +const apiPort = parsePort(process.env.API_PORT || String(basePort), basePort); +const dashboardPort = parsePort(process.env.DASHBOARD_PORT || String(basePort), basePort); + +const child = spawn("node", ["server.js"], { + stdio: "inherit", + env: { + ...process.env, + OMNIROUTE_PORT: String(basePort), + PORT: String(dashboardPort), + DASHBOARD_PORT: String(dashboardPort), + API_PORT: String(apiPort), + }, +}); + +child.on("exit", (code, signal) => { + if (signal) { + process.kill(process.pid, signal); + return; + } + process.exit(code ?? 0); +}); + +process.on("SIGINT", () => child.kill("SIGINT")); +process.on("SIGTERM", () => child.kill("SIGTERM")); diff --git a/src/app/(dashboard)/dashboard/cli-tools/CLIToolsPageClient.tsx b/src/app/(dashboard)/dashboard/cli-tools/CLIToolsPageClient.tsx index 89ba789269..8daa857bec 100644 --- a/src/app/(dashboard)/dashboard/cli-tools/CLIToolsPageClient.tsx +++ b/src/app/(dashboard)/dashboard/cli-tools/CLIToolsPageClient.tsx @@ -30,6 +30,7 @@ export default function CLIToolsPageClient({ machineId }) { const [apiKeys, setApiKeys] = useState([]); const [toolStatuses, setToolStatuses] = useState({}); const [statusesLoaded, setStatusesLoaded] = useState(false); + const [apiBaseUrl, setApiBaseUrl] = useState(""); useEffect(() => { fetchConnections(); @@ -44,6 +45,12 @@ export default function CLIToolsPageClient({ machineId }) { if (res.ok) { const data = await res.json(); setCloudEnabled(data.cloudEnabled || false); + if (typeof window !== "undefined") { + const protocol = window.location.protocol; + const hostname = window.location.hostname; + const apiPort = data?.apiPort || 20128; + setApiBaseUrl(`${protocol}//${hostname}:${apiPort}`); + } } } catch (error) { console.log("Error loading cloud settings:", error); @@ -141,6 +148,9 @@ export default function CLIToolsPageClient({ machineId }) { if (cloudEnabled && CLOUD_URL) { return CLOUD_URL; } + if (apiBaseUrl) { + return apiBaseUrl; + } if (typeof window !== "undefined") { return window.location.origin; } diff --git a/src/app/(dashboard)/dashboard/onboarding/page.tsx b/src/app/(dashboard)/dashboard/onboarding/page.tsx index a782f63964..7d18120183 100644 --- a/src/app/(dashboard)/dashboard/onboarding/page.tsx +++ b/src/app/(dashboard)/dashboard/onboarding/page.tsx @@ -24,6 +24,7 @@ export default function OnboardingWizard() { const router = useRouter(); const [step, setStep] = useState(0); const [loading, setLoading] = useState(true); + const [apiEndpoint, setApiEndpoint] = useState("http://localhost:20128/api/v1"); // Security step state const [password, setPassword] = useState(""); @@ -42,11 +43,20 @@ export default function OnboardingWizard() { // Check if setup is already complete useEffect(() => { + const resolveApiEndpoint = (apiPort) => { + if (typeof window === "undefined") return; + const protocol = window.location.protocol; + const hostname = window.location.hostname; + const effectiveApiPort = apiPort || 20128; + setApiEndpoint(`${protocol}//${hostname}:${effectiveApiPort}/api/v1`); + }; + const checkSetup = async () => { try { const res = await fetch("/api/settings"); if (res.ok) { const settings = await res.json(); + resolveApiEndpoint(settings?.apiPort); if (settings.setupComplete) { router.replace("/dashboard"); return; @@ -396,7 +406,7 @@ export default function OnboardingWizard() {

Your endpoint:

- http://localhost:20128/api/v1 + {apiEndpoint}
)} diff --git a/src/app/api/cli-tools/guide-settings/[toolId]/route.ts b/src/app/api/cli-tools/guide-settings/[toolId]/route.ts index 5984681b78..38fca04b98 100644 --- a/src/app/api/cli-tools/guide-settings/[toolId]/route.ts +++ b/src/app/api/cli-tools/guide-settings/[toolId]/route.ts @@ -68,9 +68,7 @@ async function saveContinueConfig({ baseUrl, apiKey, model }) { const existingIdx = models.findIndex( (m) => m.apiBase && - (m.apiBase.includes("localhost:20128") || - m.apiBase.includes("omniroute") || - m.title === model) + (m.apiBase.includes("localhost") || m.apiBase.includes("omniroute") || m.title === model) ); if (existingIdx >= 0) { diff --git a/src/app/api/cli-tools/status/route.ts b/src/app/api/cli-tools/status/route.ts index 610ba837f5..1465d7f66d 100644 --- a/src/app/api/cli-tools/status/route.ts +++ b/src/app/api/cli-tools/status/route.ts @@ -33,7 +33,7 @@ async function checkToolConfigStatus(toolId: string): Promise { case "kilo": // Generic check: look for any OmniRoute-related URL in the config const configStr = JSON.stringify(config).toLowerCase(); - return configStr.includes("omniroute") || configStr.includes("20128") + return configStr.includes("omniroute") || configStr.includes("localhost") ? "configured" : "not_configured"; default: diff --git a/src/app/api/settings/route.ts b/src/app/api/settings/route.ts index 9e427f1639..d5dbe3da2d 100644 --- a/src/app/api/settings/route.ts +++ b/src/app/api/settings/route.ts @@ -2,6 +2,7 @@ import { NextResponse } from "next/server"; import { getSettings, updateSettings } from "@/lib/localDb"; import bcrypt from "bcryptjs"; import { updateSettingsSchema, validateBody } from "@/shared/validation/schemas"; +import { getRuntimePorts } from "@/lib/runtime/ports"; export async function GET() { try { @@ -9,11 +10,15 @@ export async function GET() { const { password, ...safeSettings } = settings; const enableRequestLogs = process.env.ENABLE_REQUEST_LOGS === "true"; + const runtimePorts = getRuntimePorts(); return NextResponse.json({ ...safeSettings, enableRequestLogs, hasPassword: !!password || !!process.env.INITIAL_PASSWORD, + runtimePorts, + apiPort: runtimePorts.apiPort, + dashboardPort: runtimePorts.dashboardPort, }); } catch (error) { console.log("Error getting settings:", error); diff --git a/src/instrumentation.ts b/src/instrumentation.ts index d537bd4680..4ebbaef73c 100644 --- a/src/instrumentation.ts +++ b/src/instrumentation.ts @@ -29,6 +29,9 @@ export async function register() { const { initGracefulShutdown } = await import("@/lib/gracefulShutdown"); initGracefulShutdown(); + const { initApiBridgeServer } = await import("@/lib/apiBridgeServer"); + initApiBridgeServer(); + // Compliance: Initialize audit_log table + cleanup expired logs try { const { initAuditLog, cleanupExpiredLogs } = await import("@/lib/compliance/index"); diff --git a/src/lib/apiBridgeServer.ts b/src/lib/apiBridgeServer.ts new file mode 100644 index 0000000000..1b57568d1d --- /dev/null +++ b/src/lib/apiBridgeServer.ts @@ -0,0 +1,91 @@ +import http from "node:http"; +import type { IncomingMessage, ServerResponse } from "node:http"; +import { getRuntimePorts } from "@/lib/runtime/ports"; + +const OPENAI_COMPAT_PATHS = [ + /^\/v1(?:\/|$)/, + /^\/chat\/completions(?:\?|$)/, + /^\/responses(?:\?|$)/, + /^\/models(?:\?|$)/, + /^\/codex(?:\/|\?|$)/, +]; + +function isOpenAiCompatiblePath(pathname: string): boolean { + return OPENAI_COMPAT_PATHS.some((pattern) => pattern.test(pathname)); +} + +function proxyRequest(req: IncomingMessage, res: ServerResponse, dashboardPort: number): void { + const targetReq = http.request( + { + hostname: "127.0.0.1", + port: dashboardPort, + method: req.method, + path: req.url, + headers: { + ...req.headers, + host: `127.0.0.1:${dashboardPort}`, + }, + }, + (targetRes) => { + res.writeHead(targetRes.statusCode || 502, targetRes.headers); + targetRes.pipe(res); + } + ); + + targetReq.on("error", (error) => { + if (res.headersSent) return; + res.writeHead(502, { "content-type": "application/json" }); + res.end( + JSON.stringify({ error: "api_bridge_unavailable", detail: String(error.message || error) }) + ); + }); + + req.pipe(targetReq); +} + +declare global { + // eslint-disable-next-line no-var + var __omnirouteApiBridgeStarted: boolean | undefined; +} + +export function initApiBridgeServer(): void { + if (globalThis.__omnirouteApiBridgeStarted) return; + + const { apiPort, dashboardPort } = getRuntimePorts(); + if (apiPort === dashboardPort) return; + + const host = process.env.HOSTNAME || "0.0.0.0"; + + const server = http.createServer((req, res) => { + const rawUrl = req.url || "/"; + const pathname = rawUrl.split("?")[0] || "/"; + + if (!isOpenAiCompatiblePath(pathname)) { + res.writeHead(404, { "content-type": "application/json" }); + res.end( + JSON.stringify({ + error: "not_found", + message: "API port only serves OpenAI-compatible routes.", + }) + ); + return; + } + + proxyRequest(req, res, dashboardPort); + }); + + server.on("error", (error: NodeJS.ErrnoException) => { + if (error?.code === "EADDRINUSE") { + console.warn( + `[API Bridge] Port ${apiPort} is already in use. API bridge disabled. (dashboard: ${dashboardPort})` + ); + return; + } + console.warn("[API Bridge] Failed to start:", error?.message || error); + }); + + server.listen(apiPort, host, () => { + globalThis.__omnirouteApiBridgeStarted = true; + console.log(`[API Bridge] Listening on ${host}:${apiPort} -> dashboard:${dashboardPort}`); + }); +} diff --git a/src/lib/oauth/config/index.ts b/src/lib/oauth/config/index.ts index 03291185ab..d6ae85cc4b 100644 --- a/src/lib/oauth/config/index.ts +++ b/src/lib/oauth/config/index.ts @@ -11,13 +11,21 @@ interface ServerCredentials { userId: string; } +function getDefaultApiServer() { + const basePort = Number.parseInt(process.env.OMNIROUTE_PORT || process.env.PORT || "20128", 10); + const fallbackPort = Number.isFinite(basePort) ? basePort : 20128; + const apiPort = Number.parseInt(process.env.API_PORT || String(fallbackPort), 10); + const effectivePort = Number.isFinite(apiPort) ? apiPort : fallbackPort; + return `http://localhost:${effectivePort}`; +} + /** * Get server credentials from environment variables. * Used by OAuth CLI services to save tokens to the running server. */ export function getServerCredentials(): ServerCredentials { return { - server: process.env.OMNIROUTE_SERVER || process.env.SERVER_URL || "http://localhost:20128", + server: process.env.OMNIROUTE_SERVER || process.env.SERVER_URL || getDefaultApiServer(), token: process.env.OMNIROUTE_TOKEN || process.env.CLI_TOKEN || "", userId: process.env.OMNIROUTE_USER_ID || process.env.CLI_USER_ID || "cli", }; diff --git a/src/lib/runtime/ports.ts b/src/lib/runtime/ports.ts new file mode 100644 index 0000000000..af880aa8dc --- /dev/null +++ b/src/lib/runtime/ports.ts @@ -0,0 +1,32 @@ +const DEFAULT_PORT = 20128; + +function parsePort(value: string | undefined, fallback: number): number { + if (!value) return fallback; + const parsed = Number.parseInt(String(value), 10); + if (!Number.isFinite(parsed) || parsed < 1 || parsed > 65535) return fallback; + return parsed; +} + +export type RuntimePorts = { + port: number; + apiPort: number; + dashboardPort: number; + apiPortExplicit: boolean; + dashboardPortExplicit: boolean; +}; + +export function getRuntimePorts(): RuntimePorts { + // OMNIROUTE_PORT preserves the user's canonical PORT in wrapped runtimes + // where Next.js requires process.env.PORT to be the dashboard listener port. + const basePort = parsePort(process.env.OMNIROUTE_PORT || process.env.PORT, DEFAULT_PORT); + const apiPortExplicit = !!process.env.API_PORT; + const dashboardPortExplicit = !!process.env.DASHBOARD_PORT; + + return { + port: basePort, + apiPort: parsePort(process.env.API_PORT, basePort), + dashboardPort: parsePort(process.env.DASHBOARD_PORT, basePort), + apiPortExplicit, + dashboardPortExplicit, + }; +} diff --git a/src/shared/services/cloudSyncScheduler.ts b/src/shared/services/cloudSyncScheduler.ts index 30e3a21eae..84becf0ae1 100644 --- a/src/shared/services/cloudSyncScheduler.ts +++ b/src/shared/services/cloudSyncScheduler.ts @@ -1,11 +1,14 @@ import { getConsistentMachineId } from "@/shared/utils/machineId"; import { isCloudEnabled } from "@/lib/localDb"; +import { getRuntimePorts } from "@/lib/runtime/ports"; + +const { dashboardPort } = getRuntimePorts(); const INTERNAL_BASE_URL = process.env.BASE_URL || process.env.NEXT_PUBLIC_BASE_URL || process.env.NEXT_PUBLIC_APP_URL || - "http://localhost:20128"; + `http://localhost:${dashboardPort}`; /** * Cloud sync scheduler diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 53de65b5b7..7a89d27969 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -13,6 +13,9 @@ declare namespace NodeJS { PROMPT_CACHE_MAX_SIZE?: string; PROMPT_CACHE_TTL_MS?: string; NEXT_PUBLIC_CLOUD_URL?: string; + API_PORT?: string; + DASHBOARD_PORT?: string; + OMNIROUTE_PORT?: string; NODE_ENV?: "development" | "production" | "test"; } }