* feat(desktop): migration engine for Tauri → Electron handoff
Implements the infrastructure needed to move users from Tauri to Electron
without losing state or Launchpad/Dock presence. Zero user impact on its
own — this becomes live only when paired with a final Tauri release that
calls the snapshot writer and installs Electron.
What lands here:
- Unify app identity with Tauri. electron-builder appId goes from
com.differentai.openwork.electron → com.differentai.openwork so macOS
reuses the same bundle identifier (no duplicate Dock icon, no new
Gatekeeper prompt, same TCC permissions).
- Unify userData path. Electron's app.setPath("userData", ...) now points
at the exact folder Tauri uses (~/Library/Application Support/com.differentai.openwork
on macOS, %APPDATA%/com.differentai.openwork on Windows, ~/.config/
com.differentai.openwork on Linux). An OPENWORK_ELECTRON_USERDATA env
var override is available for dogfooders who want isolation.
- Filename compat. On first launch, Electron copies Tauri's
openwork-workspaces.json → workspace-state.json (leaves the legacy
file in place for rollback safety).
- electron-updater wiring. New deps + IPC handlers
(openwork:updater:check / download / installAndRestart). Packaged-only.
Publish config points at the same GitHub release as Tauri.
- Migration snapshot plumbing.
* Tauri Rust command `write_migration_snapshot` serializes an
allowlist of localStorage keys to app_data_dir/migration-snapshot.v1.json.
* `apps/app/src/app/lib/migration.ts` has matching
writeMigrationSnapshotFromTauri() / ingestMigrationSnapshotOnElectronBoot().
* Scope: workspace list + selection only
(openwork.react.activeWorkspace, .sessionByWorkspace,
openwork.server.list/active/urlOverride/token). Everything else is
cheap to redo.
* Electron main exposes openwork:migration:read / ack IPC; preload
bridges them under window.__OPENWORK_ELECTRON__.migration.
* desktop-runtime-boot.ts ingests the snapshot once on first launch,
hydrates empty localStorage keys, then acks.
- Updated prds/electron-migration-plan.md with the localStorage scope
decision and the remaining work (last Tauri release ships the UI
prompt + installer downloader).
Verified: `pnpm --filter @openwork/app typecheck` ✓,
`pnpm --filter @openwork/desktop build:electron` ✓, `cargo check`
against src-tauri ✓.
* fix(desktop): emit relative asset paths for Electron packaged builds
Packaged Electron loads index.html via file:// (Contents/Resources/app-dist/
index.html inside the .app bundle), but Vite was building with the default
base: '/' which resolves /assets/*.js to the filesystem root. Result: a
working dev experience (Vite dev server on localhost:5173) and a broken
packaged .app that renders an empty <div id="root"></div>.
The Tauri shell doesn't hit this because Tauri serves the built HTML
through its own tauri:// protocol which rewrites paths. Electron's
file:// loader has no such rewriter.
Fix: electron-build.mjs sets OPENWORK_ELECTRON_BUILD=1 when invoking
vite build, and vite.config.ts flips base to './' only when that env
var is set. Dev server and Tauri builds unchanged.
Discovered while manually exercising the Electron prod-build migration
flow (packaged app launch, snapshot ingest, idempotency, updater IPC).
Latent since PR #1522 landed the packaged build path; never caught
because dogfood ran via dev:electron which uses the Vite dev server.
---------
Co-authored-by: Benjamin Shafii <benjamin@openworklabs.com>
* feat(desktop): electron 1:1 port alongside Tauri, fix workspace-create visibility
Adds an Electron shell that mirrors the Tauri desktop runtime (bridge,
dialogs, deep links, runtime supervision for openwork-server / opencode /
opencode-router / orchestrator, packaging via electron-builder). Tauri
dev/build scripts remain the default; Electron runs via dev:electron and
package:electron.
Also fixes the "workspace I just created is invisible until I restart the
app" bug: the React routes only wrote to desktop-side state, so the running
openwork-server never learned about the new workspace and the sidebar (which
is populated from the server list) dropped it. The create flow now also
calls openworkClient.createLocalWorkspace so POST /workspaces/local
registers the workspace at runtime.
Other small fixes included:
- Clears the "OpenWork server Disconnected" flash caused by React 18
StrictMode double-invoking the connection stores' start/dispose pair.
- Real app icon wired into Electron (dock + BrowserWindow + builder).
- Fix a latent rm-import bug in runtime.mjs that silently skipped
orchestrator auth cleanup.
- Locale copy updated to say "OpenWork desktop app" instead of "Tauri app".
- Adds description/author to apps/desktop/package.json to silence
electron-builder warnings.
* docs(prds): Tauri → Electron migration plan
Describes how we'll cut every current Tauri user over to the Electron
build via the existing Tauri updater (one last migration release that
downloads + launches the Electron installer), how we unify app identity
so Electron reads the same userData Tauri wrote (zero-copy data
migration), and how ongoing auto-updates switch to electron-updater
publishing to the same GitHub releases.
---------
Co-authored-by: Benjamin Shafii <benjamin@openworklabs.com>
* feat(server): add workspace session read APIs
Expose workspace-scoped session list, detail, message, and snapshot reads so the client can fetch session data without depending on activation choreography.
* feat(app): route mounted session reads through OpenWork APIs
Use the new workspace-scoped session read endpoints for mounted OpenWork clients so the current frontend stops depending on direct session proxy reads for list, detail, message, and todo loading.
* feat(app): add React read-only session transcript
Introduce a feature-gated React island for the session transcript so we can replace the session surface incrementally while keeping the Solid shell intact.
* feat(app): add React session composer surface
Extend the feature-gated React session island to own its draft, prompt send, stop flow, and snapshot polling so the session body can evolve independently from the Solid composer.
* feat(app): add React session transition model
Keep the React session surface stable during session switches by tracking rendered vs intended session state and exposing a developer debug panel for render-source and transition inspection.
* docs(prd): add React migration plan to repo
Copy the incremental React adoption PRD into the OpenWork repo so the migration plan lives next to the implementation and PR branch.
* docs(prd): sync full React migration plan
Replace the shortened repo copy with the full incremental React adoption PRD so the implementation branch and product plan stay in sync.
* feat(desktop): add React session launch modes
Add dedicated Tauri dev and debug-build entrypoints for the React session path and honor a build-time React session flag before local storage so the alternate shell is easy to launch and reproduce.
* fix(app): fall back to legacy mounted session reads
Keep the new app working against older OpenWork servers by falling back to the original mounted OpenCode session reads when the workspace-scoped session read APIs are unavailable.
Keep OpenCode version selection predictable by reading a single repo-wide constant and packaging that pin into orchestrator builds. Remove env and latest-release fallbacks so desktop, workers, snapshots, and CI stay aligned.
Co-authored-by: Omar McAdam <omar@OpenWork-Studio.localdomain>