@bman654/clodex 2.11.5 → 2.11.7

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.
@@ -0,0 +1,300 @@
1
+ # Using clodex with the Claude Code VS Code extension on Windows
2
+
3
+ This page covers running clodex alongside Claude Code's **VS Code extension** on Windows, so
4
+ `clodex:` models and aliases are usable from the extension's chat panel rather than only from a
5
+ terminal.
6
+
7
+ There are two levels of setup, and they solve different problems:
8
+
9
+ | Setup | What you get |
10
+ | --- | --- |
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** |
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.
16
+
17
+ ## 1. Route the extension through clodex
18
+
19
+ The extension launches Claude Code itself, so there is no `clodex claude` step to hook into.
20
+ Instead, run a proxy-mode server and point the extension's environment at it.
21
+
22
+ **Start the server and leave it running** (a minimized terminal is fine):
23
+
24
+ ```powershell
25
+ clodex server --proxy
26
+ ```
27
+
28
+ It prints the values you need:
29
+
30
+ ```
31
+ clodex proxy-mode server running
32
+ HTTPS_PROXY=http://127.0.0.1:17645
33
+ HTTP_PROXY=http://127.0.0.1:17645
34
+ NODE_EXTRA_CA_CERTS=C:\Users\<you>\.clodex\http-proxy\clodex-ca.pem
35
+ ```
36
+
37
+ **Put those in your VS Code settings** (`Ctrl+Shift+P` → `Preferences: Open User Settings (JSON)`),
38
+ using the values your server printed:
39
+
40
+ ```json
41
+ "claudeCode.environmentVariables": [
42
+ { "name": "HTTPS_PROXY", "value": "http://127.0.0.1:17645" },
43
+ { "name": "HTTP_PROXY", "value": "http://127.0.0.1:17645" },
44
+ { "name": "NODE_EXTRA_CA_CERTS", "value": "C:\\Users\\<you>\\.clodex\\http-proxy\\clodex-ca.pem" }
45
+ ]
46
+ ```
47
+
48
+ Reload the window (`Ctrl+Shift+P` → `Developer: Reload Window`). The extension reads these only when
49
+ it launches Claude, so editing them without reloading changes nothing.
50
+
51
+ Requests from the extension now route through clodex. Bridging only happens while the server is
52
+ running; stop it and the port goes dead, so every request fails until you start it again.
53
+
54
+ > [!NOTE]
55
+ > 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.
59
+
60
+ ### Selecting a clodex model
61
+
62
+ 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:
64
+
65
+ ```powershell
66
+ clodex claude
67
+ ```
68
+
69
+ then `/model`, pick the model, and press Enter to save it as the default for new sessions. That
70
+ writes a `model` key into `~/.claude/settings.json`, which the extension picks up on its next
71
+ launch — Claude Code reports it as `Using <model> (from .claude\settings.json)`.
72
+
73
+ Existing chat tabs keep whatever model they launched with; open a new chat to pick up the change.
74
+
75
+ ## 2. Make clodex models appear in the model picker
76
+
77
+ ### Why the picker is empty without a wrapper
78
+
79
+ `clodex patch` patches the Claude Code binary that npm installed. The VS Code extension does not
80
+ launch that binary — it ships and launches its own copy:
81
+
82
+ ```
83
+ %USERPROFILE%\.vscode\extensions\anthropic.claude-code-<version>-win32-x64\resources\native-binary\claude.exe
84
+ ```
85
+
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.
90
+
91
+ Routing does not depend on the patch. Claude Code sends the model name it was given, and the proxy
92
+ maps it. The patch is what makes the binary itself aware of clodex models — listing them in the
93
+ picker, accepting them as known aliases, and reporting their context windows.
94
+
95
+ ### Point the extension at the patched binary
96
+
97
+ `claudeCode.claudeProcessWrapper` takes an executable path. Claude Code invokes it as:
98
+
99
+ ```
100
+ <wrapper> <path-to-claude-binary> <args...>
101
+ ```
102
+
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
+ }
213
+ ```
214
+
215
+ Build it:
216
+
217
+ ```powershell
218
+ go build -ldflags="-s -w" -o claude-wrapper.exe .
219
+ ```
220
+
221
+ Then add the setting, keeping the environment variables from step 1 — they do the routing, the
222
+ wrapper only chooses the binary:
223
+
224
+ ```json
225
+ "claudeCode.claudeProcessWrapper": "C:\\path\\to\\claude-wrapper.exe"
226
+ ```
227
+
228
+ Reload the window. The picker should now list your clodex models alongside the built-in ones.
229
+
230
+ If Claude fails to start, remove the `claudeCode.claudeProcessWrapper` line, save, and reload — that
231
+ returns you to the step 1 setup.
232
+
233
+ > [!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.
237
+
238
+ ### Consequences of setting a process wrapper
239
+
240
+ Claude Code changes two behaviors when `claudeProcessWrapper` is set. Both were read from the
241
+ extension's own code rather than observed failing:
242
+
243
+ - **Its update check is skipped.** Keeping Claude Code current becomes your job:
244
+
245
+ ```powershell
246
+ npm install -g @anthropic-ai/claude-code@latest
247
+ clodex patch
248
+ ```
249
+
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.
252
+
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.
255
+
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:
259
+
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
+ ```
265
+
266
+ ## Troubleshooting
267
+
268
+ **`spawn EINVAL`** — `claudeProcessWrapper` points at a `.cmd`, `.ps1`, or `.bat`. It must be an
269
+ `.exe`.
270
+
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.
273
+
274
+ **Certificate errors** — `NODE_EXTRA_CA_CERTS` must match the path the server printed, with
275
+ backslashes escaped in JSON.
276
+
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).
279
+
280
+ **`clodex patch` reports it cannot detect the installation** — upgrade clodex; resolving npm
281
+ launchers to the underlying binary on Windows was fixed in 2.11.5.
282
+
283
+ ## What was verified, and where
284
+
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:
287
+
288
+ - Step 1 routes extension traffic through clodex, and a model set as default from `clodex claude` is
289
+ used by the extension.
290
+ - Step 2 makes clodex models appear in the extension's model picker and selectable from it.
291
+ - 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
+
298
+ Not verified: any Node version manager other than NVM for Windows, any provider other than
299
+ ChatGPT/Codex-plan OAuth, and the two wrapper consequences above, which were read from the
300
+ extension's code rather than reproduced.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bman654/clodex",
3
- "version": "2.11.5",
3
+ "version": "2.11.7",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },