@bman654/clodex 2.11.6 → 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.
- package/README.md +3 -0
- package/dist/cli.js +43 -14
- package/dist/cli.js.map +1 -1
- package/docs/windows-setup.md +300 -0
- package/package.json +1 -1
|
@@ -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.
|