Skip to content

feat: persist VS Code serve-web port across restarts (#21)#46

Merged
arzafran merged 1 commit into
mainfrom
feat/serve-web-stable-port
Jun 30, 2026
Merged

feat: persist VS Code serve-web port across restarts (#21)#46
arzafran merged 1 commit into
mainfrom
feat/serve-web-stable-port

Conversation

@arzafran

Copy link
Copy Markdown
Member

What this does

The embedded VS Code (serve-web) browser used to jump to a different URL every time you restarted Programa, because VS Code was launched with --port 0 and the OS handed it a fresh random port each run. This pins the port: Programa now remembers the port VS Code was assigned and asks for the same one next launch, so the browser URL stays stable.

It does this safely — if that remembered port is already taken by something else when Programa starts, it quietly falls back to letting the OS pick one (exactly the old behaviour), so a stale port can never break serve-web.

This is the last remaining piece of #21; the --server-data-dir and persistent connection token were already handled in d0fe6faf.

Summary

  • Sources/AppDelegate.swift:
    • New top-level ServeWebPortStore (alongside ServeWebOutputCollector): persists the port to <ApplicationSupport>/programa/vscode-server/serve-web-port, mirroring the existing connection-token file pattern (no UserDefaults).
    • launchServeWebProcess now passes ServeWebPortStore.portArgument(persistedIn:) instead of the literal "0". The store returns the persisted port only when isPortAvailable (a loopback bind probe with SO_REUSEADDR) confirms it's free, otherwise "0".
    • After serve-web reports its URL, the assigned serveWebURL.port is written back via ServeWebPortStore.persist(port:in:).
    • The TODO(#21) comment is resolved.
  • The port helpers are pure/thread-safe static funcs (same constraint as makePersistentConnectionTokenFile), with isPortAvailable injectable for tests.

No user-facing config or UI — purely internal state persistence.

Test Plan

  • cmux-unit / ServeWebPortStoreTests (7 tests): missing-file → "0", persist→reuse round-trip, unavailable-port fallback, nil dir, out-of-range rejection, parsePort edge cases, and a live-listener occupied-port probe returning unavailable.
  • ./scripts/reload.sh --tag serve-web-port builds clean.
  • Manual: open the VS Code browser, note the 127.0.0.1:<port> URL, quit and relaunch Programa → same port/URL; occupy that port with another process, relaunch → serve-web still comes up (on a new port).
  • CI green.

serve-web ran with --port 0, so the OS assigned a fresh ephemeral port on
every launch and the embedded browser had to re-navigate to a new URL each
restart. Persist the assigned port under the serve-web data dir and request
it again on the next launch. The port is only reused when it is still
bindable on loopback; a now-occupied port falls back to --port 0 so it can
never fail the launch. Completes #21 (the data dir and connection token were
already persisted in d0fe6fa).
@arzafran arzafran merged commit 07dd992 into main Jun 30, 2026
8 checks passed
@arzafran arzafran deleted the feat/serve-web-stable-port branch June 30, 2026 18:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant