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
+85 -4
View File
@@ -25,6 +25,7 @@ import {
import { resolveAppVersion } from "@/utils/app-version";
import { ConnectionOfferSchema, type ConnectionOffer } from "@getpaseo/protocol/connection-offer";
import { shouldUseDesktopDaemon } from "@/desktop/daemon/desktop-daemon";
import { isWeb } from "@/constants/platform";
import { connectToDaemon } from "@/utils/test-daemon-connection";
import { getOrCreateClientId } from "@/utils/client-id";
import {
@@ -136,6 +137,12 @@ export interface HostRuntimeControllerDeps {
hostname: string | null;
}>;
getClientId: () => Promise<string>;
readInitialConnectionHint?: () => InitialDaemonConnectionHint | null;
}
export interface HostRuntimeStorage {
getItem: (key: string) => Promise<string | null>;
setItem: (key: string, value: string) => Promise<void>;
}
export interface HostRuntimeStartOptions {
@@ -1273,6 +1280,37 @@ const REGISTRY_STORAGE_KEY = "@paseo:daemon-registry";
const LOCALHOST_FALLBACK_ENDPOINT = "localhost:6767";
const DEFAULT_LOCALHOST_BOOTSTRAP_TIMEOUT_MS = 2500;
const E2E_STORAGE_KEY = "@paseo:e2e";
const INITIAL_DAEMON_CONNECTION_HINT_GLOBAL_KEY = "__PASEO_INITIAL_DAEMON_CONNECTION__";
export interface InitialDaemonConnectionHint {
listen: string;
useTls?: boolean;
}
function isInitialConnectionHintRecord(value: unknown): value is Record<string, unknown> {
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
export function readInitialDaemonConnectionHint(input?: {
isWebRuntime?: boolean;
}): InitialDaemonConnectionHint | null {
const isWebRuntime = input?.isWebRuntime ?? isWeb;
if (!isWebRuntime || typeof globalThis === "undefined") {
return null;
}
const value = (globalThis as Record<string, unknown>)[INITIAL_DAEMON_CONNECTION_HINT_GLOBAL_KEY];
if (!isInitialConnectionHintRecord(value)) {
return null;
}
const listen = typeof value.listen === "string" ? value.listen.trim() : "";
if (!listen) {
return null;
}
return {
listen,
useTls: value.useTls === true,
};
}
function readConfiguredLocalDaemonOverride(): string | null {
const value = process.env.EXPO_PUBLIC_LOCAL_DAEMON?.trim();
@@ -1314,9 +1352,11 @@ export class HostRuntimeStore {
private agentDirectoryBootstrapInFlight = new Map<string, Promise<void>>();
private configuredOverrideBootstrapInFlight: Promise<void> | null = null;
private bootStarted = false;
private storage: HostRuntimeStorage;
constructor(input?: { deps?: HostRuntimeControllerDeps }) {
constructor(input?: { deps?: HostRuntimeControllerDeps; storage?: HostRuntimeStorage }) {
this.deps = input?.deps ?? createDefaultDeps();
this.storage = input?.storage ?? AsyncStorage;
}
// --- Host registry ---
@@ -1354,7 +1394,7 @@ export class HostRuntimeStore {
let isE2E: string | null = null;
try {
isE2E = await AsyncStorage.getItem(E2E_STORAGE_KEY);
isE2E = await this.storage.getItem(E2E_STORAGE_KEY);
} catch {
return;
}
@@ -1366,6 +1406,16 @@ export class HostRuntimeStore {
return;
}
const initialHint = this.deps.readInitialConnectionHint
? this.deps.readInitialConnectionHint()
: readInitialDaemonConnectionHint();
if (initialHint) {
const bootstrapped = await this.bootstrapInitialConnectionHint(initialHint);
if (bootstrapped) {
return;
}
}
if (override) {
this.bootstrapConfiguredOverride(override);
} else {
@@ -1376,7 +1426,7 @@ export class HostRuntimeStore {
private async loadFromStorage(): Promise<void> {
let shouldPersistHosts = false;
try {
const stored = await AsyncStorage.getItem(REGISTRY_STORAGE_KEY);
const stored = await this.storage.getItem(REGISTRY_STORAGE_KEY);
if (!stored) {
return;
}
@@ -1469,6 +1519,37 @@ export class HostRuntimeStore {
}
}
private async bootstrapInitialConnectionHint(
hint: InitialDaemonConnectionHint,
): Promise<boolean> {
const connection = connectionFromListen(hint.listen);
if (!connection) {
return false;
}
const connectionWithHint: HostConnection =
connection.type === "directTcp"
? { ...connection, useTls: hint.useTls ?? connection.useTls ?? false }
: connection;
if (registryHasConnection(this.hosts, connectionWithHint)) {
return true;
}
try {
await this.probeAndUpsertConnection({
connection: connectionWithHint,
timeoutMs: DEFAULT_LOCALHOST_BOOTSTRAP_TIMEOUT_MS,
});
return true;
} catch (error) {
console.warn("[HostRuntime] initial connection hint probe failed", {
listen: hint.listen,
useTls: hint.useTls,
error,
});
return false;
}
}
reconcileServerId(oldServerId: string, newServerId: string): void {
if (oldServerId === newServerId) {
return;
@@ -1746,7 +1827,7 @@ export class HostRuntimeStore {
private async persistHosts(): Promise<void> {
try {
await AsyncStorage.setItem(REGISTRY_STORAGE_KEY, JSON.stringify(this.hosts));
await this.storage.setItem(REGISTRY_STORAGE_KEY, JSON.stringify(this.hosts));
} catch (error) {
console.error("[HostRuntime] Failed to persist host registry", error);
}