You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 65e4535
Browse filesBrowse the repository at this point in the historyBrowse files
fix(desktop): pin the dev sidecar build channel so sessions persist (#344)
The node-sidecar build bakes Script.channel into the server bundle, and
with CODEV_CHANNEL unset packages/script falls back to
`git branch --show-current`. Core's Database.path() suffixes the
session database with that channel (opencode-<channel>.db), so every
branch's dev runs landed in a fresh database and the previous branch's
sessions seemed to vanish. Default CODEV_CHANNEL to "dev" in predev and
prebuild (`??=`, so CI's explicit value passes through) so local builds
always use opencode-dev.db.
Claude-Session: https://claude.ai/code/session_01BcADw33oD8qs5jvn5NWMJx
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: DIVERGENCES.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -259,8 +259,8 @@ from the squash message first, or use a plain merge.
259
259
| Tabs that outlive a sandbox restart: `packages/app/src/context/{server,tabs}.tsx`, `packages/app/src/app.tsx`, `packages/app/src/i18n/{en,es}.ts`, `packages/desktop/src/renderer/index.tsx` | `server.tsx` takes a `retainedServers` prop and exposes it as `retained` — servers that exist but are not connected; `tabs.tsx` reconciles both of its prune effects (open tabs and the closed-tab stack) against `list` **plus** `retained` through a shared `knownServers()`; `app.tsx` threads `retainedServers` from `AppInterface` into `ServerProvider`, and **both** server-scoped routes — `TargetServerRoute` and `ResolvedDraftRoute` — render the fork-only `packages/app/src/sandbox/server-away.tsx` (a `LoaderV2` over the copy, since the state always ends by itself) instead of mounting their providers when the route's server is retained but absent; i18n gains `session.error.disconnected{,.description}`, worded around "this tab" rather than "this session" because the draft route shares the screen; `retained` is read by three fork-only consumers besides the routes — `pages/home-model.tsx` keeps the sidebar's selection on an away server and renders its projects/pinned/recents empty rather than the current server's, and `workspace/tabs.tsx` keeps the tab strip's folder scope — so a restart no longer swaps the sidebar to the local machine and back; `renderer/index.tsx` passes `retainedSandboxServers(...)` (fork-only, in `renderer/sandbox/connections.ts`) — every sandbox that exists, whatever its runtime state. | A sandbox is published as a connection only while it is ready, so applying a mount — which restarts the guest — takes it out of `server.list` for a second or two. The tab pruner read that as "this server is gone" and deleted every tab belonging to it, discarding each tab's memory: the typed prompt, terminal and scroll state. Retention draws the line where the user does — the sandbox still exists, so its tabs do — and explicit removal is unaffected because `dialog-select-server` already calls `tabs.removeServer(key)` and the sandbox then leaves `retained` as well. The route guard is the other half: a retained server has no connection, and mounting `ServerSDKProvider`/`ServerSyncProvider` with `undefined` sends the session's requests to whichever server is selected instead, which answered "this session cannot be found" about a healthy session and bounced the route home through a burst of ~250 navigations. WSL servers are deliberately not retained — same latent problem, but the WSL vertical has no equivalent restart in normal use. | Keep all five edits together — retention without the route guards just moves the confusing state, and the guards without retention are unreachable. Both routes need the guard: they are the two places a tab can point at a server, and the draft one is worse without it — a draft resolves to whichever server is selected, so the prompt would be sent to the local machine rather than the sandbox the tab belongs to. Take upstream's other changes to these files. `retained` must stay a superset relationship with `list` (a server can be in both mid-transition); if upstream reworks the pruner, re-apply the union. The web build passes no `retainedServers`, so `retained` is empty there and every one of these paths collapses to upstream behaviour. |
260
260
|`script/publish.ts`| The `if (Script.release)` block calling `packages/desktop/scripts/finalize-latest-{json,yml}.ts` removed, replaced by a comment. | Both finalizers existed to assemble update metadata into a GitHub release, which the fork no longer publishes to. electron-builder already writes a correct per-platform `latest*.yml` next to each installer, and the fork builds one arch per platform (macOS is universal — see the universal-builds row), so there is nothing left to merge. | Keep removed. If upstream adds further desktop release steps here, drop them unless they target the bucket. |
261
261
|`packages/desktop/scripts/finalize-latest-json.ts`, `packages/desktop/scripts/finalize-latest-yml.ts`| Deleted. | Both `gh release upload` into a GitHub release the fork does not use. `finalize-latest-yml.ts` merged per-arch `latest*.yml` from an upstream build matrix the fork does not run; `finalize-latest-json.ts` additionally built a Tauri-style `latest.json` signed with `@tauri-apps/cli signer` that nothing in the Electron updater path ever reads, and had gone stale besides — it hardcoded `opencode-desktop-mac-{x64,arm64}.app.tar.gz`, an artifact name and format this fork never produces. Leaving them in place is a trap: they look like the release path and upload to the wrong place. | Keep deleted (`git rm` the upstream side of modify/delete conflicts). If a future build matrix produces two `latest.yml` files for one platform (Windows x64 + arm64), write a bucket-targeted merge step rather than restoring these. |
262
-
|`packages/desktop/scripts/prebuild.ts`| On macOS, after the node-sidecar build, runs `bun script/build-vzhelper.ts --universal` in `packages/codev-sandbox` so `dist/codev-vmm` exists (universal) before electron-builder copies it via `mac.extraResources`. | The sandbox vz backend spawns the helper from `process.resourcesPath/codev-vmm` in packaged builds; universal keeps one already-fat Mach-O valid for `--arm64`, `--x64`, and `--universal` packaging (an identical thin binary in both halves would trip `@electron/universal`'s identical-file check). | Keep the darwin-gated build line; take upstream's other steps. It must stay after nothing in particular but before packaging — prebuild is the hook `package:*` runs. |
263
-
| `packages/desktop/scripts/predev.ts` | Runs `bun run install-electron` before the existing icon copy and node-sidecar build; after them, on macOS only, runs `bun script/build-vzhelper.ts` in `packages/codev-sandbox` (host arch, non-fatal with a warning when no Swift toolchain is present). | `bun dev:desktop` was dead on a fresh clone: Electron 42 removed the `postinstall` hook that downloaded the platform binary and exposes it as an `install-electron` bin the consumer must call (electron 41's manifest still had `"postinstall": "node install.js"`; 42.3.3's has no `scripts` field at all), so `bun install` leaves `node_modules/electron` with no `dist/` and `electron-vite dev` aborts with `Error: Electron uninstall` from `getElectronPath`. `predev` is the right hook rather than the root `postinstall`: only someone actually launching the desktop app pays the ~100 MB download, and `install.js` short-circuits once `dist/version` and `path.txt` match, so repeat runs cost nothing. Not added to `prebuild`/`package` — `electron-vite build` never spawns Electron, and `electron-builder` fetches its own copy through `@electron/get`. The vzhelper build fixes the same fresh-clone class of failure for the Work side: the vz provider's dev `helperPath` points at `codev-sandbox/native/vzhelper/dist/codev-vmm`, which only `prebuild` (packaging) built, so every dev run answered the sandbox dialogs with "sandbox VM helper is not installed". Incremental (~1.5s when up to date). | Keep our installer line at the top and the vzhelper block at the bottom; take upstream's other steps. Drop the installer line if upstream adds its own binary-install step or pins Electron back below 42; the vzhelper block is tied to the fork-only sandbox feature and stays as long as it does. |
262
+
|`packages/desktop/scripts/prebuild.ts`|Defaults `CODEV_CHANNEL` to `dev` (`??=`, so CI's explicit value wins) before the node-sidecar build — same reason as the predev row: `packages/script` otherwise derives the channel from `git branch --show-current`, and the channel names the session database. On macOS, after the node-sidecar build, runs `bun script/build-vzhelper.ts --universal` in `packages/codev-sandbox` so `dist/codev-vmm` exists (universal) before electron-builder copies it via `mac.extraResources`. | The sandbox vz backend spawns the helper from `process.resourcesPath/codev-vmm` in packaged builds; universal keeps one already-fat Mach-O valid for `--arm64`, `--x64`, and `--universal` packaging (an identical thin binary in both halves would trip `@electron/universal`'s identical-file check). | Keep the darwin-gated build line; take upstream's other steps. It must stay after nothing in particular but before packaging — prebuild is the hook `package:*` runs. |
263
+
| `packages/desktop/scripts/predev.ts` | Runs `bun run install-electron` before the existing icon copy and node-sidecar build; defaults `CODEV_CHANNEL` to `dev` (`??=`, an explicitly set value wins) before those so the node-sidecar build bakes a stable channel — with it unset, `packages/script` falls back to `git branch --show-current`, and core's `Database.path()` suffixes the session database with the channel (`opencode-<channel>.db`), which stranded every branch's dev-run sessions in their own file; after them, on macOS only, runs `bun script/build-vzhelper.ts` in `packages/codev-sandbox` (host arch, non-fatal with a warning when no Swift toolchain is present). | `bun dev:desktop` was dead on a fresh clone: Electron 42 removed the `postinstall` hook that downloaded the platform binary and exposes it as an `install-electron` bin the consumer must call (electron 41's manifest still had `"postinstall": "node install.js"`; 42.3.3's has no `scripts` field at all), so `bun install` leaves `node_modules/electron` with no `dist/` and `electron-vite dev` aborts with `Error: Electron uninstall` from `getElectronPath`. `predev` is the right hook rather than the root `postinstall`: only someone actually launching the desktop app pays the ~100 MB download, and `install.js` short-circuits once `dist/version` and `path.txt` match, so repeat runs cost nothing. Not added to `prebuild`/`package` — `electron-vite build` never spawns Electron, and `electron-builder` fetches its own copy through `@electron/get`. The vzhelper build fixes the same fresh-clone class of failure for the Work side: the vz provider's dev `helperPath` points at `codev-sandbox/native/vzhelper/dist/codev-vmm`, which only `prebuild` (packaging) built, so every dev run answered the sandbox dialogs with "sandbox VM helper is not installed". Incremental (~1.5s when up to date). | Keep our installer line at the top and the vzhelper block at the bottom; take upstream's other steps. Drop the installer line if upstream adds its own binary-install step or pins Electron back below 42; the vzhelper block is tied to the fork-only sandbox feature and stays as long as it does. |
264
264
| `packages/ui/src/assets/favicon/` (favicon-v3.svg, favicon-v3.ico, site.webmanifest, favicon-96x96-v3.png, apple-touch-icon-v3.png, web-app-manifest-192/512.png) | New codev mark: the blocky "C" in brand red `#ee0033` on a white tile, generated from the same geometry as the app icon by `packages/desktop/scripts/generate-icons.ts` (4x5 grid, one-unit stroke, unit = 120/1024, corner radius 229/1024); `favicon-v3.svg` is the hand-written 512-unit source of that geometry. The tab and `.ico` icons keep the rounded tile so they read as an app icon; `apple-touch-icon-v3.png` and the two maskable `web-app-manifest-*` icons are square and full-bleed, because iOS and PWA launchers apply their own mask and a pre-rounded icon leaves transparent corners that render black. Manifest name "CoDev Code"/"codev"; its `theme_color`/`background_color` stay `#080808` — those describe the PWA's chrome and splash, not the icon. **Was `#00cdcd` on a dark tile** until this row was rewritten: PR #10 pinned the favicon to the then-cyan TUI banner accent, that banner later moved to the navy/red wordmark, and the favicon was left behind as the only cyan in the tree — visibly so on desktop, where the dock icon was red while the window and notification icons were cyan. | Every surface that draws the mark should draw the same mark: the dock icon, the tab, notifications, the `codev --mini` badge and the landing page icon are now one geometry in one red. Cyan on `#131010` was the higher-contrast tile at 16px (~9.4:1 versus red's ~4.2:1) — a real cost, accepted so the brand reads consistently. | Keep our assets (binary: take ours wholesale). Regenerate with the script rather than hand-editing, and edit `icons/icon.svg` + `favicon-v3.svg` together if the geometry ever changes. |
265
265
|`AGENTS.md`| Default-branch note: `dev` → `main` (single merged line; upstream has two lines saying local `main` may not exist); added fork bullets requiring every upstream-file change to get a `DIVERGENCES.md` row (and matching en + es doc updates) in the same PR, and pointing agents at this file's Divergence policy (prefer config/plugins, then `packages/codev-*`, upstream edits last) and Syncing with upstream sections | The fork's default branch is `main` (see the workflow-triggers row); agents kept basing diffs on a nonexistent `dev`. Agents edit upstream files without recording the divergence — and don't apply the placement policy or sync runbook — unless the instructions file says to. | Keep our lines; take upstream's other guidance. |
0 commit comments