@timo972/cc-router 0.7.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.
Files changed (74) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/Dockerfile +42 -0
  3. package/LICENSE +21 -0
  4. package/README.md +716 -0
  5. package/accounts.example.json +25 -0
  6. package/dist/cli/cmd-accounts.js +248 -0
  7. package/dist/cli/cmd-client.js +612 -0
  8. package/dist/cli/cmd-configure.js +145 -0
  9. package/dist/cli/cmd-docker.js +140 -0
  10. package/dist/cli/cmd-logs.js +85 -0
  11. package/dist/cli/cmd-models.js +125 -0
  12. package/dist/cli/cmd-service.js +193 -0
  13. package/dist/cli/cmd-setup.js +501 -0
  14. package/dist/cli/cmd-start.js +318 -0
  15. package/dist/cli/cmd-status.js +177 -0
  16. package/dist/cli/cmd-stop.js +100 -0
  17. package/dist/cli/cmd-telemetry.js +58 -0
  18. package/dist/cli/cmd-update.js +37 -0
  19. package/dist/cli/index.js +59 -0
  20. package/dist/config/manager.js +262 -0
  21. package/dist/config/paths.js +21 -0
  22. package/dist/config/telemetry.js +64 -0
  23. package/dist/daemon/launcher.js +163 -0
  24. package/dist/daemon/pid.js +98 -0
  25. package/dist/daemon/service.js +260 -0
  26. package/dist/interceptor/mitmproxy-manager.js +616 -0
  27. package/dist/protocol/anthropic-to-openai.js +51 -0
  28. package/dist/protocol/anthropic-types.js +1 -0
  29. package/dist/protocol/model-ref.js +36 -0
  30. package/dist/protocol/model-routing-config.js +30 -0
  31. package/dist/protocol/openai-response-to-anthropic.js +20 -0
  32. package/dist/protocol/openai-responses-types.js +1 -0
  33. package/dist/protocol/openai-stream-to-anthropic.js +75 -0
  34. package/dist/protocol/openai-to-anthropic.js +61 -0
  35. package/dist/protocol/sse.js +17 -0
  36. package/dist/providers/model-discovery.js +71 -0
  37. package/dist/providers/openai/account-pool.js +11 -0
  38. package/dist/providers/openai/account-record.js +33 -0
  39. package/dist/providers/openai/codex-transport.js +36 -0
  40. package/dist/providers/openai/device-oauth.js +116 -0
  41. package/dist/providers/openai/token-refresher.js +56 -0
  42. package/dist/providers/route-selector.js +8 -0
  43. package/dist/providers/types.js +1 -0
  44. package/dist/proxy/account-deletion.js +44 -0
  45. package/dist/proxy/anthropic-proxy.js +26 -0
  46. package/dist/proxy/anthropic-routing.js +90 -0
  47. package/dist/proxy/lease-lifecycle.js +68 -0
  48. package/dist/proxy/logger.js +39 -0
  49. package/dist/proxy/messages-cross-route.js +179 -0
  50. package/dist/proxy/models-server.js +150 -0
  51. package/dist/proxy/provider-routing.js +14 -0
  52. package/dist/proxy/responses-server.js +91 -0
  53. package/dist/proxy/server.js +875 -0
  54. package/dist/proxy/session-router.js +171 -0
  55. package/dist/proxy/stats.js +25 -0
  56. package/dist/proxy/stream-lifecycle.js +83 -0
  57. package/dist/proxy/token-pool.js +407 -0
  58. package/dist/proxy/token-refresher.js +209 -0
  59. package/dist/proxy/types.js +29 -0
  60. package/dist/ui/Dashboard.js +640 -0
  61. package/dist/ui/accountsApi.js +48 -0
  62. package/dist/ui/modelsApi.js +47 -0
  63. package/dist/utils/claude-config.js +185 -0
  64. package/dist/utils/codex-config.js +62 -0
  65. package/dist/utils/network.js +16 -0
  66. package/dist/utils/platform.js +13 -0
  67. package/dist/utils/self-update.js +239 -0
  68. package/dist/utils/telemetry.js +88 -0
  69. package/dist/utils/token-extractor.js +95 -0
  70. package/dist/utils/token-validator.js +26 -0
  71. package/docker-compose.yml +63 -0
  72. package/litellm-config.yaml +44 -0
  73. package/package.json +69 -0
  74. package/src/interceptor/addon.py +78 -0
@@ -0,0 +1,193 @@
1
+ import { execFile } from "child_process";
2
+ import { promisify } from "util";
3
+ import { fileURLToPath } from "url";
4
+ import { join, dirname } from "path";
5
+ import chalk from "chalk";
6
+ import { detectPlatform } from "../utils/platform.js";
7
+ const execFileAsync = promisify(execFile);
8
+ // Resolve the path to the compiled CLI entry point
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+ const CLI_ENTRY = join(__dirname, "index.js");
12
+ export function registerService(program) {
13
+ const service = program
14
+ .command("service")
15
+ .description("Manage cc-router as a system service (auto-start on boot via PM2)");
16
+ service
17
+ .command("install")
18
+ .description("Register cc-router to start automatically on system boot")
19
+ .action(async () => {
20
+ console.log(chalk.cyan("\nInstalling cc-router as a system service...\n"));
21
+ // 1. Verify PM2 is installed
22
+ const pm2Version = await getPm2Version();
23
+ if (!pm2Version) {
24
+ console.log(chalk.yellow("PM2 not found. Installing globally..."));
25
+ try {
26
+ await execFileAsync("npm", ["install", "-g", "pm2"]);
27
+ console.log(chalk.green("✓ PM2 installed"));
28
+ }
29
+ catch (err) {
30
+ console.error(chalk.red("✗ Failed to install PM2:"), err.message);
31
+ console.error(chalk.gray(" Try manually: npm install -g pm2"));
32
+ process.exit(1);
33
+ }
34
+ }
35
+ else {
36
+ console.log(chalk.green(`✓ PM2 ${pm2Version} found`));
37
+ }
38
+ // 2. Register cc-router in PM2
39
+ console.log(chalk.gray("\nRegistering cc-router in PM2..."));
40
+ try {
41
+ await execFileAsync("pm2", [
42
+ "start", CLI_ENTRY,
43
+ "--name", "cc-router",
44
+ "--interpreter", process.execPath,
45
+ "--max-memory-restart", "500M", // restart if memory exceeds 500MB
46
+ "--",
47
+ "start",
48
+ ]);
49
+ console.log(chalk.green("✓ cc-router registered in PM2"));
50
+ }
51
+ catch (err) {
52
+ const msg = err.message;
53
+ // PM2 may already have the process — try restart instead
54
+ if (msg.includes("already")) {
55
+ await execFileAsync("pm2", ["restart", "cc-router"]);
56
+ console.log(chalk.green("✓ cc-router restarted in PM2"));
57
+ }
58
+ else {
59
+ console.error(chalk.red("✗ Failed to start in PM2:"), msg);
60
+ process.exit(1);
61
+ }
62
+ }
63
+ // 3. Save process list so it survives reboots
64
+ await execFileAsync("pm2", ["save"]);
65
+ console.log(chalk.green("✓ PM2 process list saved"));
66
+ // 4. Generate and apply startup hook
67
+ console.log(chalk.gray("\nConfiguring system startup hook..."));
68
+ console.log(chalk.gray("(may ask for your password on Linux/macOS)\n"));
69
+ try {
70
+ const { stdout, stderr } = await execFileAsync("pm2", ["startup"]);
71
+ const output = stdout + stderr;
72
+ // PM2 prints a sudo command to run if it can't apply it automatically
73
+ const sudoMatch = output.match(/sudo\s+.+/);
74
+ if (sudoMatch) {
75
+ console.log(chalk.yellow("Run this command to complete startup registration:"));
76
+ console.log(chalk.white(` ${sudoMatch[0]}`));
77
+ console.log(chalk.gray("\nThen run: pm2 save"));
78
+ }
79
+ else {
80
+ console.log(chalk.green("✓ System startup hook configured"));
81
+ }
82
+ }
83
+ catch (err) {
84
+ const msg = err;
85
+ const combined = (msg.stdout ?? "") + (msg.stderr ?? "");
86
+ const sudoMatch = combined.match(/sudo\s+.+/);
87
+ if (sudoMatch) {
88
+ console.log(chalk.yellow("\nRun this command to complete startup registration:"));
89
+ console.log(chalk.white(` ${sudoMatch[0]}`));
90
+ console.log(chalk.gray("\nThen run: pm2 save"));
91
+ }
92
+ else {
93
+ console.log(chalk.yellow("⚠ Could not configure startup hook automatically."));
94
+ printManualStartupInstructions();
95
+ }
96
+ }
97
+ printServiceInfo();
98
+ });
99
+ service
100
+ .command("uninstall")
101
+ .description("Remove cc-router from system startup")
102
+ .action(async () => {
103
+ let removed = false;
104
+ try {
105
+ await execFileAsync("pm2", ["stop", "cc-router"]);
106
+ await execFileAsync("pm2", ["delete", "cc-router"]);
107
+ await execFileAsync("pm2", ["save"]);
108
+ console.log(chalk.green("✓ cc-router removed from PM2"));
109
+ removed = true;
110
+ }
111
+ catch {
112
+ console.log(chalk.gray("cc-router was not registered in PM2."));
113
+ }
114
+ // Remove Claude Code proxy config too
115
+ const { removeClaudeSettings } = await import("../utils/claude-config.js");
116
+ const { readClaudeProxySettings } = await import("../utils/claude-config.js");
117
+ if (readClaudeProxySettings().baseUrl) {
118
+ removeClaudeSettings();
119
+ console.log(chalk.green("✓ Removed proxy settings from ~/.claude/settings.json"));
120
+ removed = true;
121
+ }
122
+ if (removed) {
123
+ console.log(chalk.green("\n✓ Service uninstalled. Claude Code will use normal authentication.\n"));
124
+ }
125
+ else {
126
+ console.log(chalk.gray("\nNothing to uninstall.\n"));
127
+ }
128
+ });
129
+ service
130
+ .command("status")
131
+ .description("Show the service status in PM2")
132
+ .action(async () => {
133
+ try {
134
+ const { stdout } = await execFileAsync("pm2", ["info", "cc-router"]);
135
+ console.log(stdout);
136
+ }
137
+ catch {
138
+ console.log(chalk.yellow("cc-router is not registered as a PM2 service."));
139
+ console.log(chalk.gray(" Install it with: cc-router service install"));
140
+ }
141
+ });
142
+ service
143
+ .command("logs")
144
+ .description("Tail the proxy logs from PM2")
145
+ .option("--lines <n>", "Number of lines to show", "50")
146
+ .action(async (opts) => {
147
+ try {
148
+ // pm2 logs streams continuously — spawn it directly so it inherits stdio
149
+ const { spawn } = await import("child_process");
150
+ const child = spawn("pm2", ["logs", "cc-router", "--lines", opts.lines], {
151
+ stdio: "inherit",
152
+ });
153
+ child.on("error", () => {
154
+ console.log(chalk.yellow("PM2 not found. Is cc-router installed as a service?"));
155
+ });
156
+ }
157
+ catch {
158
+ console.error(chalk.red("Could not tail logs."));
159
+ }
160
+ });
161
+ }
162
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
163
+ async function getPm2Version() {
164
+ try {
165
+ const { stdout } = await execFileAsync("pm2", ["--version"]);
166
+ return stdout.trim();
167
+ }
168
+ catch {
169
+ return null;
170
+ }
171
+ }
172
+ function printServiceInfo() {
173
+ console.log(chalk.bold("\n━━━ Service installed ━━━━━━━━━━━━━━━━━━━━━━━━\n"));
174
+ console.log(` Check status: ${chalk.cyan("cc-router service status")}`);
175
+ console.log(` View logs: ${chalk.cyan("cc-router service logs")}`);
176
+ console.log(` Stop & remove: ${chalk.cyan("cc-router service uninstall")}`);
177
+ console.log(` Restart: ${chalk.cyan("pm2 restart cc-router")}`);
178
+ console.log();
179
+ }
180
+ function printManualStartupInstructions() {
181
+ const platform = detectPlatform();
182
+ console.log(chalk.gray("\n To configure auto-start manually:"));
183
+ if (platform === "macos") {
184
+ console.log(chalk.gray(" macOS (launchd): pm2 startup launchd && pm2 save"));
185
+ }
186
+ else if (platform === "linux") {
187
+ console.log(chalk.gray(" Linux (systemd): pm2 startup systemd && pm2 save"));
188
+ console.log(chalk.gray(" Then: sudo systemctl enable pm2-$(whoami)"));
189
+ }
190
+ else {
191
+ console.log(chalk.gray(" Windows: see https://github.com/jessety/pm2-installer"));
192
+ }
193
+ }