@bman654/clodex 1.0.4 → 1.2.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.
package/README.md CHANGED
@@ -14,6 +14,8 @@ You can also run clodex as a local OpenAI-compatible endpoint in front of your C
14
14
 
15
15
  > clodex is derived from the original [relay-ai](https://github.com/jacob-bd/relay-ai) project, heavily modified and streamlined for this one use case, with the full commit history preserved.
16
16
 
17
+ Contributions are welcome — see [CONTRIBUTING.md](./CONTRIBUTING.md) for how to scope a PR and what the quality bar is.
18
+
17
19
  ## Quick Start (ChatGPT/Codex plan)
18
20
 
19
21
  ```bash
@@ -212,7 +214,13 @@ clodex --version # version
212
214
  ## Configuration
213
215
 
214
216
  - Config home: `~/.clodex` (override with `CLODEX_HOME`). On first run, config is migrated automatically from a legacy `~/.relay-ai` directory if present; the legacy directory is never modified.
215
- - Credentials live in the OS credential store (Keychain / Windows Credential Manager / Secret Service) under the `clodex` service.
217
+ - The config-home filesystem must support hard links because registry locks are
218
+ published atomically. Keep `CLODEX_HOME` on a local filesystem rather than
219
+ FAT, exFAT, or a network mount that rejects hard links. An abrupt process kill
220
+ during lock publication can leave a `providers.json.lock.*.tmp` file; it does
221
+ not block later lock acquisition and can be removed when no Clodex process is
222
+ running.
223
+ - Credentials live in the OS credential store (Keychain / Windows Credential Manager / Secret Service) under the `clodex` service. Set `CLODEX_CREDENTIAL_HELPER` to an absolute executable path to use an external secure store instead; see [credential helpers](docs/credential-helpers.md).
216
224
  - `CLODEX_CLAUDE_PATH` overrides Claude Code binary discovery.
217
225
  - **Outbound proxy:** when `HTTP_PROXY`/`HTTPS_PROXY` (and optionally `NO_PROXY`) are set in clodex's environment, all clodex-originated network calls honor them — OAuth sign-in and token refresh, model-list and models.dev refreshes, upstream OpenAI API calls, and the ChatGPT/Codex OAuth WebSocket transport (tunneled via HTTP CONNECT).
218
226
 
@@ -13,7 +13,11 @@ import { constants as osConstants } from "os";
13
13
 
14
14
  // src/wrapper-env.ts
15
15
  var PROXY_ENV_VARS = ["HTTPS_PROXY", "HTTP_PROXY", "https_proxy", "http_proxy"];
16
+ var REQUIRE_SERVER_ENV = "CLODEX_REQUIRE_SERVER";
16
17
  var LOCAL_GATEWAY_API_KEY = "clodex-local";
18
+ function wrapperRequiresServer(env) {
19
+ return env[REQUIRE_SERVER_ENV] === "1";
20
+ }
17
21
  function computeWrapperEnv(baseEnv, state) {
18
22
  const env = { ...baseEnv };
19
23
  if (!state) return env;
@@ -55,16 +59,18 @@ function portIsOpen(port, timeoutMs = 100) {
55
59
  }
56
60
  async function main() {
57
61
  const argv = process.argv.slice(2);
58
- let claudePath;
59
- let claudeArgs;
60
- if (argv[0] && isExecutableFile(argv[0])) {
62
+ const checkOnly = argv[0] === "--check";
63
+ let claudePath = null;
64
+ let claudeArgs = [];
65
+ if (checkOnly) {
66
+ } else if (argv[0] && isExecutableFile(argv[0])) {
61
67
  claudePath = argv[0];
62
68
  claudeArgs = argv.slice(1);
63
69
  } else {
64
70
  claudePath = findClaudeBinary();
65
71
  claudeArgs = argv;
66
72
  }
67
- if (!claudePath) {
73
+ if (!checkOnly && !claudePath) {
68
74
  process.stderr.write("clodex-claude: could not find the claude binary (set CLODEX_CLAUDE_PATH)\n");
69
75
  process.exit(127);
70
76
  }
@@ -75,6 +81,11 @@ async function main() {
75
81
  break;
76
82
  }
77
83
  }
84
+ if (checkOnly) process.exit(state ? 0 : 1);
85
+ if (!state && wrapperRequiresServer(process.env)) {
86
+ process.stderr.write("clodex-claude: no live clodex server is available\n");
87
+ process.exit(1);
88
+ }
78
89
  const env = computeWrapperEnv(process.env, state);
79
90
  const child = spawn(claudePath, claudeArgs, {
80
91
  stdio: "inherit",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/claude-wrapper.ts","../src/wrapper-env.ts"],"sourcesContent":["// src/claude-wrapper.ts — the `clodex-claude` bin.\n//\n// A tiny, fast exec-style wrapper around the Claude Code binary that injects\n// bridge env for a running standalone `clodex server` (discovered via\n// ~/.clodex/server-runtime.json). Two invocation shapes:\n//\n// 1. CLAUDE_CODE_PROCESS_WRAPPER contract: Claude Code invokes\n// `clodex-claude <claude-binary-path> <args...>` for every process it\n// spawns (agents view sessions, background agents). First arg is the\n// claude binary to exec.\n// 2. Direct terminal use: `clodex-claude [args...]` — the claude binary is\n// discovered the same way `clodex claude` discovers it\n// (CLODEX_CLAUDE_PATH override, config override, PATH, fallbacks).\n//\n// With a live proxy-mode server: HTTPS_PROXY/HTTP_PROXY + NODE_EXTRA_CA_CERTS\n// point at it and ANTHROPIC_BASE_URL is removed (claude keeps its own\n// Anthropic auth — this is the recommended mode). With a live endpoint-mode\n// server: ANTHROPIC_BASE_URL points at the gateway. With no live server the\n// env is passed through untouched, so claude always launches.\n//\n// This file must stay a thin shell over pure helpers (wrapper-env.ts,\n// server-runtime.ts) with minimal imports — it runs for every spawned agent.\n\nimport { spawn } from 'node:child_process';\nimport { accessSync, constants as fsConstants, statSync } from 'node:fs';\nimport { connect } from 'node:net';\nimport { constants as osConstants } from 'node:os';\nimport {\n orderWrapperServerCandidates,\n readLiveServerRuntimeStates,\n type ServerRuntimeState,\n} from './server-runtime.js';\nimport { computeWrapperEnv } from './wrapper-env.js';\nimport { findClaudeBinary } from './launch.js';\n\nconst isWindows = process.platform === 'win32';\n\nfunction isExecutableFile(path: string): boolean {\n try {\n if (!statSync(path).isFile()) return false;\n if (!isWindows) accessSync(path, fsConstants.X_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Fast TCP probe — the state file can outlive a SIGKILLed listener. Never hangs. */\nfunction portIsOpen(port: number, timeoutMs = 100): Promise<boolean> {\n return new Promise(resolve => {\n const socket = connect({ host: '127.0.0.1', port });\n const done = (result: boolean) => {\n socket.destroy();\n resolve(result);\n };\n socket.setTimeout(timeoutMs, () => done(false));\n socket.once('connect', () => done(true));\n socket.once('error', () => done(false));\n });\n}\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2);\n\n let claudePath: string | null;\n let claudeArgs: string[];\n if (argv[0] && isExecutableFile(argv[0])) {\n // CLAUDE_CODE_PROCESS_WRAPPER shape: first arg is the claude binary path.\n claudePath = argv[0];\n claudeArgs = argv.slice(1);\n } else {\n claudePath = findClaudeBinary();\n claudeArgs = argv;\n }\n\n if (!claudePath) {\n process.stderr.write('clodex-claude: could not find the claude binary (set CLODEX_CLAUDE_PATH)\\n');\n process.exit(127);\n }\n\n // Selection policy (see orderWrapperServerCandidates): proxy-mode servers\n // are preferred over endpoint-mode ones — bridging keeps Claude Code's own\n // Anthropic auth — with newest startedAt breaking ties within a mode. The\n // first candidate whose port answers the TCP probe wins; if only an\n // endpoint server is live it is used; with none, claude launches untouched.\n let state: ServerRuntimeState | null = null;\n for (const candidate of orderWrapperServerCandidates(readLiveServerRuntimeStates())) {\n if (await portIsOpen(candidate.port)) {\n state = candidate;\n break;\n }\n }\n const env = computeWrapperEnv(process.env, state);\n\n const child = spawn(claudePath, claudeArgs, {\n stdio: 'inherit',\n env,\n shell: isWindows,\n });\n\n const forward = (signal: NodeJS.Signals) => child.kill(signal);\n process.once('SIGINT', () => forward('SIGINT'));\n process.once('SIGTERM', () => forward('SIGTERM'));\n\n child.on('error', err => {\n process.stderr.write(`clodex-claude: failed to launch ${claudePath}: ${err.message}\\n`);\n process.exit(127);\n });\n child.on('exit', (code, signal) => {\n if (signal) {\n const signum = osConstants.signals[signal as keyof typeof osConstants.signals];\n process.exit(signum ? 128 + signum : 1);\n }\n process.exit(code ?? 0);\n });\n}\n\nvoid main();\n","// src/wrapper-env.ts\n//\n// Pure env computation for the `clodex-claude` wrapper bin. Given the process\n// env and a live `clodex server` runtime state (or null), returns the env to\n// launch the Claude Code binary with. Kept dependency-free so the wrapper\n// stays tiny and fast — it runs for every Claude-Code-spawned agent process.\n\nimport type { ServerRuntimeState } from './server-runtime.js';\n\nconst PROXY_ENV_VARS = ['HTTPS_PROXY', 'HTTP_PROXY', 'https_proxy', 'http_proxy'] as const;\n\n/**\n * Any non-empty key satisfies the local endpoint gateway (`isAuthorized`\n * accepts everything when no server password is set, i.e. local listen mode).\n */\nexport const LOCAL_GATEWAY_API_KEY = 'clodex-local';\n\nexport function computeWrapperEnv(\n baseEnv: NodeJS.ProcessEnv,\n state: ServerRuntimeState | null,\n): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = { ...baseEnv };\n // No live server: launch claude completely untouched — a down server must\n // never break launching claude.\n if (!state) return env;\n\n if (state.mode === 'proxy') {\n // Selective MITM: claude keeps its own Anthropic credentials; the proxy\n // routes clodex:/alias models to OpenAI and passes everything else through.\n const proxyUrl = `http://127.0.0.1:${state.port}`;\n delete env['ANTHROPIC_BASE_URL'];\n for (const name of PROXY_ENV_VARS) env[name] = proxyUrl;\n if (state.caPath) env['NODE_EXTRA_CA_CERTS'] = state.caPath;\n return env;\n }\n\n // Endpoint gateway: all traffic goes to the local Anthropic-format gateway.\n for (const name of PROXY_ENV_VARS) delete env[name];\n env['ANTHROPIC_BASE_URL'] = `http://127.0.0.1:${state.port}/anthropic`;\n env['ANTHROPIC_API_KEY'] = LOCAL_GATEWAY_API_KEY;\n return env;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/claude-wrapper.ts","../src/wrapper-env.ts"],"sourcesContent":["// src/claude-wrapper.ts — the `clodex-claude` bin.\n//\n// A tiny, fast exec-style wrapper around the Claude Code binary that injects\n// bridge env for a running standalone `clodex server` (discovered via\n// ~/.clodex/server-runtime.json). Two invocation shapes:\n//\n// 1. CLAUDE_CODE_PROCESS_WRAPPER contract: Claude Code invokes\n// `clodex-claude <claude-binary-path> <args...>` for every process it\n// spawns (agents view sessions, background agents). First arg is the\n// claude binary to exec.\n// 2. Direct terminal use: `clodex-claude [args...]` — the claude binary is\n// discovered the same way `clodex claude` discovers it\n// (CLODEX_CLAUDE_PATH override, config override, PATH, fallbacks).\n//\n// With a live proxy-mode server: HTTPS_PROXY/HTTP_PROXY + NODE_EXTRA_CA_CERTS\n// point at it and ANTHROPIC_BASE_URL is removed (claude keeps its own\n// Anthropic auth — this is the recommended mode). With a live endpoint-mode\n// server: ANTHROPIC_BASE_URL points at the gateway. With no live server the\n// env is passed through untouched, so claude always launches.\n//\n// This file must stay a thin shell over pure helpers (wrapper-env.ts,\n// server-runtime.ts) with minimal imports — it runs for every spawned agent.\n\nimport { spawn } from 'node:child_process';\nimport { accessSync, constants as fsConstants, statSync } from 'node:fs';\nimport { connect } from 'node:net';\nimport { constants as osConstants } from 'node:os';\nimport {\n orderWrapperServerCandidates,\n readLiveServerRuntimeStates,\n type ServerRuntimeState,\n} from './server-runtime.js';\nimport { computeWrapperEnv, wrapperRequiresServer } from './wrapper-env.js';\nimport { findClaudeBinary } from './launch.js';\n\nconst isWindows = process.platform === 'win32';\n\nfunction isExecutableFile(path: string): boolean {\n try {\n if (!statSync(path).isFile()) return false;\n if (!isWindows) accessSync(path, fsConstants.X_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Fast TCP probe — the state file can outlive a SIGKILLed listener. Never hangs. */\nfunction portIsOpen(port: number, timeoutMs = 100): Promise<boolean> {\n return new Promise(resolve => {\n const socket = connect({ host: '127.0.0.1', port });\n const done = (result: boolean) => {\n socket.destroy();\n resolve(result);\n };\n socket.setTimeout(timeoutMs, () => done(false));\n socket.once('connect', () => done(true));\n socket.once('error', () => done(false));\n });\n}\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2);\n const checkOnly = argv[0] === '--check';\n\n let claudePath: string | null = null;\n let claudeArgs: string[] = [];\n if (checkOnly) {\n // Readiness checks validate discovery and TCP state without launching Claude.\n } else if (argv[0] && isExecutableFile(argv[0])) {\n // CLAUDE_CODE_PROCESS_WRAPPER shape: first arg is the claude binary path.\n claudePath = argv[0];\n claudeArgs = argv.slice(1);\n } else {\n claudePath = findClaudeBinary();\n claudeArgs = argv;\n }\n\n if (!checkOnly && !claudePath) {\n process.stderr.write('clodex-claude: could not find the claude binary (set CLODEX_CLAUDE_PATH)\\n');\n process.exit(127);\n }\n\n // Selection policy (see orderWrapperServerCandidates): proxy-mode servers\n // are preferred over endpoint-mode ones — bridging keeps Claude Code's own\n // Anthropic auth — with newest startedAt breaking ties within a mode. The\n // first candidate whose port answers the TCP probe wins; if only an\n // endpoint server is live it is used; with none, claude launches untouched.\n let state: ServerRuntimeState | null = null;\n for (const candidate of orderWrapperServerCandidates(readLiveServerRuntimeStates())) {\n if (await portIsOpen(candidate.port)) {\n state = candidate;\n break;\n }\n }\n if (checkOnly) process.exit(state ? 0 : 1);\n if (!state && wrapperRequiresServer(process.env)) {\n process.stderr.write('clodex-claude: no live clodex server is available\\n');\n process.exit(1);\n }\n const env = computeWrapperEnv(process.env, state);\n\n const child = spawn(claudePath!, claudeArgs, {\n stdio: 'inherit',\n env,\n shell: isWindows,\n });\n\n const forward = (signal: NodeJS.Signals) => child.kill(signal);\n process.once('SIGINT', () => forward('SIGINT'));\n process.once('SIGTERM', () => forward('SIGTERM'));\n\n child.on('error', err => {\n process.stderr.write(`clodex-claude: failed to launch ${claudePath}: ${err.message}\\n`);\n process.exit(127);\n });\n child.on('exit', (code, signal) => {\n if (signal) {\n const signum = osConstants.signals[signal as keyof typeof osConstants.signals];\n process.exit(signum ? 128 + signum : 1);\n }\n process.exit(code ?? 0);\n });\n}\n\nvoid main();\n","// src/wrapper-env.ts\n//\n// Pure env computation for the `clodex-claude` wrapper bin. Given the process\n// env and a live `clodex server` runtime state (or null), returns the env to\n// launch the Claude Code binary with. Kept dependency-free so the wrapper\n// stays tiny and fast — it runs for every Claude-Code-spawned agent process.\n\nimport type { ServerRuntimeState } from './server-runtime.js';\n\nconst PROXY_ENV_VARS = ['HTTPS_PROXY', 'HTTP_PROXY', 'https_proxy', 'http_proxy'] as const;\nexport const REQUIRE_SERVER_ENV = 'CLODEX_REQUIRE_SERVER';\n\n/**\n * Any non-empty key satisfies the local endpoint gateway (`isAuthorized`\n * accepts everything when no server password is set, i.e. local listen mode).\n */\nexport const LOCAL_GATEWAY_API_KEY = 'clodex-local';\n\nexport function wrapperRequiresServer(env: NodeJS.ProcessEnv): boolean {\n return env[REQUIRE_SERVER_ENV] === '1';\n}\n\nexport function computeWrapperEnv(\n baseEnv: NodeJS.ProcessEnv,\n state: ServerRuntimeState | null,\n): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = { ...baseEnv };\n // No live server: launch claude completely untouched — a down server must\n // never break launching claude.\n if (!state) return env;\n\n if (state.mode === 'proxy') {\n // Selective MITM: claude keeps its own Anthropic credentials; the proxy\n // routes clodex:/alias models to OpenAI and passes everything else through.\n const proxyUrl = `http://127.0.0.1:${state.port}`;\n delete env['ANTHROPIC_BASE_URL'];\n for (const name of PROXY_ENV_VARS) env[name] = proxyUrl;\n if (state.caPath) env['NODE_EXTRA_CA_CERTS'] = state.caPath;\n return env;\n }\n\n // Endpoint gateway: all traffic goes to the local Anthropic-format gateway.\n for (const name of PROXY_ENV_VARS) delete env[name];\n env['ANTHROPIC_BASE_URL'] = `http://127.0.0.1:${state.port}/anthropic`;\n env['ANTHROPIC_API_KEY'] = LOCAL_GATEWAY_API_KEY;\n return env;\n}\n"],"mappings":"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","names":[]}