Commit 086aea2
authored
🤖 feat: VS Code extension prefers oRPC with fallback (#1269)
This updates the VS Code extension to prefer a locally running mux
server (localhost oRPC) for listing workspaces.
If it can't connect, it prompts the user to either fix connection config
or fall back to direct local-file reads.
Adds:
- `mux: Configure Connection` command
- Settings: `mux.connectionMode` and `mux.serverUrl`
- Auth token override stored in VS Code SecretStorage
Validation:
- `bun run --cwd vscode compile`
- `make typecheck`
- `make static-check`
---
<details>
<summary>📋 Implementation Plan</summary>
# 🤖 Plan: VS Code extension → oRPC-first with safe local-file fallback
## Goal
Make the VS Code extension prefer talking to a locally running mux
instance over **localhost + oRPC** instead of reading mux’s
config/session files directly. If the extension **cannot connect**, it
should:
1) show a small prompt explaining why, and
2) let the user either **fix connection config** or **continue with
local file access** (with a warning about possible conflicts).
Net new product code: **~+250 LoC** (recommended approach).
---
## What exists today (repo facts)
- Extension entrypoint: `vscode/src/extension.ts` registers
`mux.openWorkspace`.
- Workspace listing currently uses direct disk reads via shared node
modules:
- `vscode/src/muxConfig.ts` → `new Config().getAllWorkspaceMetadata()`
(reads `~/.mux/config.json` etc)
- `vscode/src/muxConfig.ts` → `readExtensionMetadata()` (reads
`~/.mux/extensionMetadata.json`)
- mux already exposes a local oRPC server:
- HTTP: `POST {baseUrl}/orpc`
- WS: `{baseUrl}/orpc/ws`
- Health: `GET {baseUrl}/health`
- mux server discovery already exists via:
- Env: `MUX_SERVER_URL`, `MUX_SERVER_AUTH_TOKEN`
- Lockfile: `~/.mux/server.lock` via
`src/node/services/serverLockfile.ts`
- Needed API calls are already defined:
- `workspace.list -> FrontendWorkspaceMetadata[]`
- `workspace.activity.list -> Record<workspaceId,
WorkspaceActivitySnapshot>` (recency/streaming/lastModel)
---
## Recommended approach (oRPC-first + guided fallback)
### High-level behavior
1. On `mux.openWorkspace`, try to create an oRPC client using server
discovery (settings/env/lockfile/default).
2. If connection succeeds, list workspaces via oRPC:
- `workspace.list()` for metadata
- `workspace.activity.list()` for recency/streaming
- merge + sort by recency (same UX as today)
3. If connection fails, show a **single warning prompt per VS Code
session**:
- **Fix connection config** → launches a small configuration flow
(settings + secret)
- **Use local file access** → fall back to existing disk-based behavior
for the rest of the session
- **Cancel** → abort the command
4. Auth failures (server reachable but 401/unauthorized) still offer
local-file fallback, but with a **strong warning**.
### Configuration storage (per your answers)
- Add VS Code settings:
- `mux.connectionMode`: `"auto" | "server-only" | "file-only"` (default:
`"auto"`)
- `mux.serverUrl`: optional string (overrides discovery)
- Store auth token override in **VS Code SecretStorage**:
- secret key: `mux.serverAuthToken`
### Discovery precedence
Base URL:
1) VS Code setting `mux.serverUrl` (if set)
2) `process.env.MUX_SERVER_URL`
3) `~/.mux/server.lock` (`ServerLockfile.read()`)
4) `http://localhost:3000`
Auth token:
1) SecretStorage override `mux.serverAuthToken`
2) `process.env.MUX_SERVER_AUTH_TOKEN`
3) lockfile token **only if** lockfile baseUrl matches selected baseUrl
### Connection test (fast + classified)
- `GET {baseUrl}/health` with a short timeout (e.g. 750–1000ms)
- If this fails: classify as **not running / unreachable**.
- If health passes, call `orpc.general.ping("vscode")` (also timeout)
- If this fails with unauthorized/401: classify as **auth
misconfigured**.
---
## Implementation steps (files + concrete changes)
### 1) Add connection + client utilities
**New:** `vscode/src/orpc/client.ts`
- `createVscodeOrpcClient({ baseUrl, authToken }): ORPCClient`
- Use `RPCLink` from `@orpc/client/fetch` and inject `Authorization:
Bearer <token>`.
- Defensive asserts on `baseUrl` shape.
**New:** `vscode/src/orpc/discovery.ts`
- `discoverServerConfig(): Promise<{ baseUrl: string; authToken?:
string; source: ... }>`
- Reads settings/env/lockfile, plus SecretStorage token.
- Keep this mostly pure; isolate VS Code IO to small helpers.
**New:** `vscode/src/orpc/connectionCheck.ts`
- `checkServerReachable(baseUrl): Promise<"ok" | "unreachable">`
- `checkAuth(client): Promise<"ok" | "unauthorized" | "error">`
- Implement timeouts via `AbortController`.
### 2) Wire “oRPC workspace list” into the extension
**Edit:** `vscode/src/muxConfig.ts`
- Split existing logic into:
- `getAllWorkspacesFromFiles()` (keep current disk behavior)
- `getAllWorkspacesFromOrpc()`:
- `client.workspace.list()`
- `client.workspace.activity.list()`
- map activity → `ExtensionMetadata` shape
- merge onto workspace objects and sort by recency
- `getAllWorkspaces()` becomes a mode switch:
- `file-only` → always `getAllWorkspacesFromFiles()`
- `server-only` → error if cannot connect
- `auto` → prefer orpc; on failure show prompt; obey session choice
### 3) Add UX for “fix config vs file access”
**Edit:** `vscode/src/extension.ts`
- Add session-level state:
- `let sessionPreferredMode: "orpc" | "file" | null = null;`
- `let didShowFallbackPrompt = false;`
- If orpc fails and `auto`:
- `showWarningMessage` with actions:
- **Fix connection config**
- **Use local file access**
- **Cancel**
- When auth failure: adjust message text to include strong warning about
conflicts.
### 4) Add a command to fix config
**Edit:** `vscode/src/extension.ts` + `vscode/package.json`
- Register new command: `mux.configureConnection`
- Implementation:
- Prompt for server URL (pre-filled from current setting).
- Prompt for token (password input) and store in SecretStorage.
- Optionally offer “Clear token” / “Clear URL override” via QuickPick.
- From the fallback prompt, invoke this command.
- After configuration, retry connection once.
### 5) Add VS Code settings contributions
**Edit:** `vscode/package.json`
- Add `contributes.configuration` section:
- `mux.connectionMode` (enum)
- `mux.serverUrl` (string)
- Scope settings to **application/machine** (avoid workspace check-in
risk).
---
## Validation plan
- Build + typecheck:
- `bun run -C vscode compile`
- `make typecheck`
- Manual scenarios:
1) mux running (lockfile present): extension uses oRPC and lists
workspaces.
2) mux not running: prompt appears once; choosing file access works.
3) mux running but token wrong (set bad secret): prompt shows auth
warning; file fallback still works.
4) `mux.connectionMode=file-only`: never attempts network.
5) `mux.connectionMode=server-only`: fails fast with actionable error.
---
<details>
<summary>Alternatives considered</summary>
### A) Server-side change: include activity in `workspace.list`
- Add `includeActivity` param so VS Code makes a single RPC call.
- Pros: fewer roundtrips.
- Cons: touches server API + backwards-compat; more surface area than
needed for first iteration.
### B) Don’t add VS Code settings
- Only env vars + lockfile discovery.
- “Fix config” would just open docs / show instructions.
- Pros: minimal change.
- Cons: doesn’t actually let users *fix* misconfig from within VS Code.
</details>
</details>
---
_Generated with `mux` • Model: `openai:gpt-5.2` • Thinking: `xhigh`_
---------
Signed-off-by: Thomas Kosiewski <[email protected]>1 parent 3f74a6d commit 086aea2
File tree
6 files changed
+631
-36
lines changed- vscode
- src
- api
6 files changed
+631
-36
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
25 | | - | |
| 25 | + | |
| 26 | + | |
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
30 | 31 | | |
31 | 32 | | |
32 | 33 | | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
33 | 60 | | |
34 | | - | |
| 61 | + | |
35 | 62 | | |
36 | 63 | | |
37 | 64 | | |
| |||
54 | 81 | | |
55 | 82 | | |
56 | 83 | | |
| 84 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
0 commit comments