Serve the web client from the daemon (#1635)

* Serve the web client from the daemon

Keep the bundled browser UI opt-in and exclude it from desktop packaging so desktop builds do not ship a duplicate renderer.

* Escape daemon web UI bootstrap hint

* Fix bundled web UI dist path
This commit is contained in:
Mohamed Boudra
2026-06-26 12:00:11 +08:00
committed by GitHub
parent 3d6b4adc68
commit 4e2c06ec71
24 changed files with 1302 additions and 6 deletions
+43
View File
@@ -1,4 +1,5 @@
import path from "node:path";
import { fileURLToPath } from "node:url";
import { resolvePaseoNodeEnv } from "./paseo-env.js";
import { z } from "zod";
import { expandTilde } from "../utils/path.js";
@@ -25,6 +26,18 @@ const DEFAULT_PORT = 6767;
const DEFAULT_RELAY_ENDPOINT = "relay.paseo.sh:443";
const DEFAULT_APP_BASE_URL = "https://app.paseo.sh";
export function resolveBundledWebUiDistDir(moduleUrl: string | URL = import.meta.url): string {
const moduleDir = path.dirname(fileURLToPath(moduleUrl));
if (path.basename(moduleDir) === "server" && path.basename(path.dirname(moduleDir)) === "src") {
return path.resolve(moduleDir, "..", "..", "dist", "server", "web-ui");
}
return path.resolve(moduleDir, "web-ui");
}
const BUNDLED_WEB_UI_DIST_DIR = resolveBundledWebUiDistDir();
function parseBooleanEnv(value: string | undefined): boolean | undefined {
if (value === undefined) {
return undefined;
@@ -55,6 +68,7 @@ export type CliConfigOverrides = Partial<{
relayUseTls: boolean;
mcpEnabled: boolean;
mcpInjectIntoAgents: boolean;
webUiEnabled: boolean;
hostnames: HostnamesConfig;
}>;
@@ -238,6 +252,33 @@ function resolveServiceProxyConfig(
return { publicBaseUrl, standaloneListen };
}
interface ResolvedWebUi {
enabled: boolean;
distDir: string | null;
}
function resolveWebUiConfig(
paseoHome: string,
env: NodeJS.ProcessEnv,
cli: CliConfigOverrides | undefined,
persisted: ReturnType<typeof loadPersistedConfig>,
): ResolvedWebUi {
const enabled =
cli?.webUiEnabled ??
parseBooleanEnv(env.PASEO_WEB_UI_ENABLED) ??
persisted.features?.webUi?.enabled ??
false;
const rawDistDir = env.PASEO_WEB_UI_DIST_DIR ?? persisted.features?.webUi?.distDir;
const trimmedDistDir = rawDistDir?.trim();
const distDir = trimmedDistDir
? path.resolve(path.isAbsolute(trimmedDistDir) ? trimmedDistDir : paseoHome, trimmedDistDir)
: BUNDLED_WEB_UI_DIST_DIR;
return {
enabled,
distDir,
};
}
function resolveVoiceLlmConfig(
env: NodeJS.ProcessEnv,
persisted: ReturnType<typeof loadPersistedConfig>,
@@ -365,6 +406,7 @@ export function loadConfig(
cliRelayUseTls: options?.cli?.relayUseTls,
});
const serviceProxy = resolveServiceProxyConfig(env, persisted);
const webUi = resolveWebUiConfig(paseoHome, env, options?.cli, persisted);
const { openai, speech } = resolveSpeechConfig({
paseoHome,
@@ -400,6 +442,7 @@ export function loadConfig(
relayUseTls: relay.useTls,
relayPublicUseTls: relay.publicUseTls,
serviceProxy,
webUi,
appBaseUrl,
auth: resolveAuthConfig(env, persisted),
openai,