Commit 77df993
committed
feat: make opencode web embeddable in iframes at a subpath
Enables a reverse-proxy / embedding host (e.g. a parent dashboard) to
serve opencode web under an arbitrary URL prefix — the built SPA, the
server's CSP, and the SDK's default-server-URL resolution all have to
cooperate for a same-origin iframe mount to actually work. Three
orthogonal changes:
1. packages/app/vite.config.ts — `base: './'`
Emits relative asset paths in the built index.html and chunk
imports (e.g. `./assets/foo.js` instead of `/assets/foo.js`), so a
document loaded under `/some/deep/iframe-prefix/` can resolve its
own asset URLs against that prefix rather than against the origin
root. No effect on direct-serve at `/`; every Vite-base subpath
story just works from one source build.
2. packages/opencode/src/server/routes/ui.ts — add `'unsafe-eval'`
to the embedded-UI CSP's script-src directive
Something in opencode's production bundle (best guess: a workerized
runtime or a lib that compiles at runtime; we haven't bisected the
exact call site) exercises `eval()` / `new Function()`. The
existing CSP permitted `'wasm-unsafe-eval'` but not `'unsafe-eval'`,
causing the browser to block the bundle under the stricter Firefox
policy when served behind the embed proxy. Allowing `'unsafe-eval'`
keeps the page functional. A better long-term fix is to bisect the
eval caller and remove it, then tighten CSP back; this commit is
the short-term unblocker.
3. packages/app/src/entry.tsx — `getCurrentUrl` honors the
localStorage defaultServerUrl override
`getCurrentUrl()` was previously hard-wired to `location.origin`
(in production) for the initial `servers[0]` entry, while
`getDefaultUrl()` would return the localStorage-set
`defaultServerUrl` when present for the `defaultServer` key. The
two disagreed: the server-context's `current` server resolves via
`allServers().find(key === state.active) ?? allServers()[0]`, so
if `state.active` pointed at the localStorage URL but that URL
wasn't in `allServers()`, the code fell back to `allServers()[0]`
— i.e. `location.origin` — and control-plane requests like
`/global/config` and `/global/event` bypassed the override
entirely. Having `getCurrentUrl` also honor the localStorage
override keeps both entries aligned and makes the override
globally effective.
Together these let opencode web embed inside an iframe served from a
foreign origin / subpath: assets load, the SPA bundle executes, and
all SDK calls (including control-plane routes) honor the configured
server URL.1 parent f8ff6f4 commit 77df993
3 files changed
Lines changed: 14 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
98 | 98 | | |
99 | 99 | | |
100 | 100 | | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
101 | 109 | | |
102 | 110 | | |
103 | 111 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
6 | 10 | | |
7 | 11 | | |
8 | 12 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
14 | | - | |
| 14 | + | |
15 | 15 | | |
16 | 16 | | |
17 | | - | |
| 17 | + | |
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| |||
0 commit comments