feat(webui): add initial webui with websocket chat flow

This commit is contained in:
Xubin Ren
2026-04-18 18:51:53 +00:00
parent 6bfb75ed03
commit 9ed3031a42
76 changed files with 7088 additions and 38 deletions
+40
View File
@@ -0,0 +1,40 @@
import type { BootstrapResponse } from "./types";
/**
* Fetch a short-lived token + the WebSocket path from the gateway's
* ``/webui/bootstrap`` endpoint. Localhost-only on the server side.
*/
export async function fetchBootstrap(
baseUrl: string = "",
): Promise<BootstrapResponse> {
const res = await fetch(`${baseUrl}/webui/bootstrap`, {
method: "GET",
credentials: "same-origin",
});
if (!res.ok) {
throw new Error(`bootstrap failed: HTTP ${res.status}`);
}
const body = (await res.json()) as BootstrapResponse;
if (!body.token || !body.ws_path) {
throw new Error("bootstrap response missing token or ws_path");
}
return body;
}
/** Derive a WebSocket URL from the current window location and the server-provided path.
*
* Keeps the path segment exactly as the server registered it: the root ``/``
* stays ``/`` and non-root paths are not given an extra trailing slash. This
* matters because some WS servers dispatch handshakes based on the literal
* path, not a normalised form.
*/
export function deriveWsUrl(wsPath: string, token: string): string {
const path = wsPath && wsPath.startsWith("/") ? wsPath : `/${wsPath || ""}`;
const query = `?token=${encodeURIComponent(token)}`;
if (typeof window === "undefined") {
return `ws://127.0.0.1:8765${path}${query}`;
}
const scheme = window.location.protocol === "https:" ? "wss" : "ws";
const host = window.location.host;
return `${scheme}://${host}${path}${query}`;
}