claude-code-router-config 1.0.0 → 1.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.
@@ -0,0 +1,352 @@
1
+ #!/usr/bin/env node
2
+
3
+ const express = require('express');
4
+ const cors = require('cors');
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+ const os = require('os');
8
+ const chalk = require('chalk');
9
+
10
+ class DashboardServer {
11
+ constructor(options = {}) {
12
+ this.port = options.port || 3457;
13
+ this.app = express();
14
+ this.setupMiddleware();
15
+ this.setupRoutes();
16
+ }
17
+
18
+ setupMiddleware() {
19
+ this.app.use(cors());
20
+ this.app.use(express.json());
21
+ this.app.use(express.static(path.join(__dirname, 'public')));
22
+ }
23
+
24
+ setupRoutes() {
25
+ // API Routes
26
+ this.app.get('/api/health', (req, res) => {
27
+ res.json({
28
+ success: true,
29
+ message: 'Claude Code Router Dashboard',
30
+ timestamp: new Date().toISOString()
31
+ });
32
+ });
33
+
34
+ this.app.get('/api/config', (req, res) => {
35
+ try {
36
+ const configPath = path.join(os.homedir(), '.claude-code-router', 'config.json');
37
+ if (fs.existsSync(configPath)) {
38
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
39
+ res.json({ success: true, data: config });
40
+ } else {
41
+ res.status(404).json({ success: false, error: 'Configuration not found' });
42
+ }
43
+ } catch (error) {
44
+ res.status(500).json({ success: false, error: error.message });
45
+ }
46
+ });
47
+
48
+ this.app.get('/api/providers', (req, res) => {
49
+ try {
50
+ const configPath = path.join(os.homedir(), '.claude-code-router', 'config.json');
51
+ if (fs.existsSync(configPath)) {
52
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
53
+ res.json({ success: true, data: config.Providers || [] });
54
+ } else {
55
+ res.status(404).json({ success: false, error: 'Providers not found' });
56
+ }
57
+ } catch (error) {
58
+ res.status(500).json({ success: false, error: error.message });
59
+ }
60
+ });
61
+
62
+ this.app.get('/api/analytics', (req, res) => {
63
+ try {
64
+ const analyticsPath = path.join(os.homedir(), '.claude-code-router', 'analytics', 'usage.json');
65
+ if (fs.existsSync(analyticsPath)) {
66
+ const analytics = JSON.parse(fs.readFileSync(analyticsPath, 'utf8'));
67
+ res.json({ success: true, data: analytics });
68
+ } else {
69
+ res.json({ success: true, data: { requests: [], daily: {}, monthly: {} } });
70
+ }
71
+ } catch (error) {
72
+ res.status(500).json({ success: false, error: error.message });
73
+ }
74
+ });
75
+
76
+ this.app.get('/api/status', (req, res) => {
77
+ const status = {
78
+ uptime: process.uptime(),
79
+ timestamp: new Date().toISOString(),
80
+ version: require('../package.json').version,
81
+ nodeVersion: process.version,
82
+ platform: os.platform(),
83
+ arch: os.arch()
84
+ };
85
+ res.json({ success: true, data: status });
86
+ });
87
+
88
+ // Serve the main dashboard HTML
89
+ this.app.get('/', (req, res) => {
90
+ res.sendFile(path.join(__dirname, 'public', 'index.html'));
91
+ });
92
+
93
+ // Catch all other routes
94
+ this.app.use((req, res) => {
95
+ res.status(404).json({
96
+ success: false,
97
+ error: 'Endpoint not found'
98
+ });
99
+ });
100
+ }
101
+
102
+ async start() {
103
+ return new Promise((resolve, reject) => {
104
+ try {
105
+ const server = this.app.listen(this.port, () => {
106
+ console.log(chalk.green(`🚀 Dashboard server started on port ${this.port}`));
107
+ console.log(chalk.blue(`📊 Open http://localhost:${this.port} to view dashboard`));
108
+
109
+ // Try to open browser
110
+ this.openBrowser().catch(() => {
111
+ console.log(chalk.yellow('Could not open browser automatically'));
112
+ });
113
+
114
+ resolve(server);
115
+ });
116
+
117
+ server.on('error', (error) => {
118
+ if (error.code === 'EADDRINUSE') {
119
+ reject(new Error(`Port ${this.port} is already in use`));
120
+ } else {
121
+ reject(error);
122
+ }
123
+ });
124
+ } catch (error) {
125
+ reject(error);
126
+ }
127
+ });
128
+ }
129
+
130
+ async openBrowser() {
131
+ const { spawn } = require('child_process');
132
+ const opener = process.platform === 'darwin' ? 'open' :
133
+ process.platform === 'win32' ? 'start' : 'xdg-open';
134
+
135
+ spawn(opener, [`http://localhost:${this.port}`], { detached: true }).unref();
136
+
137
+ return new Promise((resolve) => {
138
+ setTimeout(resolve, 1000);
139
+ });
140
+ }
141
+
142
+ async stop() {
143
+ if (this.server) {
144
+ return new Promise((resolve) => {
145
+ this.server.close(() => {
146
+ console.log(chalk.yellow('Dashboard server stopped'));
147
+ resolve();
148
+ });
149
+ });
150
+ }
151
+ }
152
+ }
153
+
154
+ // API routes for analytics and monitoring
155
+ class DashboardAPI {
156
+ static setupAnalyticsRoutes(app) {
157
+ const analyticsRouter = express.Router();
158
+
159
+ // Get analytics summary
160
+ analyticsRouter.get('/summary', async (req, res) => {
161
+ try {
162
+ const { getAnalyticsSummary } = require('../cli/analytics');
163
+ const summary = getAnalyticsSummary('week');
164
+ res.json({ success: true, data: summary });
165
+ } catch (error) {
166
+ res.status(500).json({ success: false, error: error.message });
167
+ }
168
+ });
169
+
170
+ // Get today's analytics
171
+ analyticsRouter.get('/today', async (req, res) => {
172
+ try {
173
+ const { getTodayAnalytics } = require('../cli/analytics');
174
+ const today = getTodayAnalytics();
175
+ res.json({ success: true, data: today });
176
+ } catch (error) {
177
+ res.status(500).json({ success: false, error: error.message });
178
+ }
179
+ });
180
+
181
+ // Export analytics
182
+ analyticsRouter.get('/export', async (req, res) => {
183
+ try {
184
+ const { exportAnalytics } = require('../cli/analytics');
185
+ const format = req.query.format || 'json';
186
+ const period = req.query.period || 'all';
187
+ const filepath = exportAnalytics(format, period);
188
+
189
+ res.download(filepath, (err) => {
190
+ if (err) {
191
+ res.status(500).json({ success: false, error: 'Export failed' });
192
+ } else {
193
+ // Clean up the temporary file
194
+ fs.unlink(filepath, () => {});
195
+ }
196
+ });
197
+ } catch (error) {
198
+ res.status(500).json({ success: false, error: error.message });
199
+ }
200
+ });
201
+
202
+ app.use('/api/analytics', analyticsRouter);
203
+ }
204
+
205
+ static setupHealthRoutes(app) {
206
+ const healthRouter = express.Router();
207
+
208
+ // Check provider health
209
+ healthRouter.get('/providers', async (req, res) => {
210
+ try {
211
+ const configPath = path.join(os.homedir(), '.claude-code-router', 'config.json');
212
+ if (fs.existsSync(configPath)) {
213
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
214
+ const providers = config.Providers || [];
215
+
216
+ const healthChecks = await Promise.all(
217
+ providers.map(async (provider) => {
218
+ try {
219
+ const { testProvider } = require('../cli/commands');
220
+ const isHealthy = await testProvider(provider.name);
221
+ return {
222
+ name: provider.name,
223
+ status: isHealthy ? 'healthy' : 'unhealthy',
224
+ models: provider.models,
225
+ lastChecked: new Date().toISOString()
226
+ };
227
+ } catch (error) {
228
+ return {
229
+ name: provider.name,
230
+ status: 'error',
231
+ error: error.message,
232
+ lastChecked: new Date().toISOString()
233
+ };
234
+ }
235
+ })
236
+ );
237
+
238
+ res.json({ success: true, data: healthChecks });
239
+ } else {
240
+ res.status(404).json({ success: false, error: 'Configuration not found' });
241
+ }
242
+ } catch (error) {
243
+ res.status(500).json({ success: false, error: error.message });
244
+ }
245
+ });
246
+
247
+ // System health
248
+ healthRouter.get('/system', async (req, res) => {
249
+ try {
250
+ const health = {
251
+ uptime: process.uptime(),
252
+ memory: process.memoryUsage(),
253
+ cpu: process.cpuUsage(),
254
+ platform: os.platform(),
255
+ nodeVersion: process.version,
256
+ timestamp: new Date().toISOString()
257
+ };
258
+
259
+ res.json({ success: true, data: health });
260
+ } catch (error) {
261
+ res.status(500).json({ success: false, error: error.message });
262
+ }
263
+ });
264
+
265
+ app.use('/api/health', healthRouter);
266
+ }
267
+
268
+ static setupConfigRoutes(app) {
269
+ const configRouter = express.Router();
270
+
271
+ // Get current configuration
272
+ configRouter.get('/current', async (req, res) => {
273
+ try {
274
+ const configPath = path.join(os.homedir(), '.claude-code-router', 'config.json');
275
+ if (fs.existsSync(configPath)) {
276
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
277
+ res.json({ success: true, data: config });
278
+ } else {
279
+ res.status(404).json({ success: false, error: 'Configuration not found' });
280
+ }
281
+ } catch (error) {
282
+ res.status(500).json({ success: false, error: error.message });
283
+ }
284
+ });
285
+
286
+ // Get available templates
287
+ configRouter.get('/templates', async (req, res) => {
288
+ try {
289
+ const templatesDir = path.join(__dirname, '../templates');
290
+ const templates = [];
291
+
292
+ if (fs.existsSync(templatesDir)) {
293
+ const files = fs.readdirSync(templatesDir).filter(f => f.endsWith('.json'));
294
+
295
+ for (const file of files) {
296
+ const templatePath = path.join(templatesDir, file);
297
+ const template = JSON.parse(fs.readFileSync(templatePath, 'utf8'));
298
+ templates.push({
299
+ name: file.replace('.json', ''),
300
+ ...template
301
+ });
302
+ }
303
+ }
304
+
305
+ res.json({ success: true, data: templates });
306
+ } catch (error) {
307
+ res.status(500).json({ success: false, error: error.message });
308
+ }
309
+ });
310
+
311
+ app.use('/api/config', configRouter);
312
+ }
313
+ }
314
+
315
+ // CLI function to start dashboard
316
+ async function startDashboard(options = {}) {
317
+ const dashboard = new DashboardServer(options);
318
+
319
+ // Setup additional API routes
320
+ DashboardAPI.setupAnalyticsRoutes(dashboard.app);
321
+ DashboardAPI.setupHealthRoutes(dashboard.app);
322
+ DashboardAPI.setupConfigRoutes(dashboard.app);
323
+
324
+ try {
325
+ const server = await dashboard.start();
326
+ return server;
327
+ } catch (error) {
328
+ console.error(chalk.red('Failed to start dashboard:'), error.message);
329
+ process.exit(1);
330
+ }
331
+ }
332
+
333
+ // Export for use in other modules
334
+ module.exports = {
335
+ DashboardServer,
336
+ DashboardAPI,
337
+ startDashboard
338
+ };
339
+
340
+ // Run server if called directly
341
+ if (require.main === module) {
342
+ const args = process.argv.slice(2);
343
+ const options = {};
344
+
345
+ // Parse command line arguments
346
+ const portIndex = args.indexOf('--port');
347
+ if (portIndex !== -1 && args[portIndex + 1]) {
348
+ options.port = parseInt(args[portIndex + 1]);
349
+ }
350
+
351
+ startDashboard(options);
352
+ }