diff --git a/packages/app/src/entry.tsx b/packages/app/src/entry.tsx index b5cbed6e75d..6fb362503b7 100644 --- a/packages/app/src/entry.tsx +++ b/packages/app/src/entry.tsx @@ -98,6 +98,14 @@ if (!(root instanceof HTMLElement) && import.meta.env.DEV) { } const getCurrentUrl = () => { + // Honor the localStorage `defaultServerUrl` override if set so that the + // initial `servers[0]` entry matches the `defaultServer` key; otherwise + // `allServers().find(...)` in the server context falls back to + // `allServers()[0]` and the SDK ends up calling `location.origin` for + // control-plane ("/global/*") routes even when the user configured a + // different server URL via localStorage. + const lsDefault = readDefaultServerUrl() + if (lsDefault) return lsDefault if (location.hostname.includes("opencode.ai")) return "http://localhost:4096" if (import.meta.env.DEV) return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "localhost"}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}` diff --git a/packages/app/vite.config.ts b/packages/app/vite.config.ts index 6a29ae6345e..62292f7f989 100644 --- a/packages/app/vite.config.ts +++ b/packages/app/vite.config.ts @@ -3,6 +3,10 @@ import desktopPlugin from "./vite" export default defineConfig({ plugins: [desktopPlugin] as any, + // Relative base so the built SPA can be served under any URL subpath + // (iframe embedding via reverse proxy) without requiring absolute-path + // asset resolution at the host origin. Also works for direct-serve at `/`. + base: "./", server: { host: "0.0.0.0", allowedHosts: true, diff --git a/packages/opencode/src/server/routes/ui.ts b/packages/opencode/src/server/routes/ui.ts index d449cd1c424..90e065a8cad 100644 --- a/packages/opencode/src/server/routes/ui.ts +++ b/packages/opencode/src/server/routes/ui.ts @@ -11,10 +11,10 @@ const embeddedUIPromise = Flag.OPENCODE_DISABLE_EMBEDDED_WEB_UI import("opencode-web-ui.gen.ts").then((module) => module.default as Record).catch(() => null) const DEFAULT_CSP = - "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; media-src 'self' data:; connect-src 'self' data:" + "default-src 'self'; script-src 'self' 'wasm-unsafe-eval' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; media-src 'self' data:; connect-src 'self' data:" const csp = (hash = "") => - `default-src 'self'; script-src 'self' 'wasm-unsafe-eval'${hash ? ` 'sha256-${hash}'` : ""}; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; media-src 'self' data:; connect-src 'self' data:` + `default-src 'self'; script-src 'self' 'wasm-unsafe-eval' 'unsafe-eval'${hash ? ` 'sha256-${hash}'` : ""}; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; media-src 'self' data:; connect-src 'self' data:` export const UIRoutes = (): Hono => new Hono().all("/*", async (c) => {