@bman654/clodex 2.13.0 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,6 +11,7 @@ This page explains how to bridge **every** Claude Code process on your machine
11
11
  - endpoint-mode server: `ANTHROPIC_BASE_URL` pointing at the gateway;
12
12
  - **no live server: env untouched** — `claude` always launches normally, a stopped server never breaks anything.
13
13
  - Setting **`CLAUDE_CODE_PROCESS_WRAPPER`** to `clodex-claude` makes Claude Code invoke it as `clodex-claude <claude-binary-path> <args...>` for every process it spawns — agents view sessions and background agents are bridged automatically.
14
+ - That ordinary process-wrapper route keeps the handed-in binary. A separate VS Code-only path (`CLAUDE_CODE_ENTRYPOINT=claude-vscode`, explicit bundled executable, live proxy server; on Windows reached through the launcher from `clodex install-vscode-launcher`) may run the clodex-patched install instead, but only when the patch manifest proves by full SHA-256 that it is the patched output of those exact pristine bytes. Missing or mismatched proof keeps the bundled binary. When the manifest identifies the other install, a refused chat spawn emits one stderr line; refused helper commands stay silent, and the inspected extension source sends chat stderr to VS Code's **Claude VSCode** output channel. Tool, hook, and agent children carry child-session markers, while background pty wrappers lose the `claude-vscode` entrypoint; neither shape substitutes. Endpoint, no-server, and direct launches do not substitute either.
14
15
  - For your own terminal sessions, run **`clodex-claude`** instead of `claude` — same auto-discovery, no port or CA path to hardcode anywhere.
15
16
  - Set **`CLODEX_REQUIRE_SERVER=1`** in an isolated routed profile when bypassing Clodex must be impossible. `clodex-claude` then exits with an error if no advertised server passes its process and TCP checks. The default remains fail-open for ordinary installations.
16
17
  - A live standalone server has already snapshotted its credentials and account selection. If a wrapper launch sets `CLODEX_OAUTH_ACCOUNT` or a nonblank `CLODEX_KEY_*`, `clodex-claude` warns that the variable is ignored and still launches through that server. Restart the server with the selector, or save the provider credential and refresh its models, before launching without the process-only variable. With no live server these variables pass through unchanged.
@@ -20,6 +21,39 @@ This page explains how to bridge **every** Claude Code process on your machine
20
21
  `clodex-claude` invocation does not reconfigure the running server.
21
22
  - Proxy-mode wrapper launches remove Anthropic entries from the union of `NO_PROXY` and `no_proxy`, while preserving unrelated bypasses. The manual server output prints the same adjusted values for users who export the proxy settings themselves.
22
23
 
24
+ ## VS Code model picker
25
+
26
+ After installing clodex, patch a compatible local Claude Code install and keep
27
+ `clodex server --proxy` running. On macOS and Linux, find the absolute, extensionless wrapper path:
28
+
29
+ ```bash
30
+ command -v clodex-claude
31
+ ```
32
+
33
+ Put that exact path in VS Code's user `settings.json` (do not use the underlying `.js` file), then
34
+ reload the window. If a Node version manager owns that path, use the stable launcher described in
35
+ step 3 below instead of an ephemeral or version-specific path:
36
+
37
+ ```json
38
+ "claudeCode.claudeProcessWrapper": "/absolute/path/to/clodex-claude"
39
+ ```
40
+
41
+ On Windows the extension cannot spawn npm's script shims; run `clodex install-vscode-launcher` and
42
+ paste the `.exe` path it prints instead — the same substitution then applies. Full walkthrough in
43
+ [windows-setup.md](windows-setup.md).
44
+
45
+ The extension and local Claude install update independently. If their pristine bytes no longer
46
+ match, the wrapper keeps the extension's bundled executable; align compatible builds and run
47
+ `clodex patch` again. A fallback notice appears in VS Code's **Claude VSCode** output channel, not
48
+ as a toast; main-chat lines use the `From claude: ...` prefix.
49
+
50
+ At least two extension behaviors relevant to this setup change when `claudeProcessWrapper` is set;
51
+ these were verified from source rather than in a running editor: it skips a post-update activation health/telemetry probe (not the VS Code
52
+ marketplace updater), and the SDK supplies `--permission-mode default` when no explicit permission
53
+ mode is configured. An explicit mode still wins. Process-level tests cover the wrapper selection;
54
+ the picker was not exercised in a running editor, and the Linux host path was not run for this
55
+ change.
56
+
23
57
  ## Setup steps
24
58
 
25
59
  1. **Install clodex globally** (puts both `clodex` and `clodex-claude` on your PATH):
@@ -113,6 +147,7 @@ The check does not launch Claude.
113
147
  ## Troubleshooting
114
148
 
115
149
  - **Is my session bridged?** In a session started via `clodex-claude` (or spawned by Claude Code with the wrapper set), `/model` accepts your `clodex:` model names and aliases (run `clodex models --list` to see them). If those models are rejected and you haven't run `clodex patch`, that's expected for unpatched binaries — but a bridged session still routes them; an unbridged one errors at the API instead.
150
+ - **VS Code routes models but omits picker entries:** open the **Claude VSCode** channel in the Output panel and look for a one-line `From claude: clodex-claude: running ...` fallback notice. Missing or unreadable state that cannot identify another install is deliberately silent, so the absence of a notice is not proof of selection. The extension and installed CLI update separately, and equal version labels do not prove equal artifacts. Align their builds and run `clodex patch` again. On Windows the wrapper setting must be the `.exe` from `clodex install-vscode-launcher` (npm's `.cmd` shim cannot be configured directly — see [windows-setup.md](windows-setup.md)); the notice appears in the same output channel.
116
151
  - **Server not running:** `clodex-claude` launches plain `claude` by default. With `CLODEX_REQUIRE_SERVER=1`, it exits instead. Check `cat ~/.clodex/server-runtime.json` — a missing file (or records whose `pid` is no longer alive) means no server is advertised; start `clodex server --proxy`. If a server is running but absent from the file, make sure it was not started with `--no-discovery` / `CLODEX_NO_DISCOVERY=1`.
117
152
  - **Wrapper variable empty or stale:** run `echo "$CLAUDE_CODE_PROCESS_WRAPPER"` in a **new login shell**. If it is empty, a `$(command -v ...)` in your profile ran before your Node version manager initialized (see the warning in step 3) — switch to a literal path. If it points at a path that no longer exists (a Node upgrade moved the global bin, an fnm/nvm shim directory expired, or it still names an old hand-written script), spawned processes fail to start or silently skip bridging. Verify with `[ -x "$CLAUDE_CODE_PROCESS_WRAPPER" ] && echo OK`.
118
153
  - **Agents fail to spawn / `env: node: No such file or directory`:** the wrapper's `#!/usr/bin/env node` shebang could not find Node, typically because Claude Code was launched from a GUI (Spotlight, Raycast, an IDE) with a minimal PATH. Use the launcher script from step 3, which resolves Node by absolute path, and test it with `env -i HOME="$HOME" PATH=/usr/bin:/bin "$CLAUDE_CODE_PROCESS_WRAPPER" --version`.
@@ -9,10 +9,15 @@ There are two levels of setup, and they solve different problems:
9
9
  | Setup | What you get |
10
10
  | --- | --- |
11
11
  | [Proxy env vars](#1-route-the-extension-through-clodex) | clodex models **work** in the extension |
12
- | [+ process wrapper](#2-make-clodex-models-appear-in-the-model-picker) | clodex models also **appear in the model picker** |
12
+ | [Launcher](#2-launch-the-extensions-claude-code-through-clodex) | the extension launches Claude Code **through `clodex-claude`**, which follows your running server on its own — no proxy values to copy into VS Code settings — and runs your **clodex-patched install** in place of the bundled binary, so clodex models appear in the extension's **model picker** |
13
13
 
14
- The first is enough if you are happy selecting a model once and leaving it. Add the second if you
15
- want to switch between clodex models from the extension's UI.
14
+ Only the launcher level changes the picker. `clodex-claude` replaces the extension's bundled,
15
+ unpatched Claude Code with your verified clodex-patched install exactly as it does on macOS and
16
+ Linux (see the [VS Code setup in
17
+ background-agents.md](background-agents.md#vs-code-model-picker)): the bundled file's full SHA-256
18
+ must match the pristine hash `clodex patch` recorded, and the patched install must still match its
19
+ recorded output. With the step 1 settings alone the extension launches its own bundled binary and
20
+ the picker stays as it was.
16
21
 
17
22
  ## 1. Route the extension through clodex
18
23
 
@@ -53,14 +58,15 @@ running; stop it and the port goes dead, so every request fails until you start
53
58
 
54
59
  > [!NOTE]
55
60
  > Do not set `claudeCode.claudeProcessWrapper` to `clodex-claude` here. On Windows npm installs that
56
- > bin as `clodex-claude`, `clodex-claude.cmd` and `clodex-claude.ps1` — there is no `.exe`. The
57
- > extension spawns the wrapper without a shell, so pointing it at the `.cmd` fails with
58
- > `spawn EINVAL`. The environment-variable approach above is the one that works.
61
+ > bin as three script shims — `clodex-claude` (a POSIX shell script), `clodex-claude.cmd` and
62
+ > `clodex-claude.ps1` — and no `.exe`. The extension spawns the wrapper without a shell, so pointing
63
+ > it at any of them fails with `spawn EINVAL`.
64
+ > [Step 2](#2-launch-the-extensions-claude-code-through-clodex) builds the `.exe` that setting needs.
59
65
 
60
66
  ### Selecting a clodex model
61
67
 
62
68
  At this level the extension's model picker will **not** list clodex models (see
63
- [why](#why-the-picker-is-empty-without-a-wrapper)). Set one as your default from a terminal instead:
69
+ [why](#why-the-picker-is-empty-without-a-launcher)). Set one as your default from a terminal instead:
64
70
 
65
71
  ```powershell
66
72
  clodex claude
@@ -72,9 +78,9 @@ launch — Claude Code reports it as `Using <model> (from .claude\settings.json)
72
78
 
73
79
  Existing chat tabs keep whatever model they launched with; open a new chat to pick up the change.
74
80
 
75
- ## 2. Make clodex models appear in the model picker
81
+ ## 2. Launch the extension's Claude Code through clodex
76
82
 
77
- ### Why the picker is empty without a wrapper
83
+ ### Why the picker is empty without a launcher
78
84
 
79
85
  `clodex patch` patches the Claude Code binary that npm installed. The VS Code extension does not
80
86
  launch that binary — it ships and launches its own copy:
@@ -83,218 +89,247 @@ launch that binary — it ships and launches its own copy:
83
89
  %USERPROFILE%\.vscode\extensions\anthropic.claude-code-<version>-win32-x64\resources\native-binary\claude.exe
84
90
  ```
85
91
 
86
- On the machine this was written from, that bundled copy was byte-identical (SHA-256) to the pristine
87
- backup clodex took before patching, confirming it was unpatched. The model picker's entries live
88
- inside the binary, so the dropdown shows whatever the *launched* binary offers — which is why models
89
- routed correctly while remaining invisible in the picker.
92
+ On the machine this was first written from, that bundled copy was byte-identical (SHA-256) to the
93
+ pristine backup clodex took before patching, confirming it was unpatched. The model picker's entries
94
+ live inside the binary, so the dropdown shows whatever the *launched* binary offers — which is why
95
+ models routed correctly while remaining invisible in the picker.
90
96
 
91
97
  Routing does not depend on the patch. Claude Code sends the model name it was given, and the proxy
92
98
  maps it. The patch is what makes the binary itself aware of clodex models — listing them in the
93
99
  picker, accepting them as known aliases, and reporting their context windows.
94
100
 
95
- ### Point the extension at the patched binary
101
+ `clodex-claude` solves this itself: when the extension hands it the bundled binary, it substitutes
102
+ the install recorded in `patch-state.json` — only when the extension's full binary hash matches
103
+ that manifest's pristine hash and the install's full hash still matches the recorded patched
104
+ output (equal version labels or sizes are not enough); a mismatch runs the extension's own binary
105
+ and writes one line to its output channel. On Windows the only missing piece was an executable the
106
+ extension could spawn; the launcher below is that piece.
96
107
 
97
- `claudeCode.claudeProcessWrapper` takes an executable path. Claude Code invokes it as:
108
+ ### What the launcher is
109
+
110
+ `claudeCode.claudeProcessWrapper` takes a single executable path. Claude Code invokes it as:
98
111
 
99
112
  ```
100
113
  <wrapper> <path-to-claude-binary> <args...>
101
114
  ```
102
115
 
103
- passing the binary it *would* have run as the first argument. A wrapper that drops that argument and
104
- runs the clodex-patched binary instead gives the extension a patched Claude Code.
105
-
106
- It must be a real `.exe`, for the `spawn EINVAL` reason above. Any language that produces one works;
107
- this is a Go reference implementation. It asks clodex which binary it patched, so the same compiled
108
- exe works on any machine without editing a path:
109
-
110
- ```go
111
- // The extension invokes a claudeProcessWrapper as:
112
- // wrapper.exe <path-to-claude-binary> <args...>
113
- // so the first argument is the binary it would otherwise have run.
114
- //
115
- // Target resolution, in order:
116
- // 1. CLODEX_WRAPPER_TARGET, if set
117
- // 2. binaryPath from clodex's patch manifest (patch-state.json)
118
- // 3. the path the caller handed us — so claude always launches, just unpatched
119
- package main
120
-
121
- import (
122
- "encoding/json"
123
- "os"
124
- "os/exec"
125
- "path/filepath"
126
- "strings"
127
- )
128
-
129
- // clodexHome mirrors getAppHome() in clodex's src/paths.ts: CLODEX_HOME is the
130
- // app directory itself when set, otherwise <home>/.clodex.
131
- func clodexHome() string {
132
- if override := strings.TrimSpace(os.Getenv("CLODEX_HOME")); override != "" {
133
- return override
134
- }
135
- for _, key := range []string{"HOME", "USERPROFILE"} {
136
- if value := os.Getenv(key); value != "" {
137
- return filepath.Join(value, ".clodex")
138
- }
139
- }
140
- if home, err := os.UserHomeDir(); err == nil {
141
- return filepath.Join(home, ".clodex")
142
- }
143
- return ""
144
- }
145
-
146
- // patchedBinary reads the binary clodex last patched, or "" if unavailable.
147
- func patchedBinary() string {
148
- home := clodexHome()
149
- if home == "" {
150
- return ""
151
- }
152
- data, err := os.ReadFile(filepath.Join(home, "patch-state.json"))
153
- if err != nil {
154
- return ""
155
- }
156
- var manifest struct {
157
- BinaryPath string `json:"binaryPath"`
158
- }
159
- if err := json.Unmarshal(data, &manifest); err != nil {
160
- return ""
161
- }
162
- return manifest.BinaryPath
163
- }
164
-
165
- func isFile(path string) bool {
166
- if path == "" {
167
- return false
168
- }
169
- info, err := os.Stat(path)
170
- return err == nil && !info.IsDir()
171
- }
172
-
173
- func main() {
174
- args := os.Args[1:]
175
-
176
- // The caller passes the binary it would have run; hold it as the fallback.
177
- handedIn := ""
178
- if len(args) > 0 {
179
- if base := strings.ToLower(filepath.Base(args[0])); base == "claude.exe" || base == "claude" {
180
- handedIn = args[0]
181
- args = args[1:]
182
- }
183
- }
184
-
185
- target := ""
186
- for _, candidate := range []string{
187
- strings.TrimSpace(os.Getenv("CLODEX_WRAPPER_TARGET")),
188
- patchedBinary(),
189
- handedIn,
190
- } {
191
- if isFile(candidate) {
192
- target = candidate
193
- break
194
- }
195
- }
196
- if target == "" {
197
- os.Stderr.WriteString("claude-wrapper: no Claude Code binary found\n")
198
- os.Exit(1)
199
- }
200
-
201
- cmd := exec.Command(target, args...)
202
- cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
203
- cmd.Env = os.Environ()
204
-
205
- if err := cmd.Run(); err != nil {
206
- if exitErr, ok := err.(*exec.ExitError); ok {
207
- os.Exit(exitErr.ExitCode())
208
- }
209
- os.Stderr.WriteString("claude-wrapper: " + err.Error() + "\n")
210
- os.Exit(1)
211
- }
212
- }
116
+ passing the binary it *would* have run as the first argument. clodex already ships a wrapper for
117
+ exactly this contract — `clodex-claude`, the one [background agents](background-agents.md) use —
118
+ but on Windows npm installs it as three script shims (extensionless, `.cmd`, `.ps1`) and no
119
+ executable, and the extension refuses those with `spawn EINVAL`.
120
+
121
+ `clodex install-vscode-launcher` builds the missing `.exe` on your machine. It compiles a small C#
122
+ program (`launcher\clodex-claude-launcher.cs` in the clodex package — a short, commented file you
123
+ can read) with the C# compiler that ships inside the .NET Framework on every Windows 10/11 install,
124
+ so there is nothing to download and no prebuilt binary to trust. The resulting
125
+ `clodex-claude.exe`:
126
+
127
+ - runs `node.exe` with clodex's `claude-wrapper.js`, passing the extension's arguments through
128
+ with their **values preserved, Unicode included** (it forwards the raw command line rather than
129
+ re-quoting each argument, and the wrapper spawns a native `claude.exe` directly rather than
130
+ through `cmd.exe`, so JSON arguments, empty strings, `%NAMES%` and paths with spaces reach Claude
131
+ Code as sent);
132
+ - ties Node and the Claude Code it launches to its own lifetime with a Windows job object, so that
133
+ when the extension cancels a chat or the window reloads, no `node.exe` or `claude.exe` is left
134
+ behind. If Windows refuses the job object the launcher prints one warning line and runs anyway;
135
+ - lets Ctrl+C reach Claude Code rather than swallowing it, waits, and exits with Claude Code's own
136
+ exit code.
137
+
138
+ `clodex-claude` then does what it does for any Claude process: finds your running
139
+ `clodex server --proxy` and bridges the session to it. **The launcher does not choose the Claude
140
+ Code binary.** It hands `clodex-claude` the bundled `claude.exe` the extension named, exactly as the
141
+ extension would, and `clodex-claude` applies its usual rule: when that file's full SHA-256 equals
142
+ the pristine hash in `patch-state.json` and your patched `claude.exe` still hashes to the recorded
143
+ patched output, it runs the patched install; otherwise it runs the bundled `claude.exe` and, for
144
+ the chat itself, writes one line to the **Claude VSCode** output channel (helper commands the
145
+ extension runs fall back silently). On Windows both files must be `.exe` programs (a
146
+ `.cmd` launcher is never substituted for or into), and the bundled file (about 200 MB) is hashed on
147
+ every launch — `tests/wrapper-substitution.windows.test.ts` prints the measured time on the CI
148
+ runner; see that job's log for the current number.
149
+
150
+ ### Build it
151
+
152
+ Run once, from any terminal, with the Node you normally use:
153
+
154
+ ```powershell
155
+ clodex install-vscode-launcher
213
156
  ```
214
157
 
215
- Build it:
158
+ It prints where the launcher went and the setting to paste:
216
159
 
217
- ```powershell
218
- go build -ldflags="-s -w" -o claude-wrapper.exe .
219
160
  ```
161
+ Built C:\Users\<you>\.clodex\bin\clodex-claude.exe
162
+ compiler: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe
163
+ runs: C:\nvm4w\nodejs\node.exe C:\nvm4w\nodejs\node_modules\@bman654\clodex\dist\claude-wrapper.js
164
+
165
+ Add this to your VS Code settings (Ctrl+Shift+P → Preferences: Open User Settings (JSON)):
166
+
167
+ "claudeCode.claudeProcessWrapper": "C:\\Users\\<you>\\.clodex\\bin\\clodex-claude.exe"
168
+ ```
169
+
170
+ The two paths on the `runs:` line are compiled into the executable. **Re-run the command after
171
+ switching Node versions or moving the clodex install** — it is idempotent and replaces the launcher.
172
+ If the wrapper script it was built against disappears, the launcher exits with code 127 and a
173
+ one-line message naming the command to re-run, which shows up in the extension's **Claude VSCode**
174
+ output channel.
175
+
176
+ If the command reports that it cannot find `csc.exe`, your Windows install is missing the .NET
177
+ Framework 4.x runtime it normally ships with; install .NET Framework 4.8 from Microsoft and re-run.
220
178
 
221
- Then add the setting, keeping the environment variables from step 1 — they do the routing, the
222
- wrapper only chooses the binary:
179
+ ### Add the setting
180
+
181
+ Paste the printed line into your user settings and **remove** the `HTTPS_PROXY`, `HTTP_PROXY` and
182
+ `NODE_EXTRA_CA_CERTS` entries from step 1: `clodex-claude` injects the equivalent values itself
183
+ from the server it discovers, and static entries pointing at a fixed port would keep failing while
184
+ the server is down, whereas the wrapper leaves the environment alone and lets Claude Code start
185
+ unbridged.
223
186
 
224
187
  ```json
225
- "claudeCode.claudeProcessWrapper": "C:\\path\\to\\claude-wrapper.exe"
188
+ "claudeCode.claudeProcessWrapper": "C:\\Users\\<you>\\.clodex\\bin\\clodex-claude.exe"
226
189
  ```
227
190
 
228
- Reload the window. The picker should now list your clodex models alongside the built-in ones.
191
+ Reload the window. `clodex server --proxy` still has to be running — the launcher does not start it.
192
+ Open a new chat: the model picker now lists your clodex favorites and aliases, provided the CLI you
193
+ patched and the extension ship the same Claude Code build. When they drift apart (the extension and
194
+ the CLI update separately), the picker falls back to the bundled list and the **Claude VSCode**
195
+ output channel shows a `From claude: clodex-claude: running ...` line; update the CLI to the
196
+ extension's version and re-run `clodex patch`.
229
197
 
230
198
  If Claude fails to start, remove the `claudeCode.claudeProcessWrapper` line, save, and reload — that
231
- returns you to the step 1 setup.
199
+ returns you to the step 1 setup. The **Claude VSCode** output channel (View → Output) shows the
200
+ launcher's and wrapper's messages, prefixed `From claude:`.
232
201
 
233
202
  > [!TIP]
234
- > If you use a Node version manager, prefer a path that survives version switches. With NVM for
235
- > Windows, `C:\nvm4w\nodejs` is a symlink to the active version, so a path through it stays valid
236
- > across both Node upgrades and Claude Code updates.
203
+ > `clodex-claude` looks for the server in `%USERPROFILE%\.clodex`. If you set `CLODEX_HOME` in your
204
+ > shell only, VS Code will not see it; add it under `claudeCode.environmentVariables` as well.
237
205
 
238
206
  ### Consequences of setting a process wrapper
239
207
 
240
- Claude Code changes two behaviors when `claudeProcessWrapper` is set. Both were read from the
241
- extension's own code rather than observed failing:
208
+ At least two extension behaviors relevant to this setup change when `claudeProcessWrapper` is set.
209
+ These were read from the extension's own code rather than observed in a running editor:
242
210
 
243
- - **Its update check is skipped.** Keeping Claude Code current becomes your job:
211
+ - **A post-update activation health/telemetry probe is skipped.** This is not the VS Code
212
+ marketplace updater and does not show that extension auto-updates stop. The extension can still
213
+ update separately from the CLI, so keep the CLI you patch aligned with the extension you run:
244
214
 
245
215
  ```powershell
246
216
  npm install -g @anthropic-ai/claude-code@latest
247
217
  clodex patch
248
218
  ```
249
219
 
250
- - **Permission-mode resolution moves out of the CLI.** If permission prompts behave unexpectedly
251
- under a wrapper, this is the setting to remove first when narrowing it down.
220
+ - **The SDK supplies a permission mode.** With no explicit mode, wrapper launches add
221
+ `--permission-mode default` instead of leaving default-mode resolution to the CLI. An explicitly
222
+ configured mode still wins. If permission prompts behave unexpectedly under a wrapper, remove the
223
+ wrapper setting while narrowing it down.
252
224
 
253
- Re-running `clodex patch` after each Claude Code update is required regardless of the wrapper — the
254
- patch applies to a specific version of the binary.
225
+ Re-running `clodex patch` after each Claude Code CLI update is required regardless of the wrapper —
226
+ the patch applies to specific bytes.
255
227
 
256
- Keep the two versions aligned as well. With a wrapper set, the binary the extension launches is no
257
- longer the one it ships with, while the extension itself can still update from the marketplace — so
258
- the two can drift apart. Compare them if something starts behaving unexpectedly:
228
+ ### Previous manual workaround
259
229
 
260
- ```powershell
261
- (Get-ChildItem "$env:USERPROFILE\.vscode\extensions\anthropic.claude-code-*" -Directory |
262
- Sort-Object Name | Select-Object -Last 1).Name
263
- claude --version
264
- ```
230
+ Earlier versions of this page carried a hand-built Go program to compile yourself. It ran the
231
+ patched npm `claude.exe` directly, bypassing `clodex-claude` entirely: it needed the step 1
232
+ environment variables, got no server discovery, and had none of `clodex-claude`'s full-hash
233
+ verification, so its target had to be kept aligned and re-patched by hand. It still works if you
234
+ built one, but `clodex install-vscode-launcher` is the supported path and the only one that verifies
235
+ what it runs.
265
236
 
266
237
  ## Troubleshooting
267
238
 
268
- **`spawn EINVAL`** — `claudeProcessWrapper` points at a `.cmd`, `.ps1`, or `.bat`. It must be an
269
- `.exe`.
239
+ **`spawn EINVAL`** — `claudeProcessWrapper` points at a `.cmd`, `.ps1`, or `.bat`. It must be the
240
+ `.exe` from `clodex install-vscode-launcher`.
270
241
 
271
- **Every request fails** — check `clodex server --proxy` is still running. The extension's
272
- `HTTPS_PROXY` points at a fixed port; nothing falls back when the server is gone.
242
+ **`clodex-claude.exe: clodex's claude-wrapper.js is no longer at ...`** (output channel, exit 127)
243
+ — clodex moved or was reinstalled under a different Node. Re-run `clodex install-vscode-launcher`.
273
244
 
274
- **Certificate errors** — `NODE_EXTRA_CA_CERTS` must match the path the server printed, with
275
- backslashes escaped in JSON.
245
+ **`could not find the .NET Framework C# compiler`** — install .NET Framework 4.8 from Microsoft; the
246
+ command looks under `%WINDIR%\Microsoft.NET\Framework64\v4.*` and `Framework\v4.*`.
276
247
 
277
- **Models route but the picker is empty** — expected without the wrapper; see
278
- [step 2](#2-make-clodex-models-appear-in-the-model-picker).
248
+ **Windows Defender or SmartScreen flags the launcher** — it is a freshly compiled unsigned
249
+ executable, so reputation-based checks have never seen it. The source is in the clodex package
250
+ (`launcher\clodex-claude-launcher.cs`); allow the file, or build it again after clearing the
251
+ warning.
252
+
253
+ **Every request fails** — check `clodex server --proxy` is still running. With the step 1 env vars
254
+ the extension's `HTTPS_PROXY` points at a fixed port and nothing falls back when the server is gone;
255
+ with the launcher, sessions started while the server is down run unbridged.
256
+
257
+ **Certificate errors** (step 1 setup) — `NODE_EXTRA_CA_CERTS` must match the path the server
258
+ printed, with backslashes escaped in JSON.
259
+
260
+ **Models route but the picker is empty** — with the step 1 settings alone that is expected: the
261
+ extension launches its own bundled binary. With the launcher, open View → Output → **Claude VSCode**
262
+ and look for a `From claude: clodex-claude: running ...` line: it names the reason (usually the
263
+ extension and the installed CLI are different Claude Code builds). Align them —
264
+ `npm install -g @anthropic-ai/claude-code@<extension version>` — and re-run `clodex patch`. No
265
+ line at all is not proof either way: a missing or unreadable patch manifest is deliberately silent.
266
+ Run `clodex patch`, and check that `CLODEX_HOME`, if you set it, is also set under
267
+ `claudeCode.environmentVariables` so the wrapper reads the same manifest.
268
+
269
+ **`CLODEX_CLAUDE_PATH` points at `clodex-claude` or `claude` without an extension, or at a `.ps1`** —
270
+ Node cannot spawn those on Windows at all. Point it at the `.cmd` or at the program's `.exe`.
279
271
 
280
272
  **`clodex patch` reports it cannot detect the installation** — upgrade clodex; resolving npm
281
273
  launchers to the underlying binary on Windows was fixed in 2.11.5.
282
274
 
283
275
  ## What was verified, and where
284
276
 
285
- Verified on Windows 11, NVM for Windows (node v22.19.0), clodex 2.11.6, Claude Code 2.1.267, Claude
286
- Code VS Code extension 2.1.267, against the ChatGPT/Codex-plan OAuth provider:
277
+ Step 1, and the *previous* Go workaround, were verified on Windows 11, NVM for Windows
278
+ (node v22.19.0), clodex 2.11.6, Claude Code 2.1.267, Claude Code VS Code extension 2.1.267, against
279
+ the ChatGPT/Codex-plan OAuth provider:
287
280
 
288
281
  - Step 1 routes extension traffic through clodex, and a model set as default from `clodex claude` is
289
282
  used by the extension.
290
- - Step 2 makes clodex models appear in the extension's model picker and selectable from it.
283
+ - A process wrapper that launches a patched binary makes clodex models appear in the extension's
284
+ model picker and selectable from it.
291
285
  - The extension's bundled binary was unpatched, matching clodex's pristine backup by SHA-256.
292
- - The wrapper's target resolution, exercised in all four states: `patch-state.json` present (runs the
293
- recorded binary), manifest absent (falls back to the handed-in binary), manifest absent with an
294
- unreadable handed-in path (exits 1 with a message rather than hanging), and the normal case on this
295
- machine. The listing above was extracted from this page and compiled to confirm it builds as
296
- printed.
297
286
 
298
- Not verified: any Node version manager other than NVM for Windows, any provider other than
287
+ Not verified there: any Node version manager other than NVM for Windows, any provider other than
299
288
  ChatGPT/Codex-plan OAuth, and the two wrapper consequences above, which were read from the
300
289
  extension's code rather than reproduced.
290
+
291
+ `clodex install-vscode-launcher` is verified on every pull request by a Windows CI job
292
+ (`windows-latest`, `tests/vscode-launcher.windows.test.ts`): the command builds the launcher with the
293
+ runner's own .NET Framework `csc.exe`; the resulting `.exe` is spawned the way the extension spawns
294
+ it (no shell, arguments as a list) with `node.exe` plus a probe script standing in for the bundled
295
+ Claude Code; argument values with spaces, quotes, JSON, empty strings, `%PATH%`, `&|<>^` and
296
+ non-ASCII text arrive at the probe unchanged through Node and `clodex-claude`; stdin, stdout and the
297
+ exit code pass through; killing the launcher takes Node, the probe and the probe's own child process
298
+ down; re-running the install over a running launcher ends in a one-line message; and a launcher
299
+ whose baked wrapper script was removed exits 127 with the re-run hint. Off Windows the command's
300
+ compiler discovery, source rendering, scratch compile and install are exercised against a fake
301
+ `csc.exe`.
302
+
303
+ The patched-install substitution behind the launcher is verified on the same job by
304
+ `tests/wrapper-substitution.windows.test.ts`: a fake `claude.exe` compiled with the runner's
305
+ `csc.exe` is patched by the real `clodex patch` command, the launcher is handed a pristine copy of
306
+ it and runs the patched one (its output names which copy ran, and the proxy settings and arguments
307
+ reach it intact); a same-size copy with different bytes runs as handed in with exactly one line on
308
+ stderr, written before anything the binary prints; helper spawns (`auth status`,
309
+ `--no-session-persistence` queries) fall back silently; and a dead server disables substitution.
310
+ The same file records what Node reports about files on the runner's NTFS volume (file ids, volume
311
+ serial, change time on an in-place rewrite, the read-only attribute) and prints the time to hash a
312
+ 200 MB file.
313
+
314
+ **Not covered by CI, please report on #245 if you can try it:** the real VS Code extension spawning
315
+ the launcher and showing clodex models in its picker (it is spawned by a test harness in CI, which
316
+ cannot open an editor), Windows Defender/SmartScreen behaviour on the freshly compiled `.exe`, and
317
+ Windows on ARM.
318
+
319
+ ```powershell
320
+ # 1. Build, and note the three paths it prints.
321
+ clodex install-vscode-launcher
322
+
323
+ # 2. Paste the printed "claudeCode.claudeProcessWrapper" line into user settings, remove the
324
+ # HTTPS_PROXY/HTTP_PROXY/NODE_EXTRA_CA_CERTS entries, keep `clodex server --proxy` running,
325
+ # Developer: Reload Window. Then, with a chat open and a reply streaming:
326
+ Get-Process node, claude -ErrorAction SilentlyContinue | Select-Object Id, ProcessName, StartTime
327
+ # Press the stop button in the chat, wait five seconds, run the same line again: the node/claude
328
+ # processes started when the chat began (their StartTime) must be gone. Paste both listings.
329
+
330
+ # 3. Output channel: View > Output > "Claude VSCode". Paste any line starting with
331
+ # "From claude: clodex-claude.exe:" (job-object warnings) — there should be none.
332
+
333
+ # 4. Defender/SmartScreen: paste any prompt or quarantine notice about
334
+ # $env:USERPROFILE\.clodex\bin\clodex-claude.exe, or say there was none.
335
+ ```