Files
opencode/plan.md

7.3 KiB

WSL Server Implementation Backlog

This backlog assumes Electron only. It is ordered chronologically. Each task is intended to be small enough for a new engineer to pick up directly.

Direction

  • Local Server is always Windows-native local on Windows.
  • Local Server has no runtime swap and no WSL mode.
  • WSL servers are a separate, additive concept.
  • A Windows user can add zero or more WSL servers; each is bound to a specific distro.
  • Each WSL server runs as its own sidecar alongside the Windows Local Server.
  • Adding/removing a WSL server is hot; no app restart required.
  • Manage Servers UI exposes an Add WSL button (Windows only) that opens the wizard.

01 Electron Config Split

  • Remove runtime mode / distro from the persisted Local Server config.
  • Introduce a new persisted wslServers key holding an array of WslServerConfig.
  • Define WslServerConfig as { id, distro, onboarding, acknowledgements }.
  • Keep onboarding metadata per WSL server, not globally.
  • Keep acknowledgement state per WSL server.
  • Migrate any legacy localServer.mode === "wsl" entry into a single wslServers entry on read.
  • Drop all references to LocalServerMode from the preload types.

02 Main-Process Multi-Sidecar Startup

  • Always start the Windows local sidecar on app launch.
  • After Windows local is spawned, iterate each WslServerConfig and spawn a WSL sidecar per entry.
  • Give each WSL sidecar its own port and password.
  • Allocate the Windows local port/password once per launch (unchanged).
  • Track all sidecars in a single map keyed by server id.
  • Kill all sidecars on before-quit / will-quit / signal.
  • Include Windows local data in the startup payload unchanged.
  • Include the initial set of WSL servers (with url/password/status) in the startup payload.
  • Emit per-WSL-server lifecycle events (starting, ready, failed, stopping, removed).
  • On startup, do not block the main window on WSL sidecar health.
  • If a WSL sidecar fails health, keep it in the list and mark it failed instead of hanging startup.

03 WSL Server Controller

  • Create a main-process controller that owns wslServers persistence and runtime state.
  • Expose typed events (state, per-item status changes) from the controller.
  • Support one in-flight job per WSL server (not a global in-flight job).
  • Implement addServer(distro) that persists, then spawns and health-checks a new sidecar.
  • Implement removeServer(id) that stops the sidecar and removes it from config.
  • Implement per-server runStep, cancelJob, installWsl, installDistro, installOpencode, openTerminal.
  • Reuse the existing WSL process helpers unchanged.
  • Keep transcripts per server, only for the current app launch.

04 IPC / Preload Surface

  • Rename localServer.* IPC channels to wslServers.*.
  • Add wslServers.getState() returning the full list plus per-server runtime info.
  • Add wslServers.subscribe() with unsubscribe support.
  • Add wslServers.add(distro) (persists config + starts sidecar).
  • Add wslServers.remove(id).
  • Add wslServers.runStep(id, step).
  • Add wslServers.cancelJob(id).
  • Add wslServers.installWsl(id).
  • Add wslServers.installDistro(id, distro).
  • Add wslServers.installOpencode(id).
  • Add wslServers.openTerminal(id).
  • Remove obsolete localServer.setConfig / localServer.* channels.
  • Include url/username/password for each WSL server in the state payload (after sidecar start).

05 Renderer Platform Wiring

  • Expose platform.wslServers as a reactive accessor (list + subscribe).
  • Remove platform.localServer runtime swap APIs.
  • Keep platform.wslServers API available on Windows only.
  • Keep distro-aware path conversion keyed by the active WSL server.
  • When the active server is a WSL sidecar, default pickers to that distro's home.
  • When the active server is the Windows Local Server, keep native Windows picker defaults.

06 Renderer Server List

  • Always include the Windows Local Server in the server list.
  • Include each configured WSL server in the server list with ServerConnection.Sidecar variant wsl.
  • Keep ServerConnection.key returning local:windows for the Windows Local Server.
  • Keep ServerConnection.key returning wsl:<distro> for WSL servers (one per distro).
  • Keep distinct projectsKey buckets for Windows local vs each WSL server.
  • Do not collapse a WSL server into the local projects bucket.

07 Manage Servers UI

  • Remove Swap to WSL and Swap to Windows buttons from the Local Server row.
  • Show the Local Server row exactly like any other server (health, name, active check).
  • Add an Add WSL button next to Add server, visible only on Windows when the platform supports WSL.
  • Add WSL opens the same wizard stepper, scoped to a new WSL server draft.
  • Each WSL server row behaves like a sidecar entry (selectable, default-able, removable).
  • Add a Remove action to the WSL server row menu.
  • Add a Retry setup action when a WSL server is unhealthy.

08 Add WSL Wizard

  • Replace the "Switch" step with a Done step.
  • Step order becomes WSL -> Distro -> OpenCode -> Done.
  • On Done, persist the new WSL server, start the sidecar, and close the dialog.
  • If the user cancels, do not persist anything.
  • Allow resuming an incomplete WSL server wizard from Manage Servers.
  • Remove restart-to-apply copy, restart toasts, and "Use Windows" CTA.
  • Keep failure-only diagnostics panel behavior.

09 Per-WSL Onboarding State

  • Keep WslServerConfig.onboarding per server.
  • Keep WslServerConfig.acknowledgements per server.
  • Resume the wizard for any server where onboarding.complete === false.
  • Mark onboarding complete only after the sidecar becomes healthy.

10 Connection Error Path

  • If the active server is a WSL sidecar and health fails, offer Open setup that deep-links into the wizard for that server.
  • Keep behavior unchanged for the Windows Local Server.
  • Keep behavior unchanged for remote HTTP servers.

11 Legacy Removal

  • Remove Swap to WSL / Swap to Windows popover components.
  • Remove restart-to-apply banner copy and helpers from dialog-local-server.tsx.
  • Remove legacy wslEnabled preload shims.
  • Remove the old DialogSelectServer initialTargetMode prop.
  • Drop localServerKey(config) distinguishing wsl vs windows in the controller (local is always Windows).

12 Verification

  • Verify the Windows Local Server starts unchanged on app launch.
  • Verify Add WSL opens the wizard with the default installed distro preselected.
  • Verify adding a WSL server spawns a new sidecar without restarting the app.
  • Verify removing a WSL server stops the sidecar and removes it from the list.
  • Verify multiple WSL servers can coexist, one per distro.
  • Verify Windows Local Server stays active while WSL sidecars come and go.
  • Verify a failed WSL sidecar does not block app startup or window creation.
  • Verify the ConnectionError deep-link reaches the right wizard scope.
  • Verify legacy localServer.mode === "wsl" persisted config migrates into wslServers on first launch.
  • Verify project histories stay separate per server key.