ai-cc-router 0.1.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/Dockerfile ADDED
@@ -0,0 +1,32 @@
1
+ # ── Build stage ───────────────────────────────────────────────────────────────
2
+ FROM node:22-alpine AS builder
3
+ WORKDIR /app
4
+
5
+ COPY package*.json ./
6
+ RUN npm ci
7
+
8
+ COPY tsconfig.json ./
9
+ COPY src/ ./src/
10
+ RUN npm run build
11
+
12
+ # ── Runtime stage ─────────────────────────────────────────────────────────────
13
+ FROM node:22-alpine AS runtime
14
+ WORKDIR /app
15
+
16
+ ENV NODE_ENV=production
17
+ # Port defaults — overridden by docker-compose environment section
18
+ ENV PORT=3456
19
+ ENV ACCOUNTS_PATH=/app/accounts.json
20
+
21
+ # Install only production deps
22
+ COPY package*.json ./
23
+ RUN npm ci --omit=dev && npm cache clean --force
24
+
25
+ COPY --from=builder /app/dist ./dist
26
+
27
+ EXPOSE 3456
28
+
29
+ # accounts.json is expected to be mounted at runtime via docker-compose volume
30
+ # The container will exit with a clear error if it's not present
31
+ ENTRYPOINT ["node", "dist/cli/index.js"]
32
+ CMD ["start"]
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CC-Router Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,395 @@
1
+ # CC-Router
2
+
3
+ **Round-robin proxy for multiple Claude Max accounts.**
4
+ Distribute Claude Code requests across N subscriptions to multiply your throughput.
5
+
6
+ [![CI](https://github.com/VictorMinemu/CC-Router/actions/workflows/ci.yml/badge.svg)](https://github.com/VictorMinemu/CC-Router/actions/workflows/ci.yml)
7
+ [![npm](https://img.shields.io/npm/v/ai-cc-router)](https://www.npmjs.com/package/ai-cc-router)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
9
+
10
+ ---
11
+
12
+ > **Warning**
13
+ > Read the [disclaimer](#disclaimer) before using this tool.
14
+
15
+ ---
16
+
17
+ ## How it works
18
+
19
+ ```
20
+ Claude Code (terminal)
21
+
22
+ │ ANTHROPIC_BASE_URL=http://localhost:3456
23
+ │ ANTHROPIC_AUTH_TOKEN=proxy-managed
24
+
25
+ ┌─────────────────────────────────────┐
26
+ │ CC-Router :3456 │
27
+ │ │
28
+ │ 1. Receives request /v1/messages │
29
+ │ 2. Round-robin → picks account N │
30
+ │ 3. Refreshes token if expiring │
31
+ │ 4. Injects Authorization: Bearer │
32
+ │ 5. Forwards to Anthropic (or │
33
+ │ LiteLLM for advanced mode) │
34
+ └──────────────┬──────────────────────┘
35
+
36
+
37
+ api.anthropic.com
38
+ (authenticated with
39
+ OAuth token of account N)
40
+ ```
41
+
42
+ All standard Claude Code features work transparently: streaming, extended thinking, tool use, prompt caching.
43
+
44
+ ---
45
+
46
+ ## Use cases
47
+
48
+ ### Heavy user — one account isn't enough
49
+
50
+ Claude Max has rate limits per account. If you hit them regularly mid-session — waiting for cooldowns, getting 429s — you're a good candidate.
51
+
52
+ With two accounts you double your effective rate limit. With three, you triple it. The proxy distributes requests automatically; you don't change how you use Claude Code at all.
53
+
54
+ ```text
55
+ 1 account → hit limit, wait 60s, continue
56
+ 3 accounts → request rotates across all three, limit effectively tripled
57
+ ```
58
+
59
+ ---
60
+
61
+ ### Team sharing accounts — fewer subscriptions, same throughput
62
+
63
+ A team of five doesn't need five Max subscriptions. In practice, developers don't all peak at the same time. Three accounts can comfortably serve five people working normal hours.
64
+
65
+ #### Example setup: 5 devs, 3 accounts
66
+
67
+ ```text
68
+ cc-router (hosted on a shared machine or VPS)
69
+
70
+ ├── max-account-1 ← alice's subscription
71
+ ├── max-account-2 ← bob's subscription
72
+ └── max-account-3 ← carol's subscription
73
+
74
+ └── serves: alice, bob, carol, dave, eve
75
+ ```
76
+
77
+ Each developer sets their `ANTHROPIC_BASE_URL` to the shared proxy. Done. The proxy handles routing and token refresh invisibly.
78
+
79
+ #### Cost example
80
+
81
+ | Setup | Monthly cost |
82
+ |-------|-------------|
83
+ | 5 individual Max subscriptions | 5 × $100 = **$500/mo** |
84
+ | 3 shared via cc-router | 3 × $100 = **$300/mo** |
85
+
86
+ You save $200/mo without any loss in capability for a typical team workload.
87
+
88
+ ---
89
+
90
+ ### Hosting cc-router on a shared machine
91
+
92
+ Run cc-router on a machine everyone on the team can reach — a home server, a VPS, or a spare machine on the office network.
93
+
94
+ #### On the server
95
+
96
+ ```bash
97
+ npm install -g cc-router
98
+ cc-router setup # configure the 3 shared accounts
99
+ cc-router service install # auto-start on boot
100
+ ```
101
+
102
+ By default cc-router binds to `localhost`. To accept connections from other machines, set the `HOST` environment variable:
103
+
104
+ ```bash
105
+ # Listen on all interfaces (team LAN or VPS)
106
+ HOST=0.0.0.0 cc-router start
107
+
108
+ # Or configure it permanently in the service
109
+ ```
110
+
111
+ #### On each developer's machine
112
+
113
+ No installation needed. Just set two environment variables in `~/.claude/settings.json`:
114
+
115
+ ```json
116
+ {
117
+ "env": {
118
+ "ANTHROPIC_BASE_URL": "http://192.168.1.50:3456",
119
+ "ANTHROPIC_AUTH_TOKEN": "proxy-managed"
120
+ }
121
+ }
122
+ ```
123
+
124
+ Replace `192.168.1.50` with the server's IP or hostname. Then run `claude` normally.
125
+
126
+ Or use the CLI to write the settings automatically:
127
+
128
+ ```bash
129
+ cc-router configure --port 3456
130
+ # Then manually update ANTHROPIC_BASE_URL to the remote IP
131
+ ```
132
+
133
+ ---
134
+
135
+ ### Hosting on a VPS (internet-accessible)
136
+
137
+ If your team is distributed or works remotely, run cc-router on a VPS and expose it over HTTPS via a reverse proxy.
138
+
139
+ #### Recommended nginx config
140
+
141
+ ```nginx
142
+ server {
143
+ listen 443 ssl;
144
+ server_name cc-router.yourcompany.com;
145
+
146
+ # ... SSL cert config (e.g. Let's Encrypt) ...
147
+
148
+ location / {
149
+ proxy_pass http://127.0.0.1:3456;
150
+ proxy_buffering off; # required for SSE streaming
151
+ proxy_read_timeout 300s; # required for long thinking requests
152
+ proxy_set_header X-Forwarded-For $remote_addr;
153
+ }
154
+ }
155
+ ```
156
+
157
+ Each developer then points to:
158
+ ```json
159
+ {
160
+ "env": {
161
+ "ANTHROPIC_BASE_URL": "https://cc-router.yourcompany.com",
162
+ "ANTHROPIC_AUTH_TOKEN": "proxy-managed"
163
+ }
164
+ }
165
+ ```
166
+
167
+ **Security note:** if the proxy is internet-accessible, add authentication at the nginx level (basic auth, mTLS, or IP allowlist) so only your team can use it. cc-router does not implement user authentication itself.
168
+
169
+ ---
170
+
171
+ ## Quickstart
172
+
173
+ ```bash
174
+ # 1. Install
175
+ npm install -g cc-router
176
+
177
+ # 2. Wizard: extract tokens + configure Claude Code automatically
178
+ cc-router setup
179
+
180
+ # 3. Start the proxy
181
+ cc-router start
182
+
183
+ # 4. Use Claude Code normally — the proxy is transparent
184
+ claude
185
+ ```
186
+
187
+ That's it. Claude Code will route through the proxy without any further changes.
188
+
189
+ **Optional:** install as a system service so it starts automatically on boot:
190
+ ```bash
191
+ cc-router service install
192
+ ```
193
+
194
+ ---
195
+
196
+ ## Installation
197
+
198
+ **Requirements:** Node.js 20 or 22.
199
+
200
+ ```bash
201
+ npm install -g ai-cc-router
202
+ ```
203
+
204
+ Verify:
205
+ ```bash
206
+ cc-router --version
207
+ ```
208
+
209
+ ---
210
+
211
+ ## Setup by platform
212
+
213
+ ### macOS
214
+
215
+ cc-router can extract OAuth tokens directly from the macOS Keychain — no manual copy-pasting needed.
216
+
217
+ ```bash
218
+ cc-router setup
219
+ # Select "Extract automatically from macOS Keychain"
220
+ ```
221
+
222
+ For multiple accounts, you need to switch accounts in Claude Code between extractions:
223
+ ```bash
224
+ # Account 1 is already logged in — run setup and extract
225
+ cc-router setup
226
+
227
+ # To add account 2:
228
+ claude logout && claude login # log in with account 2
229
+ cc-router setup --add # extract and merge
230
+ claude logout && claude login # log back in with account 1
231
+ ```
232
+
233
+ ### Linux
234
+
235
+ Tokens are read from `~/.claude/.credentials.json`:
236
+ ```bash
237
+ cc-router setup
238
+ # Select "Read from ~/.claude/.credentials.json"
239
+ ```
240
+
241
+ Make sure Claude Code is installed and you have run `claude login` at least once.
242
+
243
+ ### Windows
244
+
245
+ Same as Linux — tokens are read from `~/.claude/.credentials.json` (Windows path: `%USERPROFILE%\.claude\.credentials.json`).
246
+
247
+ ```bash
248
+ cc-router setup
249
+ ```
250
+
251
+ ---
252
+
253
+ ## CLI Reference
254
+
255
+ ```text
256
+ cc-router setup Interactive wizard: extract tokens + configure Claude Code
257
+ cc-router setup --add Add another account to an existing configuration
258
+
259
+ cc-router start Start proxy on localhost:3456 (foreground)
260
+ cc-router start --daemon Start in background via PM2
261
+ cc-router start --litellm Start with LiteLLM in Docker (advanced mode)
262
+
263
+ cc-router stop Stop proxy + restore Claude Code to normal auth
264
+ cc-router stop --keep-config Stop proxy only (keep settings.json)
265
+ cc-router revert Restore Claude Code to normal authentication
266
+
267
+ cc-router status Live dashboard (updates every 2s, press q to quit)
268
+ cc-router status --json Print current stats as JSON and exit
269
+
270
+ cc-router accounts list List configured accounts (live stats if proxy is running)
271
+ cc-router accounts add Add an account interactively
272
+ cc-router accounts remove <id> Remove an account
273
+
274
+ cc-router service install Register cc-router to start on system boot (PM2)
275
+ cc-router service uninstall Remove from system startup
276
+ cc-router service status Show PM2 service status
277
+ cc-router service logs Tail proxy logs from PM2
278
+
279
+ cc-router configure (Re)write ~/.claude/settings.json
280
+ cc-router configure --show Show current Claude Code proxy settings
281
+ cc-router configure --remove Remove cc-router settings (same as revert without stopping)
282
+
283
+ cc-router docker up Start full Docker stack (cc-router + LiteLLM)
284
+ cc-router docker up --build Rebuild cc-router image before starting
285
+ cc-router docker down Stop Docker containers
286
+ cc-router docker logs Tail all Docker logs
287
+ cc-router docker ps Show container status
288
+ cc-router docker restart [service] Restart a service
289
+ ```
290
+
291
+ ---
292
+
293
+ ## Modes of operation
294
+
295
+ ### Standalone (default — no Docker)
296
+
297
+ ```text
298
+ Claude Code → cc-router:3456 → api.anthropic.com
299
+ ```
300
+
301
+ Best for personal use. No Docker required.
302
+
303
+ ```bash
304
+ cc-router start
305
+ ```
306
+
307
+ ### Full mode with LiteLLM (optional — requires Docker)
308
+
309
+ ```text
310
+ Claude Code → cc-router:3456 → LiteLLM:4000 → api.anthropic.com
311
+ ```
312
+
313
+ Adds a LiteLLM layer for usage logging, rate limiting, and a web dashboard at `http://localhost:4000/ui`.
314
+
315
+ ```bash
316
+ cc-router docker up
317
+ # or: cc-router start --litellm
318
+ ```
319
+
320
+ See [docs/litellm-setup.md](docs/litellm-setup.md) for details.
321
+
322
+ ---
323
+
324
+ ## Reverting to normal Claude Code
325
+
326
+ To stop using cc-router and go back to normal Claude Code authentication:
327
+
328
+ ```bash
329
+ cc-router revert
330
+ ```
331
+
332
+ This stops the proxy process and removes cc-router's settings from `~/.claude/settings.json`. Claude Code will use its own authentication on the next launch.
333
+
334
+ ---
335
+
336
+ ## Status dashboard
337
+
338
+ ```bash
339
+ cc-router status
340
+ ```
341
+
342
+ ```text
343
+ CC-Router · standalone → api.anthropic.com · up 2h 14m · [q] quit
344
+
345
+ ACCOUNTS 2/2 healthy
346
+
347
+ ● max-account-1 ok req 142 err 0 expires 6h 48m last 2s ago
348
+ ● max-account-2 ok req 139 err 0 expires 6h 51m last 5s ago
349
+
350
+ TOTALS requests 281 · errors 0 · refreshes 2
351
+
352
+ RECENT ACTIVITY
353
+ 14:23:01 → max-account-1 route
354
+ 14:22:58 → max-account-2 route
355
+ 14:22:45 ↻ max-account-1 refresh
356
+ ```
357
+
358
+ Press `q` to quit. Run with `--json` for non-interactive output.
359
+
360
+ ---
361
+
362
+ ## Security
363
+
364
+ - Tokens are stored locally in `~/.cc-router/accounts.json`, **never in the repository**
365
+ - The file is excluded by `.gitignore`
366
+ - Writes are atomic (write to `.tmp`, then rename) — no corruption on crash
367
+ - Keychain reads use `execFile` with a fixed argument array — no shell injection
368
+ - No telemetry, no external logging
369
+
370
+ See [docs/security.md](docs/security.md) for details.
371
+
372
+ ---
373
+
374
+ ## Disclaimer
375
+
376
+ > CC-Router uses the OAuth tokens of your own Claude Max subscriptions.
377
+ >
378
+ > **Read Anthropic's Terms of Service before using this tool.**
379
+ > Using multiple Max subscriptions to increase throughput may violate the ToS. Anthropic has been known to ban accounts for unusual OAuth usage patterns.
380
+ >
381
+ > The authors are not responsible for any account bans, loss of access, or other consequences resulting from the use of this software. Use at your own risk.
382
+
383
+ ---
384
+
385
+ ## Contributing
386
+
387
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
388
+
389
+ Bug reports → [GitHub Issues](https://github.com/VictorMinemu/CC-Router/issues)
390
+
391
+ ---
392
+
393
+ ## License
394
+
395
+ [MIT](LICENSE)
@@ -0,0 +1,16 @@
1
+ [
2
+ {
3
+ "id": "max-account-1",
4
+ "accessToken": "sk-ant-oat01-REPLACE_ME",
5
+ "refreshToken": "sk-ant-ort01-REPLACE_ME",
6
+ "expiresAt": 1748658860000,
7
+ "scopes": ["user:inference", "user:profile"]
8
+ },
9
+ {
10
+ "id": "max-account-2",
11
+ "accessToken": "sk-ant-oat01-REPLACE_ME",
12
+ "refreshToken": "sk-ant-ort01-REPLACE_ME",
13
+ "expiresAt": 1748658860000,
14
+ "scopes": ["user:inference", "user:profile"]
15
+ }
16
+ ]
@@ -0,0 +1,142 @@
1
+ import chalk from "chalk";
2
+ import { loadAccounts, accountsFileExists, writeAccountsAtomic } from "../config/manager.js";
3
+ import { saveAccounts } from "../proxy/token-refresher.js";
4
+ import { formatExpiry, redactToken } from "../utils/token-extractor.js";
5
+ import { PROXY_PORT } from "../config/paths.js";
6
+ export function registerAccounts(program) {
7
+ const accounts = program
8
+ .command("accounts")
9
+ .description("Manage Claude Max accounts in the token pool");
10
+ // ── accounts list ────────────────────────────────────────────────────────
11
+ accounts
12
+ .command("list")
13
+ .description("List all configured accounts and their status")
14
+ .option("--json", "Output as JSON")
15
+ .action(async (opts) => {
16
+ // Try to get live stats from the running proxy first
17
+ const liveStats = await fetchLiveStats();
18
+ if (!accountsFileExists()) {
19
+ console.log(chalk.yellow("No accounts configured. Run: cc-router setup"));
20
+ return;
21
+ }
22
+ const stored = loadAccounts();
23
+ if (stored.length === 0) {
24
+ console.log(chalk.yellow("accounts.json is empty. Run: cc-router setup"));
25
+ return;
26
+ }
27
+ if (opts.json) {
28
+ console.log(JSON.stringify(liveStats ?? stored, null, 2));
29
+ return;
30
+ }
31
+ console.log(chalk.bold(`\n Accounts (${stored.length} configured)\n`));
32
+ if (liveStats) {
33
+ console.log(chalk.green(" ● Proxy is running — showing live stats\n"));
34
+ for (const s of liveStats) {
35
+ const status = s.healthy
36
+ ? chalk.green("✓ healthy")
37
+ : chalk.red("✗ unhealthy");
38
+ const busy = s.busy ? chalk.yellow(" [busy]") : "";
39
+ const exp = s.expiresInMs > 0
40
+ ? chalk.yellow(formatMs(s.expiresInMs))
41
+ : chalk.red("EXPIRED");
42
+ console.log(` ${chalk.bold(s.id.padEnd(24))}` +
43
+ ` ${status}${busy}` +
44
+ ` requests: ${chalk.cyan(String(s.requestCount).padStart(5))}` +
45
+ ` errors: ${chalk.red(String(s.errorCount).padStart(3))}` +
46
+ ` expires: ${exp}`);
47
+ }
48
+ }
49
+ else {
50
+ console.log(chalk.gray(" (Proxy not running — showing stored configuration)\n"));
51
+ for (const a of stored) {
52
+ const exp = formatExpiry(a.tokens.expiresAt);
53
+ const expColor = a.tokens.expiresAt > Date.now()
54
+ ? chalk.yellow(exp)
55
+ : chalk.red(exp);
56
+ console.log(` ${chalk.bold(a.id.padEnd(24))}` +
57
+ ` ${redactToken(a.tokens.accessToken).padEnd(26)}` +
58
+ ` expires: ${expColor}` +
59
+ ` scopes: ${chalk.gray(a.tokens.scopes.join(" "))}`);
60
+ }
61
+ }
62
+ console.log();
63
+ });
64
+ // ── accounts add ─────────────────────────────────────────────────────────
65
+ accounts
66
+ .command("add")
67
+ .description("Add a new Claude Max account interactively")
68
+ .action(async () => {
69
+ const { setupSingleAccount } = await import("./cmd-setup.js");
70
+ const existing = accountsFileExists() ? loadAccounts() : [];
71
+ const account = await setupSingleAccount(existing.length + 1);
72
+ if (!account) {
73
+ console.log(chalk.yellow("\nNo account added.\n"));
74
+ return;
75
+ }
76
+ // Merge: replace by ID if already exists, otherwise append
77
+ const merged = [
78
+ ...existing.filter(a => a.id !== account.id),
79
+ account,
80
+ ];
81
+ saveAccounts(merged);
82
+ console.log(chalk.green(`\n✓ Account "${account.id}" added (${merged.length} total).\n`));
83
+ console.log(chalk.gray(" Restart the proxy to load the new account: cc-router start\n"));
84
+ });
85
+ // ── accounts remove ───────────────────────────────────────────────────────
86
+ accounts
87
+ .command("remove <id>")
88
+ .description("Remove an account by its ID")
89
+ .action(async (id) => {
90
+ if (!accountsFileExists()) {
91
+ console.log(chalk.yellow("No accounts configured."));
92
+ return;
93
+ }
94
+ const existing = loadAccounts();
95
+ const filtered = existing.filter(a => a.id !== id);
96
+ if (filtered.length === existing.length) {
97
+ console.log(chalk.red(`✗ Account "${id}" not found.`));
98
+ console.log(chalk.gray(` Available: ${existing.map(a => a.id).join(", ")}`));
99
+ process.exit(1);
100
+ }
101
+ const { confirm } = await import("@inquirer/prompts");
102
+ const sure = await confirm({
103
+ message: `Remove "${id}"? This cannot be undone.`,
104
+ default: false,
105
+ });
106
+ if (!sure) {
107
+ console.log(chalk.gray("Cancelled."));
108
+ return;
109
+ }
110
+ writeAccountsAtomic(filtered.map(a => ({
111
+ id: a.id,
112
+ accessToken: a.tokens.accessToken,
113
+ refreshToken: a.tokens.refreshToken,
114
+ expiresAt: a.tokens.expiresAt,
115
+ scopes: a.tokens.scopes,
116
+ })));
117
+ console.log(chalk.green(`✓ Removed "${id}". ${filtered.length} account(s) remaining.`));
118
+ if (filtered.length === 0) {
119
+ console.log(chalk.yellow(" No accounts left. Run: cc-router setup"));
120
+ }
121
+ });
122
+ }
123
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
124
+ async function fetchLiveStats() {
125
+ try {
126
+ const res = await fetch(`http://localhost:${PROXY_PORT}/cc-router/health`, {
127
+ signal: AbortSignal.timeout(1_000),
128
+ });
129
+ if (!res.ok)
130
+ return null;
131
+ const data = await res.json();
132
+ return data.accounts;
133
+ }
134
+ catch {
135
+ return null;
136
+ }
137
+ }
138
+ function formatMs(ms) {
139
+ const h = Math.floor(ms / 3_600_000);
140
+ const m = Math.floor((ms % 3_600_000) / 60_000);
141
+ return h > 0 ? `${h}h ${m}m` : `${m}m`;
142
+ }
@@ -0,0 +1,37 @@
1
+ import chalk from "chalk";
2
+ import { writeClaudeSettings, removeClaudeSettings, readClaudeProxySettings } from "../utils/claude-config.js";
3
+ import { PROXY_PORT, CLAUDE_SETTINGS_PATH } from "../config/paths.js";
4
+ export function registerConfigure(program) {
5
+ program
6
+ .command("configure")
7
+ .description("Update ~/.claude/settings.json to point to the proxy (or remove the config)")
8
+ .option("--remove", "Remove cc-router settings from ~/.claude/settings.json")
9
+ .option("--port <port>", "Proxy port to configure", String(PROXY_PORT))
10
+ .option("--show", "Show current Claude Code proxy settings")
11
+ .action((opts) => {
12
+ if (opts.show) {
13
+ const current = readClaudeProxySettings();
14
+ if (current.baseUrl) {
15
+ console.log(chalk.green(" Claude Code is configured to use cc-router:"));
16
+ console.log(` ANTHROPIC_BASE_URL = ${chalk.cyan(current.baseUrl)}`);
17
+ console.log(` ANTHROPIC_AUTH_TOKEN = ${chalk.gray(current.authToken ?? "(not set)")}`);
18
+ }
19
+ else {
20
+ console.log(chalk.yellow(" Claude Code is NOT configured to use cc-router."));
21
+ console.log(chalk.gray(` Run: cc-router configure`));
22
+ }
23
+ return;
24
+ }
25
+ if (opts.remove) {
26
+ removeClaudeSettings();
27
+ console.log(chalk.green("✓ Removed cc-router settings from ~/.claude/settings.json"));
28
+ console.log(chalk.gray(" Claude Code will use its default authentication on next launch."));
29
+ return;
30
+ }
31
+ const port = parseInt(opts.port, 10);
32
+ writeClaudeSettings(port);
33
+ console.log(chalk.green(`✓ Updated ${CLAUDE_SETTINGS_PATH}`));
34
+ console.log(chalk.gray(` ANTHROPIC_BASE_URL = http://localhost:${port}`));
35
+ console.log(chalk.gray(` ANTHROPIC_AUTH_TOKEN = proxy-managed`));
36
+ });
37
+ }