@appkit/llamacpp-cli 1.7.0 → 1.9.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 (51) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +84 -0
  3. package/dist/cli.js +80 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/config.d.ts +1 -0
  6. package/dist/commands/config.d.ts.map +1 -1
  7. package/dist/commands/config.js +167 -12
  8. package/dist/commands/config.js.map +1 -1
  9. package/dist/commands/router/config.d.ts +10 -0
  10. package/dist/commands/router/config.d.ts.map +1 -0
  11. package/dist/commands/router/config.js +95 -0
  12. package/dist/commands/router/config.js.map +1 -0
  13. package/dist/commands/router/restart.d.ts +2 -0
  14. package/dist/commands/router/restart.d.ts.map +1 -0
  15. package/dist/commands/router/restart.js +39 -0
  16. package/dist/commands/router/restart.js.map +1 -0
  17. package/dist/commands/router/start.d.ts +2 -0
  18. package/dist/commands/router/start.d.ts.map +1 -0
  19. package/dist/commands/router/start.js +60 -0
  20. package/dist/commands/router/start.js.map +1 -0
  21. package/dist/commands/router/status.d.ts +2 -0
  22. package/dist/commands/router/status.d.ts.map +1 -0
  23. package/dist/commands/router/status.js +116 -0
  24. package/dist/commands/router/status.js.map +1 -0
  25. package/dist/commands/router/stop.d.ts +2 -0
  26. package/dist/commands/router/stop.d.ts.map +1 -0
  27. package/dist/commands/router/stop.js +36 -0
  28. package/dist/commands/router/stop.js.map +1 -0
  29. package/dist/lib/router-manager.d.ts +103 -0
  30. package/dist/lib/router-manager.d.ts.map +1 -0
  31. package/dist/lib/router-manager.js +393 -0
  32. package/dist/lib/router-manager.js.map +1 -0
  33. package/dist/lib/router-server.d.ts +52 -0
  34. package/dist/lib/router-server.d.ts.map +1 -0
  35. package/dist/lib/router-server.js +373 -0
  36. package/dist/lib/router-server.js.map +1 -0
  37. package/dist/types/router-config.d.ts +18 -0
  38. package/dist/types/router-config.d.ts.map +1 -0
  39. package/dist/types/router-config.js +3 -0
  40. package/dist/types/router-config.js.map +1 -0
  41. package/package.json +1 -1
  42. package/src/cli.ts +81 -0
  43. package/src/commands/config.ts +146 -14
  44. package/src/commands/router/config.ts +109 -0
  45. package/src/commands/router/restart.ts +36 -0
  46. package/src/commands/router/start.ts +60 -0
  47. package/src/commands/router/status.ts +119 -0
  48. package/src/commands/router/stop.ts +33 -0
  49. package/src/lib/router-manager.ts +413 -0
  50. package/src/lib/router-server.ts +407 -0
  51. package/src/types/router-config.ts +24 -0
@@ -0,0 +1,373 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.RouterServer = void 0;
38
+ const http = __importStar(require("http"));
39
+ const https = __importStar(require("https"));
40
+ const url_1 = require("url");
41
+ const fs = __importStar(require("fs/promises"));
42
+ const path = __importStar(require("path"));
43
+ const file_utils_1 = require("../utils/file-utils");
44
+ /**
45
+ * Router HTTP server - proxies requests to backend llama.cpp servers
46
+ */
47
+ class RouterServer {
48
+ async initialize() {
49
+ // Load router config
50
+ const configPath = path.join((0, file_utils_1.getConfigDir)(), 'router.json');
51
+ if (!(await (0, file_utils_1.fileExists)(configPath))) {
52
+ throw new Error('Router configuration not found');
53
+ }
54
+ this.config = await (0, file_utils_1.readJson)(configPath);
55
+ // Create HTTP server
56
+ this.server = http.createServer(async (req, res) => {
57
+ await this.handleRequest(req, res);
58
+ });
59
+ // Graceful shutdown
60
+ process.on('SIGTERM', async () => {
61
+ console.error('[Router] Received SIGTERM, shutting down gracefully...');
62
+ this.server.close(() => {
63
+ console.error('[Router] Server closed');
64
+ process.exit(0);
65
+ });
66
+ });
67
+ process.on('SIGINT', async () => {
68
+ console.error('[Router] Received SIGINT, shutting down gracefully...');
69
+ this.server.close(() => {
70
+ console.error('[Router] Server closed');
71
+ process.exit(0);
72
+ });
73
+ });
74
+ }
75
+ async start() {
76
+ await this.initialize();
77
+ this.server.listen(this.config.port, this.config.host, () => {
78
+ console.error(`[Router] Listening on http://${this.config.host}:${this.config.port}`);
79
+ console.error(`[Router] PID: ${process.pid}`);
80
+ });
81
+ }
82
+ /**
83
+ * Main request handler
84
+ */
85
+ async handleRequest(req, res) {
86
+ // Log request
87
+ console.error(`[Router] ${req.method} ${req.url}`);
88
+ // CORS headers
89
+ res.setHeader('Access-Control-Allow-Origin', '*');
90
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
91
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
92
+ // Handle OPTIONS preflight
93
+ if (req.method === 'OPTIONS') {
94
+ res.writeHead(200);
95
+ res.end();
96
+ return;
97
+ }
98
+ try {
99
+ // Route based on path
100
+ if (req.url === '/health' && req.method === 'GET') {
101
+ await this.handleHealth(req, res);
102
+ }
103
+ else if (req.url === '/v1/models' && req.method === 'GET') {
104
+ await this.handleModels(req, res);
105
+ }
106
+ else if (req.url === '/v1/chat/completions' && req.method === 'POST') {
107
+ await this.handleChatCompletions(req, res);
108
+ }
109
+ else if (req.url === '/v1/embeddings' && req.method === 'POST') {
110
+ await this.handleEmbeddings(req, res);
111
+ }
112
+ else {
113
+ this.sendError(res, 404, 'Not Found', `Unknown endpoint: ${req.url}`);
114
+ }
115
+ }
116
+ catch (error) {
117
+ console.error('[Router] Error handling request:', error);
118
+ this.sendError(res, 500, 'Internal Server Error', error.message);
119
+ }
120
+ }
121
+ /**
122
+ * Health check endpoint
123
+ */
124
+ async handleHealth(req, res) {
125
+ res.writeHead(200, { 'Content-Type': 'application/json' });
126
+ res.end(JSON.stringify({
127
+ status: 'healthy',
128
+ uptime: process.uptime(),
129
+ timestamp: new Date().toISOString(),
130
+ }));
131
+ }
132
+ /**
133
+ * List models endpoint - aggregate from all running servers
134
+ */
135
+ async handleModels(req, res) {
136
+ const servers = await this.getAllServers();
137
+ const runningServers = servers.filter(s => s.status === 'running');
138
+ const models = runningServers.map(server => ({
139
+ id: server.modelName,
140
+ object: 'model',
141
+ created: Math.floor(new Date(server.createdAt).getTime() / 1000),
142
+ owned_by: 'llamacpp',
143
+ }));
144
+ const response = {
145
+ object: 'list',
146
+ data: models,
147
+ };
148
+ res.writeHead(200, { 'Content-Type': 'application/json' });
149
+ res.end(JSON.stringify(response));
150
+ }
151
+ /**
152
+ * Chat completions endpoint - route to backend server
153
+ */
154
+ async handleChatCompletions(req, res) {
155
+ // Parse request body
156
+ const body = await this.readBody(req);
157
+ let requestData;
158
+ try {
159
+ requestData = JSON.parse(body);
160
+ }
161
+ catch (error) {
162
+ this.sendError(res, 400, 'Bad Request', 'Invalid JSON in request body');
163
+ return;
164
+ }
165
+ // Extract model name
166
+ const modelName = requestData.model;
167
+ if (!modelName) {
168
+ this.sendError(res, 400, 'Bad Request', 'Missing "model" field in request');
169
+ return;
170
+ }
171
+ // Find server for model
172
+ const server = await this.findServerForModel(modelName);
173
+ if (!server) {
174
+ this.sendError(res, 404, 'Not Found', `No server found for model: ${modelName}`);
175
+ return;
176
+ }
177
+ if (server.status !== 'running') {
178
+ this.sendError(res, 503, 'Service Unavailable', `Server for model "${modelName}" is not running`);
179
+ return;
180
+ }
181
+ // Proxy request to backend
182
+ const backendUrl = `http://${server.host}:${server.port}/v1/chat/completions`;
183
+ await this.proxyRequest(backendUrl, requestData, req, res);
184
+ }
185
+ /**
186
+ * Embeddings endpoint - route to backend server
187
+ */
188
+ async handleEmbeddings(req, res) {
189
+ // Parse request body
190
+ const body = await this.readBody(req);
191
+ let requestData;
192
+ try {
193
+ requestData = JSON.parse(body);
194
+ }
195
+ catch (error) {
196
+ this.sendError(res, 400, 'Bad Request', 'Invalid JSON in request body');
197
+ return;
198
+ }
199
+ // Extract model name
200
+ const modelName = requestData.model;
201
+ if (!modelName) {
202
+ this.sendError(res, 400, 'Bad Request', 'Missing "model" field in request');
203
+ return;
204
+ }
205
+ // Find server for model
206
+ const server = await this.findServerForModel(modelName);
207
+ if (!server) {
208
+ this.sendError(res, 404, 'Not Found', `No server found for model: ${modelName}`);
209
+ return;
210
+ }
211
+ if (server.status !== 'running') {
212
+ this.sendError(res, 503, 'Service Unavailable', `Server for model "${modelName}" is not running`);
213
+ return;
214
+ }
215
+ // Check if server has embeddings enabled
216
+ if (!server.embeddings) {
217
+ this.sendError(res, 400, 'Bad Request', `Server for model "${modelName}" does not have embeddings enabled`);
218
+ return;
219
+ }
220
+ // Proxy request to backend
221
+ const backendUrl = `http://${server.host}:${server.port}/v1/embeddings`;
222
+ await this.proxyRequest(backendUrl, requestData, req, res);
223
+ }
224
+ /**
225
+ * Proxy a request to a backend server
226
+ */
227
+ async proxyRequest(backendUrl, requestData, originalReq, res) {
228
+ const url = new url_1.URL(backendUrl);
229
+ const isHttps = url.protocol === 'https:';
230
+ const httpModule = isHttps ? https : http;
231
+ const requestBody = JSON.stringify(requestData);
232
+ const options = {
233
+ hostname: url.hostname,
234
+ port: url.port || (isHttps ? 443 : 80),
235
+ path: url.pathname + url.search,
236
+ method: 'POST',
237
+ headers: {
238
+ 'Content-Type': 'application/json',
239
+ 'Content-Length': Buffer.byteLength(requestBody),
240
+ },
241
+ timeout: this.config.requestTimeout,
242
+ };
243
+ return new Promise((resolve, reject) => {
244
+ const proxyReq = httpModule.request(options, (proxyRes) => {
245
+ // Forward status and headers
246
+ res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);
247
+ // Stream response
248
+ proxyRes.pipe(res);
249
+ proxyRes.on('end', () => {
250
+ resolve();
251
+ });
252
+ });
253
+ proxyReq.on('error', (error) => {
254
+ console.error('[Router] Proxy request failed:', error);
255
+ if (!res.headersSent) {
256
+ this.sendError(res, 502, 'Bad Gateway', 'Failed to connect to backend server');
257
+ }
258
+ reject(error);
259
+ });
260
+ proxyReq.on('timeout', () => {
261
+ console.error('[Router] Proxy request timed out');
262
+ proxyReq.destroy();
263
+ if (!res.headersSent) {
264
+ this.sendError(res, 504, 'Gateway Timeout', 'Backend server did not respond in time');
265
+ }
266
+ reject(new Error('Request timeout'));
267
+ });
268
+ // Send request body
269
+ proxyReq.write(requestBody);
270
+ proxyReq.end();
271
+ });
272
+ }
273
+ /**
274
+ * Read request body as string
275
+ */
276
+ async readBody(req) {
277
+ return new Promise((resolve, reject) => {
278
+ const chunks = [];
279
+ req.on('data', (chunk) => chunks.push(chunk));
280
+ req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
281
+ req.on('error', reject);
282
+ });
283
+ }
284
+ /**
285
+ * Send error response
286
+ */
287
+ sendError(res, statusCode, error, details) {
288
+ if (res.headersSent)
289
+ return;
290
+ const response = { error };
291
+ if (details)
292
+ response.details = details;
293
+ res.writeHead(statusCode, { 'Content-Type': 'application/json' });
294
+ res.end(JSON.stringify(response));
295
+ }
296
+ /**
297
+ * Get all server configurations
298
+ */
299
+ async getAllServers() {
300
+ const serversDir = (0, file_utils_1.getServersDir)();
301
+ try {
302
+ const files = await fs.readdir(serversDir);
303
+ const configFiles = files.filter(f => f.endsWith('.json'));
304
+ const servers = [];
305
+ for (const file of configFiles) {
306
+ const filePath = path.join(serversDir, file);
307
+ try {
308
+ const config = await (0, file_utils_1.readJson)(filePath);
309
+ servers.push(config);
310
+ }
311
+ catch (error) {
312
+ console.error(`[Router] Failed to load server config ${file}:`, error);
313
+ }
314
+ }
315
+ return servers;
316
+ }
317
+ catch (error) {
318
+ console.error('[Router] Failed to read servers directory:', error);
319
+ return [];
320
+ }
321
+ }
322
+ /**
323
+ * Find a server by model name
324
+ */
325
+ async findServerForModel(modelName) {
326
+ const servers = await this.getAllServers();
327
+ // Normalize a model name for flexible matching (lowercase, no extension, normalize separators)
328
+ const normalize = (name) => {
329
+ return name
330
+ .toLowerCase()
331
+ .replace(/\.gguf$/i, '')
332
+ .replace(/[_-]/g, '-'); // Normalize underscores and hyphens to hyphens
333
+ };
334
+ const normalizedRequest = normalize(modelName);
335
+ // Try exact match first
336
+ const exactMatch = servers.find(s => s.modelName === modelName);
337
+ if (exactMatch)
338
+ return exactMatch;
339
+ // Try case-insensitive match
340
+ const caseInsensitiveMatch = servers.find(s => s.modelName.toLowerCase() === modelName.toLowerCase());
341
+ if (caseInsensitiveMatch)
342
+ return caseInsensitiveMatch;
343
+ // Try adding .gguf extension if not present
344
+ if (!modelName.endsWith('.gguf')) {
345
+ const withExtension = modelName + '.gguf';
346
+ const extensionMatch = servers.find(s => s.modelName.toLowerCase() === withExtension.toLowerCase());
347
+ if (extensionMatch)
348
+ return extensionMatch;
349
+ }
350
+ // Try normalized matching (handles case, extension, and underscore/hyphen variations)
351
+ const normalizedMatch = servers.find(s => normalize(s.modelName) === normalizedRequest);
352
+ if (normalizedMatch)
353
+ return normalizedMatch;
354
+ return null;
355
+ }
356
+ }
357
+ exports.RouterServer = RouterServer;
358
+ // Main entry point
359
+ async function main() {
360
+ try {
361
+ const server = new RouterServer();
362
+ await server.start();
363
+ }
364
+ catch (error) {
365
+ console.error('[Router] Failed to start:', error);
366
+ process.exit(1);
367
+ }
368
+ }
369
+ // Only run if this is the main module
370
+ if (require.main === module) {
371
+ main();
372
+ }
373
+ //# sourceMappingURL=router-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router-server.js","sourceRoot":"","sources":["../../src/lib/router-server.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,2CAA6B;AAC7B,6CAA+B;AAC/B,6BAA0B;AAC1B,gDAAkC;AAClC,2CAA6B;AAG7B,oDAAwF;AAmBxF;;GAEG;AACH,MAAM,YAAY;IAIhB,KAAK,CAAC,UAAU;QACd,qBAAqB;QACrB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAA,yBAAY,GAAE,EAAE,aAAa,CAAC,CAAC;QAC5D,IAAI,CAAC,CAAC,MAAM,IAAA,uBAAU,EAAC,UAAU,CAAC,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,MAAM,IAAA,qBAAQ,EAAe,UAAU,CAAC,CAAC;QAEvD,qBAAqB;QACrB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;YACjD,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,oBAAoB;QACpB,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;YAC/B,OAAO,CAAC,KAAK,CAAC,wDAAwD,CAAC,CAAC;YACxE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;gBACrB,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;gBACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;YAC9B,OAAO,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;YACvE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;gBACrB,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;gBACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QAExB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;YAC1D,OAAO,CAAC,KAAK,CAAC,gCAAgC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YACtF,OAAO,CAAC,KAAK,CAAC,iBAAiB,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,aAAa,CAAC,GAAyB,EAAE,GAAwB;QAC7E,cAAc;QACd,OAAO,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;QAEnD,eAAe;QACf,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;QAClD,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,oBAAoB,CAAC,CAAC;QACpE,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,6BAA6B,CAAC,CAAC;QAE7E,2BAA2B;QAC3B,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7B,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACnB,GAAG,CAAC,GAAG,EAAE,CAAC;YACV,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,sBAAsB;YACtB,IAAI,GAAG,CAAC,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;gBAClD,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YACpC,CAAC;iBAAM,IAAI,GAAG,CAAC,GAAG,KAAK,YAAY,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;gBAC5D,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YACpC,CAAC;iBAAM,IAAI,GAAG,CAAC,GAAG,KAAK,sBAAsB,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBACvE,MAAM,IAAI,CAAC,qBAAqB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAC7C,CAAC;iBAAM,IAAI,GAAG,CAAC,GAAG,KAAK,gBAAgB,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBACjE,MAAM,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YACxC,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,qBAAqB,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;YACxE,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,KAAK,CAAC,CAAC;YACzD,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,uBAAuB,EAAG,KAAe,CAAC,OAAO,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,YAAY,CAAC,GAAyB,EAAE,GAAwB;QAC5E,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;YACrB,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE;YACxB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,YAAY,CAAC,GAAyB,EAAE,GAAwB;QAC5E,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QAC3C,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;QAEnE,MAAM,MAAM,GAAgB,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACxD,EAAE,EAAE,MAAM,CAAC,SAAS;YACpB,MAAM,EAAE,OAAO;YACf,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;YAChE,QAAQ,EAAE,UAAU;SACrB,CAAC,CAAC,CAAC;QAEJ,MAAM,QAAQ,GAAmB;YAC/B,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,MAAM;SACb,CAAC;QAEF,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpC,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,qBAAqB,CAAC,GAAyB,EAAE,GAAwB;QACrF,qBAAqB;QACrB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,WAAgB,CAAC;QACrB,IAAI,CAAC;YACH,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,aAAa,EAAE,8BAA8B,CAAC,CAAC;YACxE,OAAO;QACT,CAAC;QAED,qBAAqB;QACrB,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC;QACpC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,aAAa,EAAE,kCAAkC,CAAC,CAAC;YAC5E,OAAO;QACT,CAAC;QAED,wBAAwB;QACxB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,8BAA8B,SAAS,EAAE,CAAC,CAAC;YACjF,OAAO;QACT,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAChC,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,qBAAqB,EAAE,qBAAqB,SAAS,kBAAkB,CAAC,CAAC;YAClG,OAAO;QACT,CAAC;QAED,2BAA2B;QAC3B,MAAM,UAAU,GAAG,UAAU,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,sBAAsB,CAAC;QAC9E,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC7D,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,gBAAgB,CAAC,GAAyB,EAAE,GAAwB;QAChF,qBAAqB;QACrB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,WAAgB,CAAC;QACrB,IAAI,CAAC;YACH,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,aAAa,EAAE,8BAA8B,CAAC,CAAC;YACxE,OAAO;QACT,CAAC;QAED,qBAAqB;QACrB,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC;QACpC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,aAAa,EAAE,kCAAkC,CAAC,CAAC;YAC5E,OAAO;QACT,CAAC;QAED,wBAAwB;QACxB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,8BAA8B,SAAS,EAAE,CAAC,CAAC;YACjF,OAAO;QACT,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAChC,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,qBAAqB,EAAE,qBAAqB,SAAS,kBAAkB,CAAC,CAAC;YAClG,OAAO;QACT,CAAC;QAED,yCAAyC;QACzC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YACvB,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,aAAa,EAAE,qBAAqB,SAAS,oCAAoC,CAAC,CAAC;YAC5G,OAAO;QACT,CAAC;QAED,2BAA2B;QAC3B,MAAM,UAAU,GAAG,UAAU,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,gBAAgB,CAAC;QACxE,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC7D,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,YAAY,CACxB,UAAkB,EAClB,WAAgB,EAChB,WAAiC,EACjC,GAAwB;QAExB,MAAM,GAAG,GAAG,IAAI,SAAG,CAAC,UAAU,CAAC,CAAC;QAChC,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC;QAC1C,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;QAE1C,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAEhD,MAAM,OAAO,GAAwB;YACnC,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACtC,IAAI,EAAE,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM;YAC/B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC;aACjD;YACD,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,cAAc;SACpC,CAAC;QAEF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE;gBACxD,6BAA6B;gBAC7B,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,IAAI,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;gBAE5D,kBAAkB;gBAClB,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAEnB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;oBACtB,OAAO,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBAC7B,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAC;gBACvD,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;oBACrB,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,aAAa,EAAE,qCAAqC,CAAC,CAAC;gBACjF,CAAC;gBACD,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC,CAAC,CAAC;YAEH,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;gBAC1B,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;gBAClD,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACnB,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;oBACrB,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,iBAAiB,EAAE,wCAAwC,CAAC,CAAC;gBACxF,CAAC;gBACD,MAAM,CAAC,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;YACvC,CAAC,CAAC,CAAC;YAEH,oBAAoB;YACpB,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAC5B,QAAQ,CAAC,GAAG,EAAE,CAAC;QACjB,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,QAAQ,CAAC,GAAyB;QAC9C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YACtD,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACtE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,SAAS,CAAC,GAAwB,EAAE,UAAkB,EAAE,KAAa,EAAE,OAAgB;QAC7F,IAAI,GAAG,CAAC,WAAW;YAAE,OAAO;QAE5B,MAAM,QAAQ,GAAkB,EAAE,KAAK,EAAE,CAAC;QAC1C,IAAI,OAAO;YAAE,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAC;QAExC,GAAG,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAClE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpC,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,aAAa;QACzB,MAAM,UAAU,GAAG,IAAA,0BAAa,GAAE,CAAC;QACnC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC3C,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;YAE3D,MAAM,OAAO,GAAmB,EAAE,CAAC;YACnC,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;gBAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;gBAC7C,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAQ,EAAe,QAAQ,CAAC,CAAC;oBACtD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACvB,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,KAAK,CAAC,yCAAyC,IAAI,GAAG,EAAE,KAAK,CAAC,CAAC;gBACzE,CAAC;YACH,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,4CAA4C,EAAE,KAAK,CAAC,CAAC;YACnE,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,kBAAkB,CAAC,SAAiB;QAChD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QAE3C,+FAA+F;QAC/F,MAAM,SAAS,GAAG,CAAC,IAAY,EAAU,EAAE;YACzC,OAAO,IAAI;iBACR,WAAW,EAAE;iBACb,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;iBACvB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAE,+CAA+C;QAC5E,CAAC,CAAC;QAEF,MAAM,iBAAiB,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;QAE/C,wBAAwB;QACxB,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC;QAChE,IAAI,UAAU;YAAE,OAAO,UAAU,CAAC;QAElC,6BAA6B;QAC7B,MAAM,oBAAoB,GAAG,OAAO,CAAC,IAAI,CACvC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,WAAW,EAAE,CAC3D,CAAC;QACF,IAAI,oBAAoB;YAAE,OAAO,oBAAoB,CAAC;QAEtD,4CAA4C;QAC5C,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACjC,MAAM,aAAa,GAAG,SAAS,GAAG,OAAO,CAAC;YAC1C,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CACjC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,aAAa,CAAC,WAAW,EAAE,CAC/D,CAAC;YACF,IAAI,cAAc;gBAAE,OAAO,cAAc,CAAC;QAC5C,CAAC;QAED,sFAAsF;QACtF,MAAM,eAAe,GAAG,OAAO,CAAC,IAAI,CAClC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,iBAAiB,CAClD,CAAC;QACF,IAAI,eAAe;YAAE,OAAO,eAAe,CAAC;QAE5C,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAkBQ,oCAAY;AAhBrB,mBAAmB;AACnB,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAClC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;QAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,sCAAsC;AACtC,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IAC5B,IAAI,EAAE,CAAC;AACT,CAAC"}
@@ -0,0 +1,18 @@
1
+ export type RouterStatus = 'running' | 'stopped' | 'crashed';
2
+ export interface RouterConfig {
3
+ id: 'router';
4
+ port: number;
5
+ host: string;
6
+ status: RouterStatus;
7
+ pid?: number;
8
+ createdAt: string;
9
+ lastStarted?: string;
10
+ lastStopped?: string;
11
+ plistPath: string;
12
+ label: 'com.llama.router';
13
+ stdoutPath: string;
14
+ stderrPath: string;
15
+ healthCheckInterval: number;
16
+ requestTimeout: number;
17
+ }
18
+ //# sourceMappingURL=router-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router-config.d.ts","sourceRoot":"","sources":["../../src/types/router-config.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAE7D,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,QAAQ,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IAGb,MAAM,EAAE,YAAY,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IAGnB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;CACxB"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=router-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router-config.js","sourceRoot":"","sources":["../../src/types/router-config.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appkit/llamacpp-cli",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "CLI tool to manage local llama.cpp servers on macOS",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
package/src/cli.ts CHANGED
@@ -19,6 +19,11 @@ import { serverShowCommand } from './commands/server-show';
19
19
  import { serverConfigCommand } from './commands/config';
20
20
  import { configGlobalCommand } from './commands/config-global';
21
21
  import { monitorCommand } from './commands/monitor';
22
+ import { routerStartCommand } from './commands/router/start';
23
+ import { routerStopCommand } from './commands/router/stop';
24
+ import { routerStatusCommand } from './commands/router/status';
25
+ import { routerRestartCommand } from './commands/router/restart';
26
+ import { routerConfigCommand } from './commands/router/config';
22
27
  import packageJson from '../package.json';
23
28
 
24
29
  const program = new Command();
@@ -193,6 +198,7 @@ server
193
198
  .command('config')
194
199
  .description('Update server configuration parameters')
195
200
  .argument('<identifier>', 'Server identifier: port (9000), server ID (llama-3-2-3b), or partial model name')
201
+ .option('-m, --model <filename>', 'Update model (filename or path)')
196
202
  .option('-h, --host <address>', 'Update bind address (127.0.0.1 for localhost, 0.0.0.0 for remote access)')
197
203
  .option('-t, --threads <number>', 'Update thread count', parseInt)
198
204
  .option('-c, --ctx-size <number>', 'Update context size', parseInt)
@@ -307,5 +313,80 @@ server
307
313
  }
308
314
  });
309
315
 
316
+ // Router management commands
317
+ const router = program
318
+ .command('router')
319
+ .description('Manage the unified router endpoint');
320
+
321
+ // Start router
322
+ router
323
+ .command('start')
324
+ .description('Start the router service')
325
+ .action(async () => {
326
+ try {
327
+ await routerStartCommand();
328
+ } catch (error) {
329
+ console.error(chalk.red('❌ Error:'), (error as Error).message);
330
+ process.exit(1);
331
+ }
332
+ });
333
+
334
+ // Stop router
335
+ router
336
+ .command('stop')
337
+ .description('Stop the router service')
338
+ .action(async () => {
339
+ try {
340
+ await routerStopCommand();
341
+ } catch (error) {
342
+ console.error(chalk.red('❌ Error:'), (error as Error).message);
343
+ process.exit(1);
344
+ }
345
+ });
346
+
347
+ // Show router status
348
+ router
349
+ .command('status')
350
+ .description('Show router status and configuration')
351
+ .action(async () => {
352
+ try {
353
+ await routerStatusCommand();
354
+ } catch (error) {
355
+ console.error(chalk.red('❌ Error:'), (error as Error).message);
356
+ process.exit(1);
357
+ }
358
+ });
359
+
360
+ // Restart router
361
+ router
362
+ .command('restart')
363
+ .description('Restart the router service')
364
+ .action(async () => {
365
+ try {
366
+ await routerRestartCommand();
367
+ } catch (error) {
368
+ console.error(chalk.red('❌ Error:'), (error as Error).message);
369
+ process.exit(1);
370
+ }
371
+ });
372
+
373
+ // Configure router
374
+ router
375
+ .command('config')
376
+ .description('Update router configuration')
377
+ .option('-p, --port <number>', 'Update port number', parseInt)
378
+ .option('-h, --host <address>', 'Update bind address')
379
+ .option('--timeout <ms>', 'Update request timeout (milliseconds)', parseInt)
380
+ .option('--health-interval <ms>', 'Update health check interval (milliseconds)', parseInt)
381
+ .option('-r, --restart', 'Automatically restart router if running')
382
+ .action(async (options) => {
383
+ try {
384
+ await routerConfigCommand(options);
385
+ } catch (error) {
386
+ console.error(chalk.red('❌ Error:'), (error as Error).message);
387
+ process.exit(1);
388
+ }
389
+ });
390
+
310
391
  // Parse arguments
311
392
  program.parse();