@jcanizalez7/clauxy 0.1.9 → 0.2.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.
Files changed (3) hide show
  1. package/README.md +39 -25
  2. package/dist/cli.js +344 -145
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -29,14 +29,19 @@ No API keys needed. Just authenticate once with OAuth and you're ready to go.
29
29
  npm install -g @jcanizalez7/clauxy
30
30
  ```
31
31
 
32
+ Also requires [Claude Code](https://docs.anthropic.com/en/docs/claude-code):
33
+ ```bash
34
+ npm install -g @anthropic-ai/claude-code
35
+ ```
36
+
32
37
  ---
33
38
 
34
39
  ## Quick Start
35
40
 
36
- ### 1. Start the proxy
41
+ Just run:
37
42
 
38
43
  ```bash
39
- clauxy run
44
+ clauxy
40
45
  ```
41
46
 
42
47
  First time? Select your provider and authenticate:
@@ -51,35 +56,45 @@ First time? Select your provider and authenticate:
51
56
 
52
57
  ```
53
58
 
54
- ### 2. Run Claude Code
55
-
56
- In a new terminal:
57
-
58
- ```bash
59
- export ANTHROPIC_BASE_URL="http://localhost:3000"
60
- export ANTHROPIC_API_KEY="clauxy"
61
- claude
62
- ```
63
-
64
- That's it! Claude Code now uses your selected provider.
65
-
66
- > **Tip:** Add the exports to your `~/.zshrc` or `~/.bashrc` to make them permanent.
59
+ That's it! Clauxy starts the proxy and launches Claude Code automatically with the correct environment variables.
67
60
 
68
61
  ---
69
62
 
70
63
  ## Commands
71
64
 
72
65
  ```bash
73
- clauxy run # Start the proxy
74
- clauxy run -p copilot # Start with a specific provider
75
- clauxy run -c # Switch provider or re-authenticate
76
- clauxy run -p copilot -c # Re-authenticate with a specific provider
77
- clauxy run --port 3001 # Use a different port
66
+ # Default: Start proxy + launch Claude Code
67
+ clauxy # Just works!
68
+ clauxy "fix the bug" # Pass arguments to Claude
69
+ clauxy -p copilot # Use specific provider
70
+ clauxy -c # Re-authenticate before starting
71
+ clauxy --port 3000 # Use specific port (reuses existing proxy if running)
72
+
73
+ # Proxy-only mode (for advanced users)
74
+ clauxy run # Start proxy without launching Claude
75
+ clauxy run --port 3001 # Proxy on specific port
76
+ clauxy run -p chatgpt # Proxy with specific provider
77
+
78
+ # Configuration
78
79
  clauxy model # Configure model mappings
79
80
  clauxy auth list # Show configured providers
80
81
  ```
81
82
 
82
- **Provider flag:** Use `-p` or `--provider` to start directly with a specific provider (`copilot`, `chatgpt`, or `google`). If credentials exist, it starts immediately. Otherwise, it authenticates with that provider.
83
+ ### Port Reuse
84
+
85
+ When you specify `--port`, Clauxy checks if a proxy is already running on that port:
86
+ - If yes, it reuses the existing proxy (no new instance started)
87
+ - If no, it starts a new proxy on that port
88
+
89
+ This lets you run multiple Claude Code sessions sharing a single proxy:
90
+
91
+ ```bash
92
+ # Terminal 1
93
+ clauxy --port 3000 # Starts proxy + Claude
94
+
95
+ # Terminal 2
96
+ clauxy --port 3000 # Reuses proxy, launches another Claude
97
+ ```
83
98
 
84
99
  ---
85
100
 
@@ -96,7 +111,7 @@ Claude Code requests are automatically mapped to your provider's models:
96
111
  Customize with `clauxy model` or environment variables:
97
112
 
98
113
  ```bash
99
- CLAUXY_MODEL_BIG=gpt-4o CLAUXY_MODEL_MID=gpt-4o clauxy run
114
+ CLAUXY_MODEL_BIG=gpt-4o CLAUXY_MODEL_MID=gpt-4o clauxy
100
115
  ```
101
116
 
102
117
  ---
@@ -123,14 +138,13 @@ Credentials are stored locally in `~/.clauxy/` with restricted permissions.
123
138
 
124
139
  **"Token refresh failed"** or **"Not authenticated"**
125
140
  ```bash
126
- clauxy run -c
141
+ clauxy -c
127
142
  ```
128
143
 
129
144
  **Port already in use**
130
145
  ```bash
131
- clauxy run --port 3001
146
+ clauxy --port 3001
132
147
  ```
133
- Then update `ANTHROPIC_BASE_URL`.
134
148
 
135
149
  **Reset everything**
136
150
  ```bash
package/dist/cli.js CHANGED
@@ -2041,84 +2041,18 @@ var init_converter_gemini = __esm(() => {
2041
2041
 
2042
2042
  // proxy.ts
2043
2043
  var exports_proxy = {};
2044
- async function isPortAvailable(port) {
2045
- try {
2046
- const server = Bun.serve({ port, fetch: () => new Response("") });
2047
- server.stop();
2048
- return true;
2049
- } catch {
2050
- return false;
2051
- }
2052
- }
2053
- async function findAvailablePort() {
2054
- if (process.env.PORT) {
2055
- const port = parseInt(process.env.PORT, 10);
2056
- if (await isPortAvailable(port)) {
2057
- return port;
2058
- }
2059
- log.error(`Port ${port} is in use. Try a different port with --port <port>`);
2060
- process.exit(1);
2061
- }
2062
- for (const port of PORT_RANGE) {
2063
- if (await isPortAvailable(port)) {
2064
- return port;
2065
- }
2066
- }
2067
- log.error(`All ports (${PORT_RANGE.join(", ")}) are in use.`);
2068
- log.error(`Try: clauxy run --port <port>`);
2069
- process.exit(1);
2070
- }
2071
- async function loadProvider() {
2072
- const providerId = process.env.CLAUXY_PROVIDER || await Config.getActiveProvider();
2073
- if (!providerId) {
2074
- log.error("No provider configured. Run 'clauxy run' to set up.");
2075
- process.exit(1);
2076
- }
2077
- return await getProvider(providerId);
2078
- }
2079
- function convertAnthropicToOpenAI(anthropicReq) {
2080
- if (currentLogLevel === "debug") {
2081
- for (let i = 0;i < anthropicReq.messages.length; i++) {
2082
- const msg = anthropicReq.messages[i];
2083
- if (Array.isArray(msg.content)) {
2084
- const toolUses = msg.content.filter((b) => b.type === "tool_use").map((b) => b.id);
2085
- const toolResults = msg.content.filter((b) => b.type === "tool_result").map((b) => b.tool_use_id);
2086
- if (toolUses.length > 0)
2087
- log.debug(`msg[${i}] ${msg.role}: tool_use ids: ${toolUses.join(", ")}`);
2088
- if (toolResults.length > 0)
2089
- log.debug(`msg[${i}] ${msg.role}: tool_result ids: ${toolResults.join(", ")}`);
2090
- }
2091
- }
2092
- }
2093
- const messages = convertMessages(anthropicReq.messages, anthropicReq.system);
2094
- const tools = convertTools(anthropicReq.tools);
2095
- const mappedModel = provider.mapModel(anthropicReq.model);
2096
- log.info(`Model: ${anthropicReq.model} -> ${mappedModel}`);
2044
+ __export(exports_proxy, {
2045
+ startProxy: () => startProxy
2046
+ });
2047
+ function createLogger(level) {
2097
2048
  return {
2098
- model: mappedModel,
2099
- messages,
2100
- max_tokens: anthropicReq.max_tokens,
2101
- temperature: anthropicReq.temperature,
2102
- stream: anthropicReq.stream ?? false,
2103
- ...tools && { tools }
2104
- };
2105
- }
2106
- var LOG_LEVELS, currentLogLevel, log, DEFAULT_PORT, PORT_RANGE, provider, convertOpenAIToAnthropic, token, models, PORT, server, c, wireApi, wireApiLabel;
2107
- var init_proxy = __esm(async () => {
2108
- init_providers();
2109
- init_config();
2110
- init_converter();
2111
- init_converter_gemini();
2112
- LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
2113
- currentLogLevel = process.env.CLAUXY_LOG_LEVEL || "info";
2114
- log = {
2115
- _shouldLog(level) {
2116
- return LOG_LEVELS[level] >= LOG_LEVELS[currentLogLevel];
2049
+ _shouldLog(checkLevel) {
2050
+ return LOG_LEVELS[checkLevel] >= LOG_LEVELS[level];
2117
2051
  },
2118
- _format(level, msg) {
2052
+ _format(levelStr, msg) {
2119
2053
  const now = new Date;
2120
2054
  const ts = now.toISOString().replace("T", " ").substring(0, 19);
2121
- return `${ts} ${level.padEnd(5)} ${msg}`;
2055
+ return `${ts} ${levelStr.padEnd(5)} ${msg}`;
2122
2056
  },
2123
2057
  info(msg) {
2124
2058
  if (this._shouldLog("info")) {
@@ -2146,12 +2080,73 @@ var init_proxy = __esm(async () => {
2146
2080
  }
2147
2081
  }
2148
2082
  };
2149
- DEFAULT_PORT = parseInt(process.env.PORT || "3000", 10);
2150
- PORT_RANGE = [3000, 3001, 3002, 3003, 3004, 3005];
2151
- provider = await loadProvider();
2152
- convertOpenAIToAnthropic = convertResponse;
2083
+ }
2084
+ async function isPortAvailable(port) {
2085
+ try {
2086
+ const server = Bun.serve({ port, fetch: () => new Response("") });
2087
+ server.stop();
2088
+ return true;
2089
+ } catch {
2090
+ return false;
2091
+ }
2092
+ }
2093
+ async function findAvailablePort(requestedPort) {
2094
+ if (requestedPort) {
2095
+ if (await isPortAvailable(requestedPort)) {
2096
+ return requestedPort;
2097
+ }
2098
+ }
2099
+ for (const port2 of PORT_RANGE) {
2100
+ if (await isPortAvailable(port2)) {
2101
+ return port2;
2102
+ }
2103
+ }
2104
+ const server = Bun.serve({ port: 0, fetch: () => new Response("") });
2105
+ const port = server.port;
2106
+ server.stop();
2107
+ return port;
2108
+ }
2109
+ async function loadProvider(providerId) {
2110
+ const id = providerId || process.env.CLAUXY_PROVIDER || await Config.getActiveProvider();
2111
+ if (!id) {
2112
+ throw new Error("No provider configured. Run 'clauxy' to set up.");
2113
+ }
2114
+ return await getProvider(id);
2115
+ }
2116
+ async function startProxy(options = {}) {
2117
+ const { port: requestedPort, logLevel = "info", providerId, quiet = false } = options;
2118
+ const log = createLogger(logLevel);
2119
+ const provider = await loadProvider(providerId);
2120
+ function convertAnthropicToOpenAI(anthropicReq) {
2121
+ if (logLevel === "debug") {
2122
+ for (let i = 0;i < anthropicReq.messages.length; i++) {
2123
+ const msg = anthropicReq.messages[i];
2124
+ if (Array.isArray(msg.content)) {
2125
+ const toolUses = msg.content.filter((b) => b.type === "tool_use").map((b) => b.id);
2126
+ const toolResults = msg.content.filter((b) => b.type === "tool_result").map((b) => b.tool_use_id);
2127
+ if (toolUses.length > 0)
2128
+ log.debug(`msg[${i}] ${msg.role}: tool_use ids: ${toolUses.join(", ")}`);
2129
+ if (toolResults.length > 0)
2130
+ log.debug(`msg[${i}] ${msg.role}: tool_result ids: ${toolResults.join(", ")}`);
2131
+ }
2132
+ }
2133
+ }
2134
+ const messages = convertMessages(anthropicReq.messages, anthropicReq.system);
2135
+ const tools = convertTools(anthropicReq.tools);
2136
+ const mappedModel = provider.mapModel(anthropicReq.model);
2137
+ log.info(`Model: ${anthropicReq.model} -> ${mappedModel}`);
2138
+ return {
2139
+ model: mappedModel,
2140
+ messages,
2141
+ max_tokens: anthropicReq.max_tokens,
2142
+ temperature: anthropicReq.temperature,
2143
+ stream: anthropicReq.stream ?? false,
2144
+ ...tools && { tools }
2145
+ };
2146
+ }
2147
+ const convertOpenAIToAnthropic = convertResponse;
2153
2148
  log.info(`Authenticating with ${provider.info.name}...`);
2154
- token = await provider.getToken();
2149
+ const token = await provider.getToken();
2155
2150
  log.info("\x1B[32m+\x1B[0m Authentication successful");
2156
2151
  if (provider.loadCodeAssist) {
2157
2152
  log.info("Initializing Cloud Code session...");
@@ -2162,15 +2157,17 @@ var init_proxy = __esm(async () => {
2162
2157
  log.warn("\x1B[33m!\x1B[0m Could not get Cloud Code project ID - requests may fail");
2163
2158
  }
2164
2159
  }
2165
- models = { big: "default", mid: "default", small: "default" };
2160
+ let models = { big: "default", mid: "default", small: "default" };
2166
2161
  if (provider.loadModels) {
2167
2162
  models = await provider.loadModels();
2168
2163
  }
2169
- PORT = await findAvailablePort();
2170
- if (PORT !== DEFAULT_PORT && !process.env.PORT) {
2164
+ const PORT = await findAvailablePort(requestedPort);
2165
+ if (requestedPort && PORT !== requestedPort) {
2166
+ log.warn(`Port ${requestedPort} in use, using port ${PORT} instead`);
2167
+ } else if (!requestedPort && PORT !== DEFAULT_PORT) {
2171
2168
  log.warn(`Port 3000 in use, using port ${PORT} instead`);
2172
2169
  }
2173
- server = Bun.serve({
2170
+ const server = Bun.serve({
2174
2171
  port: PORT,
2175
2172
  async fetch(req) {
2176
2173
  const url = new URL(req.url);
@@ -2365,6 +2362,47 @@ var init_proxy = __esm(async () => {
2365
2362
  }
2366
2363
  }
2367
2364
  });
2365
+ if (!quiet) {
2366
+ const wireApi = provider.info.wireApi || "chat";
2367
+ const wireApiLabel = wireApi === "gemini" ? "Gemini API" : wireApi === "responses" ? "Responses API" : "Chat Completions";
2368
+ log.raw(c.dim("=".repeat(60)));
2369
+ log.raw(`${c.green(">")} ${c.bold("Clauxy")} proxy ready on ${c.cyan(`http://localhost:${PORT}`)}`);
2370
+ log.raw(`${c.green(">")} Provider: ${c.cyan(provider.info.name)}`);
2371
+ log.raw(`${c.green(">")} Wire API: ${c.cyan(wireApiLabel)}`);
2372
+ log.raw(c.dim("=".repeat(60)));
2373
+ log.raw(`
2374
+ ${c.yellow("Model Mapping")} (Claude -> Provider):`);
2375
+ log.raw(` opus/big -> ${c.cyan(models.big)}`);
2376
+ log.raw(` sonnet/mid -> ${c.cyan(models.mid)}`);
2377
+ log.raw(` haiku/small -> ${c.cyan(models.small)}`);
2378
+ log.raw(`
2379
+ ${c.yellow("To change models:")}`);
2380
+ log.raw(c.dim(" CLAUXY_MODEL_BIG=<model> CLAUXY_MODEL_MID=<model> CLAUXY_MODEL_SMALL=<model>"));
2381
+ log.raw(c.dim(" Or run: clauxy model"));
2382
+ log.raw(c.dim("=".repeat(60)));
2383
+ log.raw(`
2384
+ ${c.yellow("Run in a NEW terminal:")}
2385
+ `);
2386
+ log.raw(` ${c.green("export")} ANTHROPIC_BASE_URL=${c.cyan(`"http://localhost:${PORT}"`)}`);
2387
+ log.raw(` ${c.green("export")} ANTHROPIC_API_KEY=${c.cyan('"clauxy"')}`);
2388
+ log.raw(` ${c.bold("claude")}
2389
+ `);
2390
+ log.raw(c.dim("=".repeat(60)));
2391
+ }
2392
+ return {
2393
+ server,
2394
+ port: PORT,
2395
+ stop: () => server.stop()
2396
+ };
2397
+ }
2398
+ var LOG_LEVELS, DEFAULT_PORT = 3000, PORT_RANGE, c;
2399
+ var init_proxy = __esm(async () => {
2400
+ init_providers();
2401
+ init_config();
2402
+ init_converter();
2403
+ init_converter_gemini();
2404
+ LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
2405
+ PORT_RANGE = [3000, 3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, 3009];
2368
2406
  c = {
2369
2407
  green: (s) => `\x1B[32m${s}\x1B[0m`,
2370
2408
  cyan: (s) => `\x1B[36m${s}\x1B[0m`,
@@ -2372,31 +2410,7 @@ var init_proxy = __esm(async () => {
2372
2410
  dim: (s) => `\x1B[2m${s}\x1B[0m`,
2373
2411
  bold: (s) => `\x1B[1m${s}\x1B[0m`
2374
2412
  };
2375
- wireApi = provider.info.wireApi || "chat";
2376
- wireApiLabel = wireApi === "gemini" ? "Gemini API" : wireApi === "responses" ? "Responses API" : "Chat Completions";
2377
- log.raw(c.dim("=".repeat(60)));
2378
- log.raw(`${c.green(">")} ${c.bold("Clauxy")} proxy ready on ${c.cyan(`http://localhost:${PORT}`)}`);
2379
- log.raw(`${c.green(">")} Provider: ${c.cyan(provider.info.name)}`);
2380
- log.raw(`${c.green(">")} Wire API: ${c.cyan(wireApiLabel)}`);
2381
- log.raw(c.dim("=".repeat(60)));
2382
- log.raw(`
2383
- ${c.yellow("Model Mapping")} (Claude -> Provider):`);
2384
- log.raw(` opus/big -> ${c.cyan(models.big)}`);
2385
- log.raw(` sonnet/mid -> ${c.cyan(models.mid)}`);
2386
- log.raw(` haiku/small -> ${c.cyan(models.small)}`);
2387
- log.raw(`
2388
- ${c.yellow("To change models:")}`);
2389
- log.raw(c.dim(" CLAUXY_MODEL_BIG=<model> CLAUXY_MODEL_MID=<model> CLAUXY_MODEL_SMALL=<model>"));
2390
- log.raw(c.dim(" Or run: clauxy model"));
2391
- log.raw(c.dim("=".repeat(60)));
2392
- log.raw(`
2393
- ${c.yellow("Run in a NEW terminal:")}
2394
- `);
2395
- log.raw(` ${c.green("export")} ANTHROPIC_BASE_URL=${c.cyan(`"http://localhost:${PORT}"`)}`);
2396
- log.raw(` ${c.green("export")} ANTHROPIC_API_KEY=${c.cyan('"clauxy"')}`);
2397
- log.raw(` ${c.bold("claude")}
2398
- `);
2399
- log.raw(c.dim("=".repeat(60)));
2413
+ if (false) {}
2400
2414
  });
2401
2415
 
2402
2416
  // cli/index.ts
@@ -2441,23 +2455,21 @@ async function selectAndAuthenticate() {
2441
2455
  return authenticateProvider(providerId);
2442
2456
  }
2443
2457
  async function authenticateProvider(providerId) {
2444
- const provider2 = await getProvider(providerId);
2445
- prompts2.intro(`Clauxy - Connecting to ${provider2.info.name}`);
2446
- const result = await provider2.authenticate();
2458
+ const provider = await getProvider(providerId);
2459
+ prompts2.intro(`Clauxy - Connecting to ${provider.info.name}`);
2460
+ const result = await provider.authenticate();
2447
2461
  if (result.type === "failed") {
2448
2462
  prompts2.cancel(`Authentication failed: ${result.error}`);
2449
2463
  process.exit(1);
2450
2464
  }
2451
2465
  await Config.setActiveProvider(providerId);
2452
- prompts2.outro(`Connected to ${provider2.info.name}`);
2466
+ prompts2.outro(`Connected to ${provider.info.name}`);
2453
2467
  return providerId;
2454
2468
  }
2455
2469
  async function runCommand(options) {
2456
- const { port, logLevel, connect, provider: provider2 } = options;
2457
- process.env.PORT = port.toString();
2458
- process.env.CLAUXY_LOG_LEVEL = logLevel;
2470
+ const { port, logLevel, connect, provider } = options;
2459
2471
  const availableProviders = getProviderList().map((item) => item.id);
2460
- const providerOverride = provider2?.toLowerCase();
2472
+ const providerOverride = provider?.toLowerCase();
2461
2473
  if (providerOverride && !availableProviders.includes(providerOverride)) {
2462
2474
  console.error(`Invalid provider: "${providerOverride}". Valid values: ${availableProviders.join(" | ")}.`);
2463
2475
  process.exit(1);
@@ -2482,8 +2494,158 @@ async function runCommand(options) {
2482
2494
  }
2483
2495
  }
2484
2496
  await Config.setActiveProvider(activeProvider);
2485
- process.env.CLAUXY_PROVIDER = activeProvider;
2486
- await init_proxy().then(() => exports_proxy);
2497
+ const { startProxy: startProxy2 } = await init_proxy().then(() => exports_proxy);
2498
+ await startProxy2({
2499
+ port,
2500
+ logLevel,
2501
+ providerId: activeProvider
2502
+ });
2503
+ await new Promise(() => {});
2504
+ }
2505
+
2506
+ // cli/commands/wrapper.ts
2507
+ init_auth();
2508
+ init_config();
2509
+ init_providers();
2510
+ import * as prompts3 from "@clack/prompts";
2511
+ var c2 = {
2512
+ green: (s) => `\x1B[32m${s}\x1B[0m`,
2513
+ cyan: (s) => `\x1B[36m${s}\x1B[0m`,
2514
+ yellow: (s) => `\x1B[33m${s}\x1B[0m`,
2515
+ dim: (s) => `\x1B[2m${s}\x1B[0m`,
2516
+ bold: (s) => `\x1B[1m${s}\x1B[0m`,
2517
+ red: (s) => `\x1B[31m${s}\x1B[0m`
2518
+ };
2519
+ async function findClaudeCli() {
2520
+ try {
2521
+ const proc = Bun.spawn(["which", "claude"], {
2522
+ stdout: "pipe",
2523
+ stderr: "pipe"
2524
+ });
2525
+ const exitCode = await proc.exited;
2526
+ if (exitCode === 0) {
2527
+ const output = await new Response(proc.stdout).text();
2528
+ return output.trim();
2529
+ }
2530
+ return null;
2531
+ } catch {
2532
+ return null;
2533
+ }
2534
+ }
2535
+ async function selectAndAuthenticate2() {
2536
+ prompts3.intro("Clauxy - Connect to AI Provider");
2537
+ const providers = getProviderList();
2538
+ const providerChoice = await prompts3.select({
2539
+ message: "Select your AI provider",
2540
+ options: providers.map((p) => ({
2541
+ label: p.name,
2542
+ value: p.id,
2543
+ hint: p.description
2544
+ }))
2545
+ });
2546
+ const providerId = handleCancel(providerChoice, "Setup cancelled");
2547
+ return authenticateProvider2(providerId);
2548
+ }
2549
+ async function authenticateProvider2(providerId) {
2550
+ const provider = await getProvider(providerId);
2551
+ prompts3.intro(`Clauxy - Connecting to ${provider.info.name}`);
2552
+ const result = await provider.authenticate();
2553
+ if (result.type === "failed") {
2554
+ prompts3.cancel(`Authentication failed: ${result.error}`);
2555
+ process.exit(1);
2556
+ }
2557
+ await Config.setActiveProvider(providerId);
2558
+ prompts3.outro(`Connected to ${provider.info.name}`);
2559
+ return providerId;
2560
+ }
2561
+ async function isClauxyRunning(port) {
2562
+ try {
2563
+ const response = await fetch(`http://localhost:${port}/health`, {
2564
+ signal: AbortSignal.timeout(1000)
2565
+ });
2566
+ return response.ok && await response.text() === "OK";
2567
+ } catch {
2568
+ return false;
2569
+ }
2570
+ }
2571
+ async function wrapperCommand(options) {
2572
+ const { logLevel = "warn", connect = false, provider, port: requestedPort, claudeArgs = [] } = options;
2573
+ const claudePath = await findClaudeCli();
2574
+ if (!claudePath) {
2575
+ console.error(c2.red("Error: Claude CLI not found."));
2576
+ console.error(`
2577
+ Install it with: ${c2.cyan("npm i -g @anthropic-ai/claude-code")}`);
2578
+ console.error(`Or visit: ${c2.cyan("https://docs.anthropic.com/en/docs/claude-code")}`);
2579
+ process.exit(1);
2580
+ }
2581
+ const availableProviders = getProviderList().map((item) => item.id);
2582
+ const providerOverride = provider?.toLowerCase();
2583
+ if (providerOverride && !availableProviders.includes(providerOverride)) {
2584
+ console.error(`Invalid provider: "${providerOverride}". Valid values: ${availableProviders.join(" | ")}.`);
2585
+ process.exit(1);
2586
+ }
2587
+ let activeProvider;
2588
+ if (providerOverride) {
2589
+ const credentials = await Auth.get(providerOverride);
2590
+ if (credentials && !connect) {
2591
+ activeProvider = providerOverride;
2592
+ } else {
2593
+ activeProvider = await authenticateProvider2(providerOverride);
2594
+ }
2595
+ } else {
2596
+ activeProvider = await Config.getActiveProvider();
2597
+ if (!activeProvider || connect) {
2598
+ activeProvider = await selectAndAuthenticate2();
2599
+ } else {
2600
+ const credentials = await Auth.get(activeProvider);
2601
+ if (!credentials) {
2602
+ activeProvider = await selectAndAuthenticate2();
2603
+ }
2604
+ }
2605
+ }
2606
+ await Config.setActiveProvider(activeProvider);
2607
+ let proxyPort;
2608
+ let proxyServer = null;
2609
+ if (requestedPort && await isClauxyRunning(requestedPort)) {
2610
+ proxyPort = requestedPort;
2611
+ console.log(`${c2.green(">")} ${c2.bold("Clauxy")} reusing proxy on ${c2.cyan(`localhost:${proxyPort}`)}`);
2612
+ } else {
2613
+ const { startProxy: startProxy2 } = await init_proxy().then(() => exports_proxy);
2614
+ const server = await startProxy2({
2615
+ port: requestedPort,
2616
+ logLevel,
2617
+ providerId: activeProvider,
2618
+ quiet: true
2619
+ });
2620
+ proxyServer = server;
2621
+ proxyPort = server.port;
2622
+ const providerInfo = await getProvider(activeProvider);
2623
+ console.log(`${c2.green(">")} ${c2.bold("Clauxy")} proxy on ${c2.cyan(`localhost:${proxyPort}`)} ${c2.dim(`(${providerInfo.info.name})`)}`);
2624
+ }
2625
+ const claude = Bun.spawn(["claude", ...claudeArgs], {
2626
+ env: {
2627
+ ...process.env,
2628
+ ANTHROPIC_BASE_URL: `http://localhost:${proxyPort}`,
2629
+ ANTHROPIC_API_KEY: "clauxy"
2630
+ },
2631
+ stdin: "inherit",
2632
+ stdout: "inherit",
2633
+ stderr: "inherit"
2634
+ });
2635
+ let isCleaningUp = false;
2636
+ const cleanup = () => {
2637
+ if (isCleaningUp)
2638
+ return;
2639
+ isCleaningUp = true;
2640
+ proxyServer?.stop();
2641
+ claude.kill();
2642
+ };
2643
+ process.on("SIGINT", cleanup);
2644
+ process.on("SIGTERM", cleanup);
2645
+ process.on("SIGHUP", cleanup);
2646
+ const exitCode = await claude.exited;
2647
+ proxyServer?.stop();
2648
+ process.exit(exitCode);
2487
2649
  }
2488
2650
 
2489
2651
  // cli/commands/auth.ts
@@ -2503,14 +2665,14 @@ Configured Providers:
2503
2665
  `);
2504
2666
  return;
2505
2667
  }
2506
- for (const provider2 of providers) {
2507
- const auth = allAuth[provider2.id];
2668
+ for (const provider of providers) {
2669
+ const auth = allAuth[provider.id];
2508
2670
  if (auth) {
2509
- const isActive = provider2.id === activeProvider;
2671
+ const isActive = provider.id === activeProvider;
2510
2672
  const marker = isActive ? colors.green("*") : " ";
2511
2673
  const authType = auth.type === "oauth" ? "OAuth" : "API Key";
2512
2674
  const activeLabel = isActive ? ` ${colors.green("[active]")}` : "";
2513
- console.log(` ${marker} ${provider2.name} (${authType})${activeLabel}`);
2675
+ console.log(` ${marker} ${provider.name} (${authType})${activeLabel}`);
2514
2676
  }
2515
2677
  }
2516
2678
  console.log("");
@@ -2520,7 +2682,7 @@ Configured Providers:
2520
2682
  init_config();
2521
2683
  init_providers();
2522
2684
  init_models();
2523
- import * as prompts3 from "@clack/prompts";
2685
+ import * as prompts4 from "@clack/prompts";
2524
2686
 
2525
2687
  // cli/constants.ts
2526
2688
  var CUSTOM_MODEL_VALUE = "__custom__";
@@ -2544,21 +2706,21 @@ async function selectModel(tier, providerId, currentValue) {
2544
2706
  })),
2545
2707
  { label: "Enter custom model", value: CUSTOM_MODEL_VALUE, hint: "Type model name" }
2546
2708
  ];
2547
- const choice = await prompts3.select({
2709
+ const choice = await prompts4.select({
2548
2710
  message: `Select ${tier} model`,
2549
2711
  options,
2550
2712
  initialValue: currentValue
2551
2713
  });
2552
- if (prompts3.isCancel(choice)) {
2714
+ if (prompts4.isCancel(choice)) {
2553
2715
  return null;
2554
2716
  }
2555
2717
  if (choice === CUSTOM_MODEL_VALUE) {
2556
- const custom = await prompts3.text({
2718
+ const custom = await prompts4.text({
2557
2719
  message: `Enter custom ${tier} model name`,
2558
2720
  placeholder: currentValue,
2559
2721
  defaultValue: currentValue
2560
2722
  });
2561
- if (prompts3.isCancel(custom)) {
2723
+ if (prompts4.isCancel(custom)) {
2562
2724
  return null;
2563
2725
  }
2564
2726
  return custom;
@@ -2567,9 +2729,9 @@ async function selectModel(tier, providerId, currentValue) {
2567
2729
  }
2568
2730
  async function configureModels() {
2569
2731
  const activeProvider = await Config.getActiveProvider();
2570
- prompts3.intro("Clauxy - Model Configuration");
2732
+ prompts4.intro("Clauxy - Model Configuration");
2571
2733
  const providers = getProviderList();
2572
- const providerChoice = await prompts3.select({
2734
+ const providerChoice = await prompts4.select({
2573
2735
  message: "Select provider to configure models for",
2574
2736
  options: providers.map((p) => ({
2575
2737
  label: p.name,
@@ -2587,7 +2749,7 @@ Current models for ${PROVIDERS[providerId].name}:`);
2587
2749
  console.log(` Mid: ${currentModels.mid}`);
2588
2750
  console.log(` Small: ${currentModels.small}
2589
2751
  `);
2590
- const action = await prompts3.select({
2752
+ const action = await prompts4.select({
2591
2753
  message: "What would you like to do?",
2592
2754
  options: [
2593
2755
  { label: "Configure all models", value: "all" },
@@ -2605,36 +2767,36 @@ Models reset to defaults:`);
2605
2767
  console.log(` Big: ${defaults.big}`);
2606
2768
  console.log(` Mid: ${defaults.mid}`);
2607
2769
  console.log(` Small: ${defaults.small}`);
2608
- prompts3.outro("Done");
2770
+ prompts4.outro("Done");
2609
2771
  return;
2610
2772
  }
2611
- const models2 = {
2773
+ const models = {
2612
2774
  big: currentModels.big,
2613
2775
  mid: currentModels.mid,
2614
2776
  small: currentModels.small
2615
2777
  };
2616
2778
  for (const tier of MODEL_TIERS) {
2617
2779
  if (action === "all" || action === tier.key) {
2618
- const result = await selectModel(tier.label, providerId, models2[tier.key]);
2780
+ const result = await selectModel(tier.label, providerId, models[tier.key]);
2619
2781
  if (result === null) {
2620
- prompts3.cancel("Cancelled");
2782
+ prompts4.cancel("Cancelled");
2621
2783
  process.exit(0);
2622
2784
  }
2623
- models2[tier.key] = result;
2785
+ models[tier.key] = result;
2624
2786
  }
2625
2787
  }
2626
- await saveModels(models2);
2788
+ await saveModels(models);
2627
2789
  console.log(`
2628
2790
  Models configured:`);
2629
- console.log(` opus/big -> ${models2.big}`);
2630
- console.log(` sonnet/mid -> ${models2.mid}`);
2631
- console.log(` haiku/small -> ${models2.small}`);
2632
- prompts3.outro("Model configuration saved");
2791
+ console.log(` opus/big -> ${models.big}`);
2792
+ console.log(` sonnet/mid -> ${models.mid}`);
2793
+ console.log(` haiku/small -> ${models.small}`);
2794
+ prompts4.outro("Model configuration saved");
2633
2795
  }
2634
2796
  // package.json
2635
2797
  var package_default = {
2636
2798
  name: "@jcanizalez7/clauxy",
2637
- version: "0.1.9",
2799
+ version: "0.2.1",
2638
2800
  description: "Multi-provider AI proxy for Claude Code (GitHub Copilot, ChatGPT Plus, Google Gemini)",
2639
2801
  type: "module",
2640
2802
  bin: {
@@ -2679,7 +2841,7 @@ var package_default = {
2679
2841
  var run = defineCommand({
2680
2842
  meta: {
2681
2843
  name: "run",
2682
- description: "Start the proxy server"
2844
+ description: "Start the proxy server (without launching Claude)"
2683
2845
  },
2684
2846
  args: {
2685
2847
  port: {
@@ -2715,12 +2877,12 @@ var run = defineCommand({
2715
2877
  if (!LOG_LEVELS2.includes(logLevel)) {
2716
2878
  console.error(`Invalid log level: "${args["log-level"]}". Valid values: ${LOG_LEVELS2.join(" | ")}. Using "${DEFAULT_LOG_LEVEL}".`);
2717
2879
  }
2718
- const provider2 = args.provider ? String(args.provider).toLowerCase() : undefined;
2880
+ const provider = args.provider ? String(args.provider).toLowerCase() : undefined;
2719
2881
  await runCommand({
2720
2882
  port,
2721
2883
  logLevel: LOG_LEVELS2.includes(logLevel) ? logLevel : DEFAULT_LOG_LEVEL,
2722
2884
  connect: args.connect,
2723
- provider: provider2
2885
+ provider
2724
2886
  });
2725
2887
  }
2726
2888
  });
@@ -2756,13 +2918,50 @@ var main = defineCommand({
2756
2918
  meta: {
2757
2919
  name: "clauxy",
2758
2920
  version: package_default.version,
2759
- description: "Multi-Provider AI Proxy for Claude Code"
2921
+ description: "Multi-Provider AI Proxy for Claude Code. Run without arguments to start proxy and launch Claude automatically."
2922
+ },
2923
+ args: {
2924
+ "log-level": {
2925
+ type: "string",
2926
+ alias: "l",
2927
+ description: "Log level: debug, info, warn, error",
2928
+ default: "warn"
2929
+ },
2930
+ connect: {
2931
+ type: "boolean",
2932
+ alias: "c",
2933
+ description: "Re-authenticate before starting",
2934
+ default: false
2935
+ },
2936
+ provider: {
2937
+ type: "string",
2938
+ alias: "p",
2939
+ description: "Provider to use (copilot, chatgpt, google)"
2940
+ },
2941
+ port: {
2942
+ type: "string",
2943
+ description: "Port to use (reuses existing proxy if running)"
2944
+ }
2760
2945
  },
2761
2946
  subCommands: {
2762
2947
  run,
2763
2948
  auth,
2764
2949
  model,
2765
2950
  models: model
2951
+ },
2952
+ async run({ args, rawArgs }) {
2953
+ const port = args.port ? parseInt(args.port, 10) : undefined;
2954
+ if (args.port && isNaN(port)) {
2955
+ console.error(`Invalid port number: "${args.port}". Use --port <number> (e.g., --port 3000).`);
2956
+ process.exit(1);
2957
+ }
2958
+ await wrapperCommand({
2959
+ logLevel: args["log-level"],
2960
+ connect: args.connect,
2961
+ provider: args.provider,
2962
+ port,
2963
+ claudeArgs: rawArgs
2964
+ });
2766
2965
  }
2767
2966
  });
2768
2967
  function cli() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jcanizalez7/clauxy",
3
- "version": "0.1.9",
3
+ "version": "0.2.1",
4
4
  "description": "Multi-provider AI proxy for Claude Code (GitHub Copilot, ChatGPT Plus, Google Gemini)",
5
5
  "type": "module",
6
6
  "bin": {