@@ -8,23 +8,25 @@ quickly.
88
99## How to rebase
1010
11- The workflow ` sync-upstream.yml ` runs every Friday and rebases ` zexi/dev ` onto
12- upstream automatically. After a rebase, verify the [ behavioral invariants ] ( #behavioral-invariants ) below .
11+ The workflow ` sync-upstream.yml ` runs every Friday and opens a PR rebasing
12+ ` zexi/dev ` onto upstream automatically .
1313
14- Manual rebase if needed :
14+ Manual rebase onto a clean branch :
1515
1616``` sh
1717git fetch upstream
18- git rebase --onto upstream/dev $( git merge-base HEAD upstream/dev) zexi/dev
18+ git checkout -b zexi/dev-clean upstream/dev
19+ git merge --squash zexi/dev
20+ # resolve conflicts, then commit in logical groups (see commits below)
1921```
2022
2123---
2224
2325## Fork commits (relative to upstream/dev)
2426
25- ### 1 · Release workflows — ` f4bb5ae17 `
27+ ### 1 · Release workflows — ` dfa2df240 `
2628
27- ** Files:** ` .github/workflows/zexi-electron.yml ` , ` .github/workflows/sync-upstream.yml ` , ` packages/desktop/electron-builder.config.ts ` , ` packages/script/src/index.ts `
29+ ** Files:** ` .github/workflows/zexi-electron.yml ` , ` .github/workflows/sync-upstream.yml `
2830
2931Custom CI that builds and releases signed macOS (arm64 + notarization) and
3032Windows (x64 + Azure trusted signing) Electron packages on every push to
@@ -33,124 +35,105 @@ pruned to keep the 3 most recent.
3335
3436The sync workflow disables all upstream workflows except these two.
3537
36- ** Rebase risk:** Low — touches only ` .github/ ` and build config. Conflicts
37- typically arise if upstream renames ` electron-builder.config.ts ` .
38+ ** Rebase risk:** Low — touches only ` .github/ ` . Conflicts only if upstream
39+ renames its own workflow files .
3840
3941---
4042
41- ### 2 · Configure desktop local server access — ` 78b5d1182 `
43+ ### 2 · Configure desktop local server access and sync opened projects — ` bd776bfa3 `
4244
43- ** Files:** ` packages/app/src/components/dialog-select-server.tsx ` , ` packages/app/src/components/server/server-row.tsx ` , ` packages/app/src/components/status-popover*.tsx ` , ` packages/desktop/src/main/{index,ipc,server,sidecar}.ts ` , ` packages/desktop/src/preload/ ` , ` packages/opencode/src/config/server.ts ` , i18n files
45+ ** Files:** ` packages/app/src/components/dialog-select-server.tsx ` , ` packages/app/src/components/server/server-row.tsx ` , ` packages/app/src/components/status-popover*.tsx ` , ` packages/desktop/src/main/{index,ipc,server,sidecar,constants }.ts ` , ` packages/desktop/src/preload/ ` , ` packages/opencode/src/config/{ server,projects} .ts ` , ` packages/opencode/src/server/shared/opened-projects{,.sql}.ts ` , ` packages/opencode/src/server/routes/instance/httpapi/{groups,handlers}/global.ts ` , ` packages/opencode/migration/20260511170500_opened_projects_db/migration.sql ` , ` packages/sdk/js/src/v2/gen/ ` , i18n files
4446
45- ** What it does:** Adds UI for users to configure the local Electron-embedded
46- server's hostname, port, username, and password from within the app. Previously
47- these could only be set via environment variables. The config is persisted in
48- Electron's store under ` localServerConfig ` (see ` constants.ts ` ).
49-
50- Key additions:
51- - IPC channels: ` get-local-server-config ` , ` set-local-server-config `
52- - ` getLocalServerConfig() / setLocalServerConfig() ` in ` server.ts `
53- - Server dialog revamp in ` dialog-select-server.tsx ` — includes a local-server
54- config panel when running in desktop mode
55- - Status bar server icon (bottom-left) opens the server selection/config dialog
56-
57- ** Rebase risk:** High — touches many app-layer files. Conflicts are likely if
58- upstream refactors ` status-popover ` , ` dialog-select-server ` , or the desktop
59- main/preload plumbing.
60-
61- ---
62-
63- ### 3 · Sync opened projects through server events — ` 7aa32f066 `
64-
65- ** Files:** ` packages/app/src/context/opened-projects.tsx ` , ` packages/opencode/src/config/projects.ts ` , ` packages/opencode/src/server/routes/instance/httpapi/{groups,handlers}/global.ts ` , ` packages/sdk/js/src/v2/gen/ `
47+ ** What it does:**
6648
67- ** What it does:** Moves opened-project state from scattered per-component state
68- into a single ` OpenedProjectsContext ` (Solid.js). The context subscribes to the
69- ` project.opened.updated ` server-sent event so all windows/tabs stay in sync
70- without polling.
49+ ** a) Local server config UI**
50+ Adds UI for users to configure the local Electron-embedded server's hostname,
51+ port, username, and password from within the app. Config is persisted in
52+ Electron's store under ` localServerConfig ` . Key IPC channels:
53+ ` get-local-server-config ` , ` set-local-server-config ` .
7154
72- Adds server-side API routes under ` /global ` for listing, opening, closing, and
73- reordering opened projects. Also extends the JS SDK types accordingly.
55+ ** b) Opened projects sync**
56+ Moves opened-project state into a single ` OpenedProjectsContext ` (Solid.js)
57+ backed by a server-side SQLite table (` OpenedProjectTable ` ). All windows/tabs
58+ subscribe to the ` project.opened.updated ` SSE event and stay in sync without
59+ polling. Server-side API routes under ` /global ` handle list, open, close, and
60+ reorder.
7461
75- ** Rebase risk:** Medium — ` handlers/global.ts ` and the SDK generated files are
76- common conflict points if upstream adds routes in the same files.
62+ ** Rebase risk:** High — touches many app-layer files. Most likely conflict
63+ points: ` status-popover.tsx ` , ` dialog-select-server.tsx ` , ` handlers/global.ts ` ,
64+ and the SDK generated files.
7765
7866---
7967
80- ### 4 · Keep opened project metadata single-sourced — ` 02416fa48 `
68+ ### 3 · Embed and serve web UI from sidecar — ` 54660edfa `
8169
82- ** Files:** ` packages/opencode /src/server/shared/opened-projects .ts ` (new) , ` packages/opencode/src/server/shared/opened-projects.sql .ts ` (new) , ` packages/desktop/src/main/{server,ipc,constants} .ts ` , ` packages/app /src/context/{platform, server,opened-projects}.tsx ` , ` packages/app /src/pages/layout.tsx `
70+ ** Files:** ` packages/core /src/flag/flag .ts ` , ` packages/desktop/electron-builder.config .ts ` , ` packages/desktop/electron.vite.config.ts ` , ` packages/desktop/scripts/prebuild .ts` , ` packages/opencode /src/config/ server.ts ` , ` packages/opencode /src/server/shared/{public-ui,ui}.ts `
8371
84- ** What it does:** Persists opened-project metadata (name, icon, commands,
85- ordering) in the opencode SQLite database via a dedicated ` OpenedProjectTable ` ,
86- rather than duplicating it across Electron store and server memory. This was a
87- follow-up fix after the upstream rebase broke the original implementation.
72+ ** What it does:**
73+ Bundles the web SPA into the sidecar binary at build time via a generated
74+ module ( ` opencode-web-ui.gen.ts ` ). The sidecar serves it directly, with a
75+ priority chain:
8876
89- Also stores ` localServerConfig ` in Electron store (previously it lived only in
90- memory), fixing the config disappearing on restart.
77+ 1 . Embedded bundle (` opencode-web-ui.gen.ts ` ) — production
78+ 2 . Local directory (` OPENCODE_DEV_UI_DIR ` ) — dev mode, set automatically in
79+ Electron dev to ` packages/app/dist `
80+ 3 . Proxy to ` https://app.opencode.ai ` (override with ` OPENCODE_DEV_UI_URL ` ) —
81+ fallback
9182
92- ** Rebase risk: ** Medium-High — ` opened-projects.ts ` is a new file owned by this
93- fork; conflicts arise only if upstream creates a file at the same path. The
94- migration SQL file path must stay unique .
83+ Static assets (HTML shell, JS/CSS bundles, icons, favicons) under
84+ ` isPublicUIPath() ` are served without authentication so a remote browser can
85+ load the UI shell before entering credentials .
9586
96- ---
87+ ** Prerequisite for dev:** build ` packages/app ` first:
88+ ``` sh
89+ cd packages/app && bun run build
90+ ```
9791
98- ### 5 · Allow web UI shell without auth — ` b8c154e01 `
92+ ** Rebase risk:** Medium — ` ui.ts ` conflicts if upstream restructures
93+ ` serveUIEffect ` . ` flag.ts ` conflicts if upstream adds flags at the same
94+ location.
9995
100- ** Files: ** ` packages/opencode/src/server/shared/public-ui.ts ` , ` packages/opencode/test/server/httpapi-ui.test.ts `
96+ ---
10197
102- ** What it does:** Static assets (HTML shell, JS bundles, CSS, icons, favicons)
103- are served without requiring authentication, so a remote browser can load the
104- app UI shell before the user has entered credentials. API routes remain
105- protected.
98+ ### 4 · Serve SPA at any subpath without auth, display auth-required page on 401 — ` 7bac50fd8 `
10699
107- ` isPublicUIPath() ` returns ` true ` for:
108- - ` / ` , ` /index.html ` , ` /site.webmanifest ` , manifest PNGs
109- - ` /assets/* ` (JS/CSS bundles)
110- - ` /favicon* ` , ` /apple-touch-icon* ` , ` /social-share.* `
100+ ** Files:** ` packages/opencode/src/server/routes/instance/httpapi/middleware/authorization.ts ` , ` packages/opencode/src/server/routes/instance/httpapi/server.ts ` , ` packages/app/src/pages/error.tsx ` , ` packages/app/src/pages/session.tsx ` , ` packages/app/src/i18n/{en,zh,zht}.ts ` , ` packages/opencode/test/server/httpapi-ui.test.ts `
111101
112- ** Why this matters:** Without this, a remote browser hitting the server gets a
113- 401 on the very first request and cannot load anything — the user has no way to
114- enter credentials.
102+ ** What it does:**
115103
116- ** Rebase risk:** Low — ` public-ui.ts ` is a small file. Conflict only if upstream
117- adds its own public-path logic here.
104+ ** a) SPA catch-all serves without auth**
105+ The ` /* ` route loads unconditionally (no credentials check) so the browser can
106+ bootstrap the SPA shell at any subpath. API routes under ` /global/* ` and
107+ instance routes remain fully protected.
118108
119- ---
109+ ** b) Bearer auth scheme**
110+ ` WWW-Authenticate ` is ` Bearer realm="Secure Area" ` instead of ` Basic ` . This
111+ suppresses the browser's native credential popup on 401 while keeping the server
112+ fully functional — it still reads and validates ` Authorization: Basic ` headers
113+ sent by the desktop app.
120114
121- ### 6 · Serve local web UI in dev mode, suppress auth dialog — ` dd426faec `
115+ ** c) Auth-required error page**
116+ On 401 the SPA shows a localized "Authentication required" page with a manual
117+ "Go to home" button, breaking the infinite redirect loop that previously occurred
118+ when the app auto-navigated from ` / ` to the last opened project.
122119
123- ** Files:** ` packages/core/src/flag/flag.ts ` , ` packages/desktop/src/main/server.ts ` , ` packages/opencode/src/server/routes/instance/httpapi/middleware/authorization.ts ` , ` packages/opencode/src/server/shared/ui.ts `
120+ ** d) /global/health probe**
121+ Allows unauthenticated health probes so the SPA can discover the server before
122+ the user enters credentials. If credentials are supplied but wrong, returns 401.
124123
125- ** What it does:**
124+ ** Rebase risk:** Low for ` authorization.ts ` (small, self-contained). Medium for
125+ ` error.tsx ` if upstream changes the error page structure.
126126
127- ** a) Bearer auth scheme** (` authorization.ts ` )
128- Changed ` WWW-Authenticate ` response header from ` Basic ` to ` Bearer ` . Chrome and
129- Firefox show a native credentials popup for ` Basic ` on every 401, including XHR
130- responses from the SPA. ` Bearer ` suppresses this popup while keeping the server
131- fully functional — it still reads and validates ` Authorization: Basic ` headers
132- sent by the desktop app.
127+ ---
133128
134- ** b) Local build serving in dev mode** (` flag.ts ` , ` server.ts ` , ` ui.ts ` )
135- Added two env flags:
136- - ` OPENCODE_DEV_UI_DIR ` — if set, the server serves static files from this
137- directory (with SPA index.html fallback) instead of proxying to
138- ` https://app.opencode.ai ` .
139- - ` OPENCODE_DEV_UI_URL ` — overrides the upstream proxy base URL.
129+ ### 5 · Cap diff size to prevent SQLite/V8 crash on large files — ` c37061538 `
140130
141- In desktop dev mode (` !app.isPackaged ` ), ` preferAppEnv() ` automatically sets
142- ` OPENCODE_DEV_UI_DIR ` to ` packages/app/dist ` . This means remote browsers
143- connecting to the Electron-local server see the locally built UI (with fork
144- customizations like the server icon) instead of the production CDN version.
131+ ** Files:** ` packages/opencode/src/tool/apply_patch.ts ` , ` packages/opencode/src/tool/edit.ts `
145132
146- ** Prerequisite:** ` packages/app ` must be built before starting Electron in dev
147- mode:
148- ``` sh
149- cd packages/app && bun run build
150- ```
133+ Truncates patch/diff strings before storing them to prevent SQLite blob limits
134+ and V8 string size limits from crashing the process on very large file edits.
151135
152- ** Rebase risk:** Low for ` authorization.ts ` (one constant). Medium for ` ui.ts `
153- if upstream restructures ` serveUIEffect ` or ` serveEmbeddedUIEffect ` .
136+ ** Rebase risk:** Low — isolated tool change.
154137
155138---
156139
@@ -163,11 +146,12 @@ After every rebase, verify these before merging/releasing:
163146| 1 | Remote browser opens ` http://<host>:4096/ ` (no credentials) | 200, loads UI shell (no browser auth popup) |
164147| 2 | Remote browser fetches ` /favicon-96x96-v3.png ` | 200 (no auth required) |
165148| 3 | SPA fetches ` /global/config ` without credentials | 401 with ` WWW-Authenticate: Bearer … ` (not ` Basic ` ) |
166- | 4 | Desktop app bottom-left shows server icon with current server URL | Visible, clickable |
167- | 5 | Clicking server icon opens server config dialog | Dialog appears, shows local-server config panel |
168- | 6 | Setting local server credentials and restarting → credentials persist | Config survives restart |
169- | 7 | Opening a project in one browser tab → other tabs update | ` project.opened.updated ` event triggers sync |
170- | 8 | Dev mode: remote browser sees fork UI (server icon in bottom left) | Not the upstream ` app.opencode.ai ` version |
149+ | 4 | Navigate to ` http://<host>:4096/<project>/session/<id> ` without credentials | Loads SPA shell, shows auth-required page, no infinite redirect |
150+ | 5 | Desktop app bottom-left shows server icon with current server URL | Visible, clickable |
151+ | 6 | Clicking server icon opens server config dialog | Dialog appears, shows local-server config panel in desktop mode |
152+ | 7 | Setting local server credentials and restarting → credentials persist | Config survives restart |
153+ | 8 | Opening a project in one browser tab → other tabs update | ` project.opened.updated ` event triggers sync |
154+ | 9 | Dev mode: remote browser sees fork UI (server icon in bottom left) | Not the upstream ` app.opencode.ai ` version |
171155
172156Quick automated check (run against a live local server on port 4096):
173157
0 commit comments