@bman654/clodex 0.1.0 → 0.1.1
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 +48 -6
- package/dist/{chunk-C7N2JI7Z.js → chunk-3XM6UZWP.js} +138 -28
- package/dist/chunk-3XM6UZWP.js.map +1 -0
- package/dist/claude-wrapper.js +10 -4
- package/dist/claude-wrapper.js.map +1 -1
- package/dist/cli.js +265 -193
- package/dist/cli.js.map +1 -1
- package/docs/background-agents.md +41 -7
- package/docs/model-picker.png +0 -0
- package/package.json +1 -1
- package/dist/chunk-C7N2JI7Z.js.map +0 -1
|
@@ -5,8 +5,8 @@ This page explains how to bridge **every** Claude Code process on your machine
|
|
|
5
5
|
## How it works
|
|
6
6
|
|
|
7
7
|
- One global **`clodex server --proxy`** runs in the background (proxy mode is the recommended mode for this setup: your existing Anthropic login keeps working, and only `clodex:` models / aliases are rerouted to OpenAI).
|
|
8
|
-
- On startup the server
|
|
9
|
-
- The **`clodex-claude`** bin (installed alongside `clodex`) reads that file, checks
|
|
8
|
+
- On startup the server adds its own record to `~/.clodex/server-runtime.json` (`mode`, `port`, `pid`, and in proxy mode the CA certificate path). The file holds one record per running server — several `clodex server` instances (say a proxy server for Claude Code plus a separate endpoint server for another tool) can be advertised at once — and each server removes only its own record on shutdown. Start a server with `--no-discovery` (or `CLODEX_NO_DISCOVERY=1`) to keep it out of the file entirely so `clodex-claude` never bridges to it.
|
|
9
|
+
- The **`clodex-claude`** bin (installed alongside `clodex`) reads that file, checks each server is actually alive (pid + TCP probe), picks one — proxy-mode servers are preferred over endpoint-mode (bridging keeps Claude Code's own auth), newest first within a mode — and launches the real `claude` binary with the right env injected:
|
|
10
10
|
- proxy-mode server: `HTTPS_PROXY`/`HTTP_PROXY` + `NODE_EXTRA_CA_CERTS`, with `ANTHROPIC_BASE_URL` removed;
|
|
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.
|
|
@@ -29,13 +29,46 @@ This page explains how to bridge **every** Claude Code process on your machine
|
|
|
29
29
|
|
|
30
30
|
Bridging only happens while this server is running. When it is not, `clodex-claude` falls back cleanly and launches `claude` with an untouched environment.
|
|
31
31
|
|
|
32
|
-
3. **Point Claude Code's process wrapper at `clodex-claude`.** Add to your shell profile
|
|
32
|
+
3. **Point Claude Code's process wrapper at `clodex-claude`.** This variable must hold a **literal absolute path that is valid in any environment** — see the Node version manager warning below before you pick it. Add to your shell profile (`~/.zprofile` / `~/.zshrc` for zsh, `~/.bash_profile` for bash):
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
export CLAUDE_CODE_PROCESS_WRAPPER="$
|
|
35
|
+
export CLAUDE_CODE_PROCESS_WRAPPER="$HOME/.local/bin/clodex-claude"
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
Then open a new terminal (or `source` the profile)
|
|
38
|
+
Then open a new terminal (or `source` the profile) and confirm it resolves:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
echo "$CLAUDE_CODE_PROCESS_WRAPPER" # must be non-empty
|
|
42
|
+
[ -x "$CLAUDE_CODE_PROCESS_WRAPPER" ] && echo OK # must print OK
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Every `claude` process Claude Code spawns from sessions started in that environment is now bridged.
|
|
46
|
+
|
|
47
|
+
> ### ⚠️ If you use a Node version manager (fnm, nvm, asdf, volta), read this
|
|
48
|
+
>
|
|
49
|
+
> Do **not** write `export CLAUDE_CODE_PROCESS_WRAPPER="$(command -v clodex-claude)"`. It fails in three separate ways:
|
|
50
|
+
>
|
|
51
|
+
> 1. **Profile ordering.** Version managers usually initialize in `~/.zshrc`, which runs *after* `~/.zprofile` for login shells. A `command -v` in `~/.zprofile` therefore finds nothing and silently exports an **empty string** — no error, just no bridging.
|
|
52
|
+
> 2. **Ephemeral shim paths.** Some managers put the active binary in a per-shell directory (fnm's `.../fnm_multishells/<pid>_<timestamp>/bin`, for example). That path dies with the shell that created it, so a value captured at login is a dead path for anything spawned later. Others (nvm, asdf, volta) use a *version-specific* path that breaks the next time you upgrade Node.
|
|
53
|
+
> 3. **Shebang PATH dependence.** The installed `clodex-claude` is a JS file with a `#!/usr/bin/env node` shebang, so it needs `node` **on PATH at spawn time**. If Claude Code is launched from a GUI context (Spotlight, Raycast, an IDE) rather than a terminal, PATH is minimal, `env node` fails, and the wrapper cannot start — which breaks spawning agents entirely.
|
|
54
|
+
>
|
|
55
|
+
> **Robust fix — a tiny launcher in a stable directory.** Create `~/.local/bin/clodex-claude` (any directory that never moves), make it executable with `chmod +x`, and point the variable at it. It resolves Node explicitly instead of trusting PATH:
|
|
56
|
+
>
|
|
57
|
+
> ```sh
|
|
58
|
+
> #!/bin/sh
|
|
59
|
+
> # Resolve node without depending on PATH, then run the real wrapper.
|
|
60
|
+
> NODE="$HOME/.local/share/fnm/aliases/default/bin/node" # fnm: stable, follows your default version
|
|
61
|
+
> [ -x "$NODE" ] || NODE=node # fallback if that path is absent
|
|
62
|
+
> exec "$NODE" "$(npm root -g)/@bman654/clodex/dist/claude-wrapper.js" "$@"
|
|
63
|
+
> ```
|
|
64
|
+
>
|
|
65
|
+
> Replace the `NODE=` line with your manager's stable path — nvm: `"$NVM_DIR/alias/default"` names the version, so use `"$NVM_DIR/versions/node/$(cat "$NVM_DIR/alias/default")/bin/node"`; volta: `"$HOME/.volta/bin/node"`; asdf: `"$(asdf which node)"` captured once. Hardcode the resolved `npm root -g` path if you prefer not to shell out. Verify the result works even with no PATH:
|
|
66
|
+
>
|
|
67
|
+
> ```bash
|
|
68
|
+
> env -i HOME="$HOME" PATH=/usr/bin:/bin ~/.local/bin/clodex-claude --version
|
|
69
|
+
> ```
|
|
70
|
+
>
|
|
71
|
+
> That must print a Claude Code version. If it prints `env: node: No such file or directory`, the Node path in your launcher is wrong.
|
|
39
72
|
|
|
40
73
|
4. **Use `clodex-claude` (not `claude`) for terminal sessions you want bridged:**
|
|
41
74
|
|
|
@@ -49,6 +82,7 @@ Port and CA discovery are automatic via `~/.clodex/server-runtime.json` — do n
|
|
|
49
82
|
## Troubleshooting
|
|
50
83
|
|
|
51
84
|
- **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.
|
|
52
|
-
- **Server not running:** `clodex-claude` silently launches plain `claude`. Check `cat ~/.clodex/server-runtime.json` — missing file (or
|
|
53
|
-
- **
|
|
85
|
+
- **Server not running:** `clodex-claude` silently launches plain `claude`. 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`.
|
|
86
|
+
- **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`.
|
|
87
|
+
- **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`.
|
|
54
88
|
- **Port conflicts:** the server default is 17645; `clodex server --proxy --port <n>` picks another. `clodex-claude` reads the actual port from the runtime file, so no other change is needed.
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server-runtime.ts","../src/paths.ts","../src/config.ts","../src/launch.ts","../src/binary-lookup.ts"],"sourcesContent":["// src/server-runtime.ts\n//\n// Runtime-state advertisement for the standalone `clodex server` command.\n// The server writes ~/.clodex/server-runtime.json on startup and removes it on\n// graceful shutdown so other processes (notably the `clodex-claude` wrapper\n// bin) can discover the running server's mode, port, and CA path without any\n// hardcoding. Stale detection is the READER's job: a crashed server leaves the\n// file behind, so readers must validate pid liveness before trusting it.\n//\n// NOTE: only the standalone `clodex server` command writes this file. The\n// per-session MITM proxy spawned by `clodex claude --proxy` is private to that\n// session and must NOT advertise itself here.\n\nimport { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { getAppHome } from './paths.js';\n\nexport interface ServerRuntimeState {\n mode: 'endpoint' | 'proxy';\n port: number;\n pid: number;\n /** Proxy mode only: absolute path to the CA bundle a client must trust. */\n caPath?: string;\n startedAt: string;\n}\n\ninterface HomeEnv {\n HOME?: string;\n CLODEX_HOME?: string;\n USERPROFILE?: string;\n}\n\nexport function getServerRuntimePath(env: HomeEnv = process.env): string {\n return join(getAppHome(env), 'server-runtime.json');\n}\n\nfunction isPort(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value >= 1 && value <= 65535;\n}\n\n/** Parse + validate a raw server-runtime.json payload. Returns null for anything malformed. */\nexport function parseServerRuntimeState(raw: string): ServerRuntimeState | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n if (!parsed || typeof parsed !== 'object') return null;\n const record = parsed as Record<string, unknown>;\n\n const mode = record['mode'];\n if (mode !== 'endpoint' && mode !== 'proxy') return null;\n if (!isPort(record['port'])) return null;\n const pid = record['pid'];\n if (typeof pid !== 'number' || !Number.isInteger(pid) || pid <= 0) return null;\n const startedAt = typeof record['startedAt'] === 'string' ? record['startedAt'] : '';\n\n const caPath = record['caPath'];\n if (mode === 'proxy') {\n // A proxy-mode server without a CA path is unusable to clients — treat as invalid.\n if (typeof caPath !== 'string' || !caPath.trim()) return null;\n return { mode, port: record['port'], pid, caPath, startedAt };\n }\n return { mode, port: record['port'], pid, startedAt };\n}\n\n/** kill(pid, 0) liveness probe: EPERM still means the process exists. */\nexport function isPidAlive(\n pid: number,\n kill: (pid: number, signal: number) => unknown = process.kill.bind(process),\n): boolean {\n try {\n kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException)?.code === 'EPERM';\n }\n}\n\n/** Best-effort write — a state-file failure must never take the server down. */\nexport function writeServerRuntimeState(state: ServerRuntimeState, env: HomeEnv = process.env): void {\n try {\n const path = getServerRuntimePath(env);\n mkdirSync(dirname(path), { recursive: true, mode: 0o700 });\n writeFileSync(path, `${JSON.stringify(state, null, 2)}\\n`, { encoding: 'utf8', mode: 0o600 });\n } catch {\n // Discovery is optional; the server itself keeps running.\n }\n}\n\n/** Best-effort removal on graceful shutdown. Missing file is fine. */\nexport function removeServerRuntimeState(env: HomeEnv = process.env): void {\n try {\n rmSync(getServerRuntimePath(env), { force: true });\n } catch {\n // Stale files are handled by readers via pid liveness.\n }\n}\n\nexport interface ReadServerRuntimeOptions {\n isAlive?: (pid: number) => boolean;\n}\n\n/**\n * Read the advertised server state, returning null when the file is missing,\n * malformed, or refers to a process that is no longer alive (crashed server).\n */\nexport function readLiveServerRuntimeState(\n env: HomeEnv = process.env,\n options: ReadServerRuntimeOptions = {},\n): ServerRuntimeState | null {\n let raw: string;\n try {\n raw = readFileSync(getServerRuntimePath(env), 'utf8');\n } catch {\n return null;\n }\n const state = parseServerRuntimeState(raw);\n if (!state) return null;\n const alive = options.isAlive ?? isPidAlive;\n return alive(state.pid) ? state : null;\n}\n","import { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { cpSync, existsSync, mkdirSync, readdirSync } from 'node:fs';\n\nexport const APP_DIR_NAME = 'clodex';\n/** One-time silent migration source: the relay-ai config home. */\nexport const LEGACY_APP_DIR_NAME = 'relay-ai';\n\ninterface HomeEnv {\n HOME?: string;\n CLODEX_HOME?: string;\n USERPROFILE?: string;\n}\n\nfunction userHome(env: HomeEnv = process.env): string {\n return env.HOME ?? env.USERPROFILE ?? homedir();\n}\n\nexport function resolveAppHomeOverride(env: HomeEnv = process.env): string | undefined {\n const override = env.CLODEX_HOME;\n return override?.trim() || undefined;\n}\n\nexport function getAppHome(env: HomeEnv = process.env): string {\n const override = resolveAppHomeOverride(env);\n if (override) return override;\n return join(userHome(env), `.${APP_DIR_NAME}`);\n}\n\nexport function getLegacyAppHome(env: HomeEnv = process.env): string {\n return join(userHome(env), `.${LEGACY_APP_DIR_NAME}`);\n}\n\nlet legacyMigrationDone = false;\n\n/**\n * One-time silent migration: when the clodex home does not exist yet but a\n * legacy ~/.relay-ai does, copy its config + auth state over so existing\n * providers and OAuth credentials keep working. The legacy directory itself is\n * never modified or deleted.\n */\nexport function ensureLegacyAppHomeMigrated(env: HomeEnv = process.env): void {\n if (legacyMigrationDone) return;\n legacyMigrationDone = true;\n try {\n const appHome = getAppHome(env);\n if (existsSync(appHome)) return;\n const legacyHome = getLegacyAppHome(env);\n if (!existsSync(legacyHome)) return;\n\n mkdirSync(appHome, { recursive: true, mode: 0o700 });\n for (const entry of readdirSync(legacyHome)) {\n if (entry === 'logs') continue; // session logs are not config/auth state\n cpSync(join(legacyHome, entry), join(appHome, entry), { recursive: true });\n }\n } catch {\n // Migration is best-effort; a fresh home still works.\n }\n}\n\n/** Test hook: allow the migration to run again against a new CLODEX_HOME. */\nexport function resetLegacyMigrationForTests(): void {\n legacyMigrationDone = false;\n}\n\nexport function getConfigPath(env: HomeEnv = process.env): string {\n return join(getAppHome(env), 'config.json');\n}\n\nexport function getProvidersPath(env: HomeEnv = process.env): string {\n return join(getAppHome(env), 'providers.json');\n}\n\nexport function getLogsPath(env: HomeEnv = process.env): string {\n return join(getAppHome(env), 'logs');\n}\n","import type { UserPreferences } from './types.js';\nimport { dirname } from 'node:path';\nimport { mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { ensureLegacyAppHomeMigrated, getConfigPath } from './paths.js';\n\nfunction readJsonFile(path: string): UserPreferences | null {\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf8'));\n return parsed && typeof parsed === 'object' ? parsed as UserPreferences : null;\n } catch {\n return null;\n }\n}\n\nfunction readConfig(): UserPreferences {\n ensureLegacyAppHomeMigrated();\n return readJsonFile(getConfigPath()) ?? {};\n}\n\nfunction writeConfig(config: UserPreferences): void {\n const configPath = getConfigPath();\n mkdirSync(dirname(configPath), { recursive: true, mode: 0o700 });\n writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\\n`, { encoding: 'utf8', mode: 0o600 });\n}\n\nexport function loadPreferences(): UserPreferences {\n const config = readConfig();\n return {\n lastModel: config.lastModel,\n lastProvider: config.lastProvider,\n recentModelsByProvider: config.recentModelsByProvider,\n favoriteModels: config.favoriteModels,\n modelAliases: config.modelAliases,\n claudeBridgeMode: config.claudeBridgeMode,\n serverBridgeMode: config.serverBridgeMode,\n appPathOverrides: config.appPathOverrides,\n recentLaunchFolders: config.recentLaunchFolders,\n server: config.server,\n };\n}\n\nexport function savePreferences(prefs: Partial<Pick<UserPreferences, 'lastModel' | 'lastProvider' | 'recentModelsByProvider' | 'favoriteModels' | 'modelAliases' | 'claudeBridgeMode' | 'serverBridgeMode' | 'appPathOverrides' | 'recentLaunchFolders'>>): void {\n const config = readConfig();\n if (prefs.lastModel !== undefined) config.lastModel = prefs.lastModel;\n if (prefs.lastProvider !== undefined) config.lastProvider = prefs.lastProvider;\n if (prefs.recentModelsByProvider !== undefined) config.recentModelsByProvider = prefs.recentModelsByProvider;\n if (prefs.favoriteModels !== undefined) config.favoriteModels = prefs.favoriteModels;\n if (prefs.modelAliases !== undefined) config.modelAliases = prefs.modelAliases;\n if (prefs.claudeBridgeMode !== undefined) config.claudeBridgeMode = prefs.claudeBridgeMode;\n if (prefs.serverBridgeMode !== undefined) config.serverBridgeMode = prefs.serverBridgeMode;\n if (prefs.appPathOverrides !== undefined) config.appPathOverrides = prefs.appPathOverrides;\n if (prefs.recentLaunchFolders !== undefined) config.recentLaunchFolders = prefs.recentLaunchFolders;\n writeConfig(config);\n}\n\nexport function getAppPathOverride(appId: string): string | undefined {\n const value = loadPreferences().appPathOverrides?.[appId];\n return typeof value === 'string' && value.trim() ? value : undefined;\n}\n\nexport function setAppPathOverride(appId: string, path: string | null): Record<string, string> {\n const config = readConfig();\n const next = { ...(config.appPathOverrides ?? {}) };\n const trimmed = path?.trim() ?? '';\n if (trimmed) next[appId] = trimmed;\n else delete next[appId];\n config.appPathOverrides = next;\n if (Object.keys(next).length === 0) delete config.appPathOverrides;\n writeConfig(config);\n return next;\n}\n\n/**\n * Resolve the bridge mode for a command. An explicit flag applies to that run only —\n * it is persisted as the command's default ONLY when the caller opts in (--save-mode).\n * With no flag, the saved per-command default applies; with no saved default, proxy.\n */\nexport function resolveBridgeMode(\n command: 'claude' | 'server',\n explicit: import('./types.js').BridgeMode | undefined,\n opts: { persist?: boolean } = {},\n): import('./types.js').BridgeMode {\n const key = command === 'claude' ? 'claudeBridgeMode' : 'serverBridgeMode';\n if (explicit) {\n if (opts.persist === true) savePreferences({ [key]: explicit });\n return explicit;\n }\n return loadPreferences()[key] ?? 'proxy';\n}\n\nconst MAX_RECENT_MODELS = 3;\nconst MAX_RECENT_LAUNCH_FOLDERS = 6;\n\nexport function recordLaunchFolder(folder: string): string[] {\n const trimmed = folder.trim();\n if (!trimmed) return loadPreferences().recentLaunchFolders ?? [];\n const config = readConfig();\n const prev = config.recentLaunchFolders ?? [];\n const next = [trimmed, ...prev.filter(path => path !== trimmed)].slice(0, MAX_RECENT_LAUNCH_FOLDERS);\n config.recentLaunchFolders = next;\n writeConfig(config);\n return next;\n}\n\nexport function recordLaunchSelection(\n _agent: 'claude',\n providerId: string,\n modelId: string,\n prefs: UserPreferences,\n): void {\n const prevRecent = prefs.recentModelsByProvider?.[providerId] ?? [];\n const updatedRecent = [modelId, ...prevRecent.filter(id => id !== modelId)].slice(0, MAX_RECENT_MODELS);\n savePreferences({\n lastProvider: providerId,\n lastModel: modelId,\n recentModelsByProvider: { ...prefs.recentModelsByProvider, [providerId]: updatedRecent },\n });\n}\n\nconst SERVER_PASSWORD_SERVICE = 'clodex-server-password';\nconst SERVER_PASSWORD_ACCOUNT = 'server-password';\n\nasync function getServerPasswordKeyring(): Promise<any | null> {\n try {\n const { Entry } = await import('@napi-rs/keyring');\n return new Entry(SERVER_PASSWORD_SERVICE, SERVER_PASSWORD_ACCOUNT);\n } catch {\n return null;\n }\n}\n\nexport async function getSavedServerPassword(): Promise<string | null> {\n const config = readConfig();\n if (config.server?.savedPassword) {\n const pwd = config.server.savedPassword;\n const keyring = await getServerPasswordKeyring();\n if (keyring) {\n try {\n await keyring.setPassword(pwd);\n delete config.server.savedPassword;\n if (Object.keys(config.server).length === 0) delete config.server;\n writeConfig(config);\n } catch {\n // Fallback: keep in config.json if keyring fails\n }\n }\n return pwd;\n }\n\n const keyring = await getServerPasswordKeyring();\n if (keyring) {\n try {\n return await keyring.getPassword();\n } catch {\n return null;\n }\n }\n return null;\n}\n\nexport async function setSavedServerPassword(password: string): Promise<void> {\n const keyring = await getServerPasswordKeyring();\n if (keyring) {\n try {\n await keyring.setPassword(password);\n return;\n } catch {\n // Fallback\n }\n }\n const config = readConfig();\n config.server = {\n ...(config.server ?? {}),\n savedPassword: password,\n };\n writeConfig(config);\n}\n\nexport async function clearSavedServerPassword(): Promise<void> {\n const keyring = await getServerPasswordKeyring();\n if (keyring) {\n try {\n await keyring.deletePassword();\n } catch {\n // Ignore\n }\n }\n const config = readConfig();\n if (!config.server) return;\n delete config.server.savedPassword;\n if (Object.keys(config.server).length === 0) delete config.server;\n writeConfig(config);\n}\n\nexport function getServerExposedProviders(): string[] | null {\n const list = readConfig().server?.exposedProviders;\n return list && list.length > 0 ? list : null;\n}\n\nexport function setServerExposedProviders(providerIds: string[]): void {\n const config = readConfig();\n config.server = {\n ...(config.server ?? {}),\n exposedProviders: providerIds,\n };\n writeConfig(config);\n}\n\nexport function getServerMaskGatewayIds(): boolean {\n return readConfig().server?.maskGatewayIds ?? true;\n}\n\nexport function setServerMaskGatewayIds(mask: boolean): void {\n const config = readConfig();\n config.server = {\n ...(config.server ?? {}),\n maskGatewayIds: mask,\n };\n writeConfig(config);\n}\n\nexport function getServerFavoritesOnly(): boolean {\n return readConfig().server?.favoritesOnly ?? false;\n}\n\nexport function setServerFavoritesOnly(favoritesOnly: boolean): void {\n const config = readConfig();\n config.server = {\n ...(config.server ?? {}),\n favoritesOnly,\n };\n writeConfig(config);\n}\n\nexport function getServerListenMode(): 'local' | 'network' {\n return readConfig().server?.listenMode === 'network' ? 'network' : 'local';\n}\n\nexport function setServerListenMode(listenMode: 'local' | 'network'): void {\n const config = readConfig();\n config.server = {\n ...(config.server ?? {}),\n listenMode,\n };\n writeConfig(config);\n}\n","// src/launch.ts\nimport { execSync, spawn } from 'node:child_process';\nimport { existsSync, appendFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { getAppPathOverride } from './config.js';\nimport { findBinaryOnPath } from './binary-lookup.js';\n\nconst isWindows = process.platform === 'win32';\n\nconst FALLBACK_PATHS = isWindows\n ? [\n join(process.env['APPDATA'] ?? homedir(), 'npm', 'claude.cmd'),\n join(process.env['APPDATA'] ?? homedir(), 'npm', 'claude'),\n join(homedir(), 'AppData', 'Roaming', 'npm', 'claude.cmd'),\n ]\n : [\n join(homedir(), '.local', 'bin', 'claude'),\n join(homedir(), '.npm', 'bin', 'claude'),\n '/usr/local/bin/claude',\n '/opt/homebrew/bin/claude',\n ];\n\nexport function findClaudeBinary(): string | null {\n const environmentOverride = process.env['CLODEX_CLAUDE_PATH'];\n if (environmentOverride?.trim()) {\n return existsSync(environmentOverride) ? environmentOverride : null;\n }\n\n const override = getAppPathOverride('claude');\n if (override) return existsSync(override) ? override : null;\n\n return findBinaryOnPath('claude', FALLBACK_PATHS);\n}\n\nexport function getInstalledClaudeVersion(): string {\n try {\n const claudePath = findClaudeBinary();\n if (!claudePath) return '2.1.183';\n const result = execSync(`${isWindows ? `\"${claudePath}\"` : claudePath} --version`, {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n const match = result.match(/(\\d+\\.\\d+\\.\\d+)/);\n if (match) return match[1];\n } catch {\n // fallback\n }\n return '2.1.183'; // default fallback version known to work\n}\n\nexport function buildClaudeArgs(model: string | undefined, extraArgs: string[]): string[] {\n return model ? ['--model', model, ...extraArgs] : [...extraArgs];\n}\n\nexport function launchClaude(\n env: NodeJS.ProcessEnv,\n model: string | undefined,\n extraArgs: string[],\n): Promise<number> {\n return new Promise((resolve) => {\n const claudePath = findClaudeBinary()!;\n const args = buildClaudeArgs(model, extraArgs);\n\n const debugFileIdx = extraArgs.indexOf('--debug-file');\n const debugLogPath = debugFileIdx !== -1 && extraArgs[debugFileIdx + 1] ? extraArgs[debugFileIdx + 1] : undefined;\n\n const originalStdoutWrite = process.stdout.write;\n const originalStderrWrite = process.stderr.write;\n\n const muteWrite = (chunk: string | Uint8Array, encoding?: any, callback?: any) => {\n if (typeof encoding === 'function') {\n callback = encoding;\n }\n if (debugLogPath) {\n try {\n const str = typeof chunk === 'string' ? chunk : new TextDecoder().decode(chunk);\n appendFileSync(debugLogPath, `[parent] ${str}`);\n } catch {\n // ignore\n }\n }\n if (callback) callback();\n return true;\n };\n\n process.stdout.write = muteWrite as any;\n process.stderr.write = muteWrite as any;\n\n const restore = () => {\n process.stdout.write = originalStdoutWrite;\n process.stderr.write = originalStderrWrite;\n };\n\n const child = spawn(claudePath, args, {\n stdio: 'inherit',\n env,\n shell: isWindows,\n });\n\n const forward = (signal: NodeJS.Signals): void => {\n child.kill(signal);\n };\n\n process.once('SIGINT', () => forward('SIGINT'));\n process.once('SIGTERM', () => forward('SIGTERM'));\n\n child.on('exit', (code) => {\n restore();\n resolve(code ?? 0);\n });\n\n child.on('error', (err) => {\n restore();\n resolve(1);\n });\n });\n}\n","import { execFileSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\n\nexport interface FindBinaryOnPathOptions {\n verifyWhichResult?: boolean;\n isWindows?: boolean;\n exists?: (path: string) => boolean;\n runWhich?: (name: string, isWindows: boolean) => string;\n}\n\nexport function findBinaryOnPath(\n name: string,\n fallbackPaths: string[],\n options: FindBinaryOnPathOptions = {},\n): string | null {\n const isWindows = options.isWindows ?? process.platform === 'win32';\n const exists = options.exists ?? existsSync;\n // argv form, never a shell string — the binary name must not be shell-interpretable\n // (defense-in-depth originally added in d887984, must survive refactors).\n const runWhich = options.runWhich ?? ((binary, win) =>\n execFileSync(win ? 'where.exe' : 'which', [binary], {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }));\n\n try {\n const lines = runWhich(name, isWindows)\n .trim()\n .split('\\n')\n .map(line => line.trim())\n .filter(Boolean);\n const path = (isWindows ? lines.find(line => line.toLowerCase().endsWith('.cmd')) : null)\n ?? lines[0];\n if (path && (!options.verifyWhichResult || exists(path))) return path;\n } catch {\n // Fall through to fallback paths.\n }\n\n for (const path of fallbackPaths) {\n if (exists(path)) return path;\n }\n return null;\n}\n"],"mappings":"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","names":["mkdirSync","join","join","mkdirSync","dirname","mkdirSync","readFileSync","writeFileSync","readFileSync","mkdirSync","dirname","writeFileSync","keyring","existsSync","homedir","join","existsSync","isWindows","join","homedir","existsSync"]}
|