@appkit/llamacpp-cli 1.11.0 → 1.12.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 (105) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +356 -3
  3. package/dist/cli.js +99 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/admin/config.d.ts +10 -0
  6. package/dist/commands/admin/config.d.ts.map +1 -0
  7. package/dist/commands/admin/config.js +100 -0
  8. package/dist/commands/admin/config.js.map +1 -0
  9. package/dist/commands/admin/logs.d.ts +10 -0
  10. package/dist/commands/admin/logs.d.ts.map +1 -0
  11. package/dist/commands/admin/logs.js +114 -0
  12. package/dist/commands/admin/logs.js.map +1 -0
  13. package/dist/commands/admin/restart.d.ts +2 -0
  14. package/dist/commands/admin/restart.d.ts.map +1 -0
  15. package/dist/commands/admin/restart.js +29 -0
  16. package/dist/commands/admin/restart.js.map +1 -0
  17. package/dist/commands/admin/start.d.ts +2 -0
  18. package/dist/commands/admin/start.d.ts.map +1 -0
  19. package/dist/commands/admin/start.js +30 -0
  20. package/dist/commands/admin/start.js.map +1 -0
  21. package/dist/commands/admin/status.d.ts +2 -0
  22. package/dist/commands/admin/status.d.ts.map +1 -0
  23. package/dist/commands/admin/status.js +82 -0
  24. package/dist/commands/admin/status.js.map +1 -0
  25. package/dist/commands/admin/stop.d.ts +2 -0
  26. package/dist/commands/admin/stop.d.ts.map +1 -0
  27. package/dist/commands/admin/stop.js +21 -0
  28. package/dist/commands/admin/stop.js.map +1 -0
  29. package/dist/commands/logs.d.ts +1 -0
  30. package/dist/commands/logs.d.ts.map +1 -1
  31. package/dist/commands/logs.js +22 -0
  32. package/dist/commands/logs.js.map +1 -1
  33. package/dist/lib/admin-manager.d.ts +111 -0
  34. package/dist/lib/admin-manager.d.ts.map +1 -0
  35. package/dist/lib/admin-manager.js +413 -0
  36. package/dist/lib/admin-manager.js.map +1 -0
  37. package/dist/lib/admin-server.d.ts +148 -0
  38. package/dist/lib/admin-server.d.ts.map +1 -0
  39. package/dist/lib/admin-server.js +1161 -0
  40. package/dist/lib/admin-server.js.map +1 -0
  41. package/dist/lib/download-job-manager.d.ts +64 -0
  42. package/dist/lib/download-job-manager.d.ts.map +1 -0
  43. package/dist/lib/download-job-manager.js +164 -0
  44. package/dist/lib/download-job-manager.js.map +1 -0
  45. package/dist/tui/MultiServerMonitorApp.js +1 -1
  46. package/dist/types/admin-config.d.ts +19 -0
  47. package/dist/types/admin-config.d.ts.map +1 -0
  48. package/dist/types/admin-config.js +3 -0
  49. package/dist/types/admin-config.js.map +1 -0
  50. package/dist/utils/log-parser.d.ts +9 -0
  51. package/dist/utils/log-parser.d.ts.map +1 -1
  52. package/dist/utils/log-parser.js +11 -0
  53. package/dist/utils/log-parser.js.map +1 -1
  54. package/docs/images/web-ui-servers.png +0 -0
  55. package/package.json +1 -1
  56. package/src/cli.ts +100 -0
  57. package/src/commands/admin/config.ts +121 -0
  58. package/src/commands/admin/logs.ts +91 -0
  59. package/src/commands/admin/restart.ts +26 -0
  60. package/src/commands/admin/start.ts +27 -0
  61. package/src/commands/admin/status.ts +84 -0
  62. package/src/commands/admin/stop.ts +16 -0
  63. package/src/commands/logs.ts +24 -0
  64. package/src/lib/admin-manager.ts +435 -0
  65. package/src/lib/admin-server.ts +1243 -0
  66. package/src/lib/download-job-manager.ts +213 -0
  67. package/src/tui/MultiServerMonitorApp.ts +1 -1
  68. package/src/types/admin-config.ts +25 -0
  69. package/src/utils/log-parser.ts +13 -0
  70. package/web/README.md +429 -0
  71. package/web/eslint.config.js +23 -0
  72. package/web/index.html +13 -0
  73. package/web/llamacpp-web-dist.tar.gz +0 -0
  74. package/web/package-lock.json +4017 -0
  75. package/web/package.json +38 -0
  76. package/web/postcss.config.js +6 -0
  77. package/web/public/vite.svg +1 -0
  78. package/web/src/App.css +42 -0
  79. package/web/src/App.tsx +86 -0
  80. package/web/src/assets/react.svg +1 -0
  81. package/web/src/components/ApiKeyPrompt.tsx +71 -0
  82. package/web/src/components/CreateServerModal.tsx +372 -0
  83. package/web/src/components/DownloadProgress.tsx +123 -0
  84. package/web/src/components/Nav.tsx +89 -0
  85. package/web/src/components/RouterConfigModal.tsx +240 -0
  86. package/web/src/components/SearchModal.tsx +306 -0
  87. package/web/src/components/ServerConfigModal.tsx +291 -0
  88. package/web/src/hooks/useApi.ts +259 -0
  89. package/web/src/index.css +42 -0
  90. package/web/src/lib/api.ts +226 -0
  91. package/web/src/main.tsx +10 -0
  92. package/web/src/pages/Dashboard.tsx +103 -0
  93. package/web/src/pages/Models.tsx +258 -0
  94. package/web/src/pages/Router.tsx +270 -0
  95. package/web/src/pages/RouterLogs.tsx +201 -0
  96. package/web/src/pages/ServerLogs.tsx +553 -0
  97. package/web/src/pages/Servers.tsx +358 -0
  98. package/web/src/types/api.ts +140 -0
  99. package/web/tailwind.config.js +31 -0
  100. package/web/tsconfig.app.json +28 -0
  101. package/web/tsconfig.json +7 -0
  102. package/web/tsconfig.node.json +26 -0
  103. package/web/vite.config.ts +25 -0
  104. package/MONITORING-ACCURACY-FIX.md +0 -199
  105. package/PER-PROCESS-METRICS.md +0 -190
@@ -0,0 +1,121 @@
1
+ import chalk from 'chalk';
2
+ import { adminManager } from '../../lib/admin-manager';
3
+
4
+ interface ConfigOptions {
5
+ port?: number;
6
+ host?: string;
7
+ regenerateKey?: boolean;
8
+ verbose?: boolean;
9
+ restart?: boolean;
10
+ }
11
+
12
+ export async function adminConfigCommand(options: ConfigOptions): Promise<void> {
13
+ try {
14
+ const result = await adminManager.getStatus();
15
+
16
+ if (!result) {
17
+ console.error(chalk.red('✗ Admin service is not configured'));
18
+ console.log(chalk.gray('\nRun: llamacpp admin start'));
19
+ process.exit(1);
20
+ }
21
+
22
+ const { config, status } = result;
23
+
24
+ // Check if any options were provided
25
+ const hasChanges = options.port || options.host || options.regenerateKey !== undefined || options.verbose !== undefined;
26
+ if (!hasChanges) {
27
+ console.error(chalk.red('✗ No configuration options provided'));
28
+ console.log(chalk.gray('\nAvailable options:'));
29
+ console.log(chalk.gray(' --port <port> Change port'));
30
+ console.log(chalk.gray(' --host <host> Change host'));
31
+ console.log(chalk.gray(' --regenerate-key Generate new API key'));
32
+ console.log(chalk.gray(' --verbose Enable verbose logging'));
33
+ console.log(chalk.gray(' --restart Restart after config change'));
34
+ process.exit(1);
35
+ }
36
+
37
+ // Display what will change
38
+ console.log(chalk.bold('Configuration Changes:'));
39
+ console.log();
40
+
41
+ const updates: Partial<typeof config> = {};
42
+
43
+ if (options.port !== undefined) {
44
+ console.log(chalk.bold(' Port: '), chalk.gray(config.port.toString()), chalk.gray('→'), chalk.cyan(options.port.toString()));
45
+ updates.port = options.port;
46
+ }
47
+
48
+ if (options.host !== undefined) {
49
+ console.log(chalk.bold(' Host: '), chalk.gray(config.host), chalk.gray('→'), chalk.cyan(options.host));
50
+ updates.host = options.host;
51
+
52
+ // Warn if binding to non-localhost
53
+ if (options.host !== '127.0.0.1' && options.host !== 'localhost') {
54
+ console.log();
55
+ console.log(chalk.yellow(' ⚠ Warning: Binding to non-localhost address exposes admin API to network'));
56
+ console.log(chalk.yellow(' ⚠ Ensure your firewall is properly configured'));
57
+ }
58
+ }
59
+
60
+ if (options.verbose !== undefined) {
61
+ console.log(chalk.bold(' Verbose: '), chalk.gray(config.verbose ? 'enabled' : 'disabled'), chalk.gray('→'), chalk.cyan(options.verbose ? 'enabled' : 'disabled'));
62
+ updates.verbose = options.verbose;
63
+ }
64
+
65
+ let newApiKey: string | undefined;
66
+ if (options.regenerateKey) {
67
+ newApiKey = await adminManager.regenerateApiKey();
68
+ console.log(chalk.bold(' API Key: '), chalk.gray('*********************'), chalk.gray('→'), chalk.yellow(newApiKey));
69
+ }
70
+
71
+ console.log();
72
+
73
+ // Check if restart is needed
74
+ const isRunning = status.isRunning;
75
+ const needsRestart = isRunning && (options.port !== undefined || options.host !== undefined);
76
+
77
+ if (needsRestart && !options.restart) {
78
+ console.log(chalk.yellow('⚠ Admin service is running. Changes require restart.'));
79
+ console.log(chalk.gray(' Add --restart flag to restart automatically\n'));
80
+ }
81
+
82
+ // Apply changes
83
+ if (Object.keys(updates).length > 0) {
84
+ await adminManager.updateConfig(updates);
85
+ }
86
+
87
+ // Restart if requested and needed
88
+ if (options.restart && needsRestart) {
89
+ console.log(chalk.blue('🔄 Restarting admin service...\n'));
90
+
91
+ // Regenerate plist with new config
92
+ const updatedConfig = await adminManager.loadConfig();
93
+ if (updatedConfig) {
94
+ await adminManager.createPlist(updatedConfig);
95
+ }
96
+
97
+ await adminManager.restart();
98
+
99
+ console.log(chalk.green('✓ Admin service restarted successfully'));
100
+ } else {
101
+ console.log(chalk.green('✓ Configuration updated'));
102
+
103
+ if (needsRestart && !options.restart) {
104
+ console.log(chalk.gray('\nRestart with: llamacpp admin restart'));
105
+ }
106
+ }
107
+
108
+ console.log();
109
+
110
+ // Show new API key prominently if regenerated
111
+ if (newApiKey) {
112
+ console.log(chalk.bold('New API Key:'), chalk.yellow(newApiKey));
113
+ console.log(chalk.gray('Store this key securely - it cannot be retrieved later'));
114
+ console.log();
115
+ }
116
+ } catch (error) {
117
+ console.error(chalk.red('✗ Failed to update admin configuration'));
118
+ console.error(chalk.gray((error as Error).message));
119
+ process.exit(1);
120
+ }
121
+ }
@@ -0,0 +1,91 @@
1
+ import chalk from 'chalk';
2
+ import { spawn } from 'child_process';
3
+ import * as fs from 'fs/promises';
4
+ import { adminManager } from '../../lib/admin-manager';
5
+ import { fileExists } from '../../utils/file-utils';
6
+
7
+ interface LogsOptions {
8
+ stdout?: boolean;
9
+ stderr?: boolean;
10
+ follow?: boolean;
11
+ clear?: boolean;
12
+ lines?: number;
13
+ }
14
+
15
+ export async function adminLogsCommand(options: LogsOptions): Promise<void> {
16
+ try {
17
+ const result = await adminManager.getStatus();
18
+
19
+ if (!result) {
20
+ console.error(chalk.red('✗ Admin service is not configured'));
21
+ console.log(chalk.gray('\nRun: llamacpp admin start'));
22
+ process.exit(1);
23
+ }
24
+
25
+ const { config } = result;
26
+
27
+ // Default to stdout if neither specified
28
+ const showStdout = options.stdout || (!options.stdout && !options.stderr);
29
+ const showStderr = options.stderr || (!options.stdout && !options.stderr);
30
+
31
+ // Handle clear operation
32
+ if (options.clear) {
33
+ if (showStdout && (await fileExists(config.stdoutPath))) {
34
+ await fs.writeFile(config.stdoutPath, '');
35
+ console.log(chalk.green('✓ Cleared stdout log'));
36
+ }
37
+ if (showStderr && (await fileExists(config.stderrPath))) {
38
+ await fs.writeFile(config.stderrPath, '');
39
+ console.log(chalk.green('✓ Cleared stderr log'));
40
+ }
41
+ return;
42
+ }
43
+
44
+ // Determine which logs to show
45
+ const logPaths: string[] = [];
46
+ if (showStdout) logPaths.push(config.stdoutPath);
47
+ if (showStderr) logPaths.push(config.stderrPath);
48
+
49
+ // Check if log files exist
50
+ for (const logPath of logPaths) {
51
+ if (!(await fileExists(logPath))) {
52
+ console.log(chalk.yellow(`Log file does not exist: ${logPath}`));
53
+ console.log(chalk.gray('No logs available yet'));
54
+ return;
55
+ }
56
+ }
57
+
58
+ // Follow mode (tail -f)
59
+ if (options.follow) {
60
+ console.log(chalk.blue(`📋 Following admin logs (Ctrl+C to exit)\n`));
61
+
62
+ const tailArgs = ['-f', ...logPaths];
63
+ const tail = spawn('tail', tailArgs, { stdio: 'inherit' });
64
+
65
+ // Handle Ctrl+C gracefully
66
+ process.on('SIGINT', () => {
67
+ tail.kill();
68
+ console.log(chalk.gray('\n\nStopped following logs'));
69
+ process.exit(0);
70
+ });
71
+
72
+ tail.on('exit', (code) => {
73
+ process.exit(code || 0);
74
+ });
75
+ } else {
76
+ // Static mode (tail -n)
77
+ const lines = options.lines || 100;
78
+ const tailArgs = ['-n', lines.toString(), ...logPaths];
79
+
80
+ const tail = spawn('tail', tailArgs, { stdio: 'inherit' });
81
+
82
+ tail.on('exit', (code) => {
83
+ process.exit(code || 0);
84
+ });
85
+ }
86
+ } catch (error) {
87
+ console.error(chalk.red('✗ Failed to read admin logs'));
88
+ console.error(chalk.gray((error as Error).message));
89
+ process.exit(1);
90
+ }
91
+ }
@@ -0,0 +1,26 @@
1
+ import chalk from 'chalk';
2
+ import { adminManager } from '../../lib/admin-manager';
3
+
4
+ export async function adminRestartCommand(): Promise<void> {
5
+ try {
6
+ console.log(chalk.blue('🔄 Restarting admin service...\n'));
7
+
8
+ await adminManager.restart();
9
+
10
+ const result = await adminManager.getStatus();
11
+ if (!result) {
12
+ throw new Error('Failed to retrieve admin status after restart');
13
+ }
14
+
15
+ const { config, status } = result;
16
+
17
+ console.log(chalk.green('✓ Admin service restarted successfully\n'));
18
+ console.log(chalk.bold(' Endpoint:'), chalk.cyan(`http://${config.host}:${config.port}`));
19
+ console.log(chalk.bold(' PID: '), status.pid);
20
+ console.log();
21
+ } catch (error) {
22
+ console.error(chalk.red('✗ Failed to restart admin service'));
23
+ console.error(chalk.gray((error as Error).message));
24
+ process.exit(1);
25
+ }
26
+ }
@@ -0,0 +1,27 @@
1
+ import chalk from 'chalk';
2
+ import { adminManager } from '../../lib/admin-manager';
3
+
4
+ export async function adminStartCommand(): Promise<void> {
5
+ try {
6
+ console.log(chalk.blue('🚀 Starting admin service...\n'));
7
+
8
+ await adminManager.start();
9
+
10
+ const result = await adminManager.getStatus();
11
+ if (!result) {
12
+ throw new Error('Failed to retrieve admin status after start');
13
+ }
14
+
15
+ const { config, status } = result;
16
+
17
+ console.log(chalk.green('✓ Admin service started successfully\n'));
18
+ console.log(chalk.bold(' Endpoint:'), chalk.cyan(`http://${config.host}:${config.port}`));
19
+ console.log(chalk.bold(' API Key: '), chalk.yellow(config.apiKey), chalk.gray('(use for authentication)'));
20
+ console.log(chalk.bold(' PID: '), status.pid);
21
+ console.log();
22
+ } catch (error) {
23
+ console.error(chalk.red('✗ Failed to start admin service'));
24
+ console.error(chalk.gray((error as Error).message));
25
+ process.exit(1);
26
+ }
27
+ }
@@ -0,0 +1,84 @@
1
+ import chalk from 'chalk';
2
+ import { adminManager } from '../../lib/admin-manager';
3
+
4
+ function formatUptime(lastStarted: string): string {
5
+ const start = new Date(lastStarted).getTime();
6
+ const now = Date.now();
7
+ const uptimeSeconds = Math.floor((now - start) / 1000);
8
+
9
+ const hours = Math.floor(uptimeSeconds / 3600);
10
+ const minutes = Math.floor((uptimeSeconds % 3600) / 60);
11
+
12
+ if (hours > 0) {
13
+ return `${hours}h ${minutes}m`;
14
+ }
15
+ return `${minutes}m`;
16
+ }
17
+
18
+ export async function adminStatusCommand(): Promise<void> {
19
+ try {
20
+ const result = await adminManager.getStatus();
21
+
22
+ if (!result) {
23
+ console.log(chalk.yellow('Admin service is not configured'));
24
+ console.log(chalk.gray('\nRun: llamacpp admin start'));
25
+ return;
26
+ }
27
+
28
+ const { config, status } = result;
29
+
30
+ console.log(chalk.bold.underline('Admin Service Status'));
31
+ console.log();
32
+
33
+ // Status
34
+ if (status.isRunning) {
35
+ console.log(chalk.bold(' Status: '), chalk.green('● RUNNING'));
36
+ console.log(chalk.bold(' PID: '), status.pid);
37
+ if (config.lastStarted) {
38
+ console.log(chalk.bold(' Uptime: '), formatUptime(config.lastStarted));
39
+ }
40
+ } else {
41
+ console.log(chalk.bold(' Status: '), chalk.gray('○ STOPPED'));
42
+ if (status.lastExitReason) {
43
+ console.log(chalk.bold(' Last Exit:'), chalk.yellow(status.lastExitReason));
44
+ }
45
+ }
46
+
47
+ console.log(chalk.bold(' Port: '), config.port);
48
+ console.log(chalk.bold(' Host: '), config.host);
49
+ console.log(chalk.bold(' API Key: '), chalk.yellow(config.apiKey));
50
+ console.log();
51
+
52
+ // Endpoints
53
+ if (status.isRunning) {
54
+ console.log(chalk.bold(' Endpoints:'));
55
+ console.log(chalk.bold(' Health: '), chalk.cyan(`GET http://${config.host}:${config.port}/health`));
56
+ console.log(chalk.bold(' Servers: '), chalk.cyan(`GET http://${config.host}:${config.port}/api/servers`));
57
+ console.log(chalk.bold(' Models: '), chalk.cyan(`GET http://${config.host}:${config.port}/api/models`));
58
+ console.log();
59
+ }
60
+
61
+ // Configuration
62
+ console.log(chalk.bold(' Configuration:'));
63
+ console.log(chalk.bold(' Config: '), chalk.gray(config.plistPath.replace(process.env.HOME || '', '~')));
64
+ console.log(chalk.bold(' Plist: '), chalk.gray(config.plistPath.replace(process.env.HOME || '', '~')));
65
+ console.log(chalk.bold(' Logs: '), chalk.gray(config.stdoutPath.replace('.stdout', '.{stdout,stderr}').replace(process.env.HOME || '', '~')));
66
+ console.log();
67
+
68
+ // Quick commands
69
+ console.log(chalk.bold(' Quick Commands:'));
70
+ if (status.isRunning) {
71
+ console.log(chalk.bold(' Stop: '), chalk.gray('llamacpp admin stop'));
72
+ console.log(chalk.bold(' Restart: '), chalk.gray('llamacpp admin restart'));
73
+ console.log(chalk.bold(' Logs: '), chalk.gray('llamacpp admin logs --follow'));
74
+ } else {
75
+ console.log(chalk.bold(' Start: '), chalk.gray('llamacpp admin start'));
76
+ console.log(chalk.bold(' Logs: '), chalk.gray('llamacpp admin logs'));
77
+ }
78
+ console.log();
79
+ } catch (error) {
80
+ console.error(chalk.red('✗ Failed to get admin status'));
81
+ console.error(chalk.gray((error as Error).message));
82
+ process.exit(1);
83
+ }
84
+ }
@@ -0,0 +1,16 @@
1
+ import chalk from 'chalk';
2
+ import { adminManager } from '../../lib/admin-manager';
3
+
4
+ export async function adminStopCommand(): Promise<void> {
5
+ try {
6
+ console.log(chalk.blue('⏸ Stopping admin service...\n'));
7
+
8
+ await adminManager.stop();
9
+
10
+ console.log(chalk.green('✓ Admin service stopped successfully'));
11
+ } catch (error) {
12
+ console.error(chalk.red('✗ Failed to stop admin service'));
13
+ console.error(chalk.gray((error as Error).message));
14
+ process.exit(1);
15
+ }
16
+ }
@@ -27,6 +27,7 @@ interface LogsOptions {
27
27
  rotate?: boolean;
28
28
  clearArchived?: boolean;
29
29
  clearAll?: boolean;
30
+ includeHealth?: boolean;
30
31
  }
31
32
 
32
33
  export async function logsCommand(identifier: string, options: LogsOptions): Promise<void> {
@@ -142,6 +143,9 @@ export async function logsCommand(identifier: string, options: LogsOptions): Pro
142
143
  let filterDesc = '';
143
144
  let useCompactMode = false;
144
145
 
146
+ // Whether to include health check requests (filtered by default)
147
+ const includeHealth = options.includeHealth ?? false;
148
+
145
149
  if (options.verbose) {
146
150
  // Show everything (no filter)
147
151
  filterDesc = ' (all messages)';
@@ -195,7 +199,15 @@ export async function logsCommand(identifier: string, options: LogsOptions): Pro
195
199
 
196
200
  rl.on('line', (line) => {
197
201
  if (line.includes('log_server_r')) {
202
+ // Skip health check requests unless --include-health is set
203
+ if (!includeHealth && logParser.isHealthCheckRequest(line)) {
204
+ return;
205
+ }
198
206
  logParser.processLine(line, (compactLine) => {
207
+ // Double-check the parsed line for health checks (in case buffered)
208
+ if (!includeHealth && logParser.isHealthCheckRequest(compactLine)) {
209
+ return;
210
+ }
199
211
  console.log(compactLine);
200
212
  });
201
213
  }
@@ -266,13 +278,25 @@ export async function logsCommand(identifier: string, options: LogsOptions): Pro
266
278
 
267
279
  const compactLines: string[] = [];
268
280
  for (const line of logLines) {
281
+ // Skip health check requests unless --include-health is set
282
+ if (!includeHealth && logParser.isHealthCheckRequest(line)) {
283
+ continue;
284
+ }
269
285
  logParser.processLine(line, (compactLine) => {
286
+ // Double-check the parsed line for health checks (in case buffered)
287
+ if (!includeHealth && logParser.isHealthCheckRequest(compactLine)) {
288
+ return;
289
+ }
270
290
  compactLines.push(compactLine);
271
291
  });
272
292
  }
273
293
 
274
294
  // Flush any remaining buffered logs (handles simple format)
275
295
  logParser.flush((compactLine) => {
296
+ // Filter health checks from flushed lines too
297
+ if (!includeHealth && logParser.isHealthCheckRequest(compactLine)) {
298
+ return;
299
+ }
276
300
  compactLines.push(compactLine);
277
301
  });
278
302