@appkit/llamacpp-cli 1.11.0 → 1.12.1

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 (126) hide show
  1. package/README.md +572 -170
  2. package/dist/cli.js +99 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/admin/config.d.ts +10 -0
  5. package/dist/commands/admin/config.d.ts.map +1 -0
  6. package/dist/commands/admin/config.js +100 -0
  7. package/dist/commands/admin/config.js.map +1 -0
  8. package/dist/commands/admin/logs.d.ts +10 -0
  9. package/dist/commands/admin/logs.d.ts.map +1 -0
  10. package/dist/commands/admin/logs.js +114 -0
  11. package/dist/commands/admin/logs.js.map +1 -0
  12. package/dist/commands/admin/restart.d.ts +2 -0
  13. package/dist/commands/admin/restart.d.ts.map +1 -0
  14. package/dist/commands/admin/restart.js +29 -0
  15. package/dist/commands/admin/restart.js.map +1 -0
  16. package/dist/commands/admin/start.d.ts +2 -0
  17. package/dist/commands/admin/start.d.ts.map +1 -0
  18. package/dist/commands/admin/start.js +30 -0
  19. package/dist/commands/admin/start.js.map +1 -0
  20. package/dist/commands/admin/status.d.ts +2 -0
  21. package/dist/commands/admin/status.d.ts.map +1 -0
  22. package/dist/commands/admin/status.js +82 -0
  23. package/dist/commands/admin/status.js.map +1 -0
  24. package/dist/commands/admin/stop.d.ts +2 -0
  25. package/dist/commands/admin/stop.d.ts.map +1 -0
  26. package/dist/commands/admin/stop.js +21 -0
  27. package/dist/commands/admin/stop.js.map +1 -0
  28. package/dist/commands/logs.d.ts +1 -0
  29. package/dist/commands/logs.d.ts.map +1 -1
  30. package/dist/commands/logs.js +22 -0
  31. package/dist/commands/logs.js.map +1 -1
  32. package/dist/lib/admin-manager.d.ts +111 -0
  33. package/dist/lib/admin-manager.d.ts.map +1 -0
  34. package/dist/lib/admin-manager.js +413 -0
  35. package/dist/lib/admin-manager.js.map +1 -0
  36. package/dist/lib/admin-server.d.ts +148 -0
  37. package/dist/lib/admin-server.d.ts.map +1 -0
  38. package/dist/lib/admin-server.js +1161 -0
  39. package/dist/lib/admin-server.js.map +1 -0
  40. package/dist/lib/download-job-manager.d.ts +64 -0
  41. package/dist/lib/download-job-manager.d.ts.map +1 -0
  42. package/dist/lib/download-job-manager.js +164 -0
  43. package/dist/lib/download-job-manager.js.map +1 -0
  44. package/dist/tui/MultiServerMonitorApp.js +1 -1
  45. package/dist/types/admin-config.d.ts +19 -0
  46. package/dist/types/admin-config.d.ts.map +1 -0
  47. package/dist/types/admin-config.js +3 -0
  48. package/dist/types/admin-config.js.map +1 -0
  49. package/dist/utils/log-parser.d.ts +9 -0
  50. package/dist/utils/log-parser.d.ts.map +1 -1
  51. package/dist/utils/log-parser.js +11 -0
  52. package/dist/utils/log-parser.js.map +1 -1
  53. package/package.json +10 -2
  54. package/web/README.md +429 -0
  55. package/web/dist/assets/index-Bin89Lwr.css +1 -0
  56. package/web/dist/assets/index-CVmonw3T.js +17 -0
  57. package/web/dist/index.html +14 -0
  58. package/web/dist/vite.svg +1 -0
  59. package/.versionrc.json +0 -16
  60. package/CHANGELOG.md +0 -203
  61. package/MONITORING-ACCURACY-FIX.md +0 -199
  62. package/PER-PROCESS-METRICS.md +0 -190
  63. package/docs/images/.gitkeep +0 -1
  64. package/src/cli.ts +0 -423
  65. package/src/commands/config-global.ts +0 -38
  66. package/src/commands/config.ts +0 -323
  67. package/src/commands/create.ts +0 -183
  68. package/src/commands/delete.ts +0 -74
  69. package/src/commands/list.ts +0 -37
  70. package/src/commands/logs-all.ts +0 -251
  71. package/src/commands/logs.ts +0 -321
  72. package/src/commands/monitor.ts +0 -110
  73. package/src/commands/ps.ts +0 -84
  74. package/src/commands/pull.ts +0 -44
  75. package/src/commands/rm.ts +0 -107
  76. package/src/commands/router/config.ts +0 -116
  77. package/src/commands/router/logs.ts +0 -256
  78. package/src/commands/router/restart.ts +0 -36
  79. package/src/commands/router/start.ts +0 -60
  80. package/src/commands/router/status.ts +0 -119
  81. package/src/commands/router/stop.ts +0 -33
  82. package/src/commands/run.ts +0 -233
  83. package/src/commands/search.ts +0 -107
  84. package/src/commands/server-show.ts +0 -161
  85. package/src/commands/show.ts +0 -207
  86. package/src/commands/start.ts +0 -101
  87. package/src/commands/stop.ts +0 -39
  88. package/src/commands/tui.ts +0 -25
  89. package/src/lib/config-generator.ts +0 -130
  90. package/src/lib/history-manager.ts +0 -172
  91. package/src/lib/launchctl-manager.ts +0 -225
  92. package/src/lib/metrics-aggregator.ts +0 -257
  93. package/src/lib/model-downloader.ts +0 -328
  94. package/src/lib/model-scanner.ts +0 -157
  95. package/src/lib/model-search.ts +0 -114
  96. package/src/lib/models-dir-setup.ts +0 -46
  97. package/src/lib/port-manager.ts +0 -80
  98. package/src/lib/router-logger.ts +0 -201
  99. package/src/lib/router-manager.ts +0 -414
  100. package/src/lib/router-server.ts +0 -538
  101. package/src/lib/state-manager.ts +0 -206
  102. package/src/lib/status-checker.ts +0 -113
  103. package/src/lib/system-collector.ts +0 -315
  104. package/src/tui/ConfigApp.ts +0 -1085
  105. package/src/tui/HistoricalMonitorApp.ts +0 -587
  106. package/src/tui/ModelsApp.ts +0 -368
  107. package/src/tui/MonitorApp.ts +0 -386
  108. package/src/tui/MultiServerMonitorApp.ts +0 -1833
  109. package/src/tui/RootNavigator.ts +0 -74
  110. package/src/tui/SearchApp.ts +0 -511
  111. package/src/tui/SplashScreen.ts +0 -149
  112. package/src/types/global-config.ts +0 -26
  113. package/src/types/history-types.ts +0 -39
  114. package/src/types/model-info.ts +0 -8
  115. package/src/types/monitor-types.ts +0 -162
  116. package/src/types/router-config.ts +0 -25
  117. package/src/types/server-config.ts +0 -46
  118. package/src/utils/downsample-utils.ts +0 -128
  119. package/src/utils/file-utils.ts +0 -146
  120. package/src/utils/format-utils.ts +0 -98
  121. package/src/utils/log-parser.ts +0 -271
  122. package/src/utils/log-utils.ts +0 -178
  123. package/src/utils/process-utils.ts +0 -316
  124. package/src/utils/prompt-utils.ts +0 -47
  125. package/test-load.sh +0 -100
  126. package/tsconfig.json +0 -20
@@ -1,251 +0,0 @@
1
- import chalk from 'chalk';
2
- import Table from 'cli-table3';
3
- import { stateManager } from '../lib/state-manager';
4
- import { fileExists } from '../utils/file-utils';
5
- import {
6
- getFileSize,
7
- formatFileSize,
8
- getArchivedLogInfo,
9
- clearLogFile,
10
- rotateLogFile,
11
- deleteArchivedLogs,
12
- } from '../utils/log-utils';
13
-
14
- interface LogsAllOptions {
15
- clear?: boolean;
16
- clearArchived?: boolean;
17
- clearAll?: boolean;
18
- rotate?: boolean;
19
- }
20
-
21
- export async function logsAllCommand(options: LogsAllOptions): Promise<void> {
22
- // Get all servers
23
- const servers = await stateManager.getAllServers();
24
-
25
- if (servers.length === 0) {
26
- console.log(chalk.yellow('⚠️ No servers found'));
27
- console.log(chalk.dim('\nCreate a server: llamacpp server create <model-filename>'));
28
- return;
29
- }
30
-
31
- // Handle batch operations
32
- if (options.clear || options.clearArchived || options.clearAll || options.rotate) {
33
- await handleBatchOperation(servers, options);
34
- return;
35
- }
36
-
37
- // Show table of log information
38
- await showLogsTable(servers);
39
- }
40
-
41
- async function showLogsTable(servers: any[]): Promise<void> {
42
- const table = new Table({
43
- head: [
44
- chalk.bold('Server ID'),
45
- chalk.bold('Current Stderr'),
46
- chalk.bold('Current Stdout'),
47
- chalk.bold('Archived'),
48
- chalk.bold('Total'),
49
- ],
50
- colWidths: [30, 18, 18, 18, 18],
51
- });
52
-
53
- let totalCurrent = 0;
54
- let totalArchived = 0;
55
-
56
- for (const server of servers) {
57
- // Get current log sizes
58
- const stderrSize = (await fileExists(server.stderrPath))
59
- ? await getFileSize(server.stderrPath)
60
- : 0;
61
- const stdoutSize = (await fileExists(server.stdoutPath))
62
- ? await getFileSize(server.stdoutPath)
63
- : 0;
64
-
65
- // Get archived info
66
- const archivedInfo = await getArchivedLogInfo(server.id);
67
-
68
- const currentTotal = stderrSize + stdoutSize;
69
- const total = currentTotal + archivedInfo.totalSize;
70
-
71
- totalCurrent += currentTotal;
72
- totalArchived += archivedInfo.totalSize;
73
-
74
- table.push([
75
- server.id,
76
- formatFileSize(stderrSize),
77
- formatFileSize(stdoutSize),
78
- archivedInfo.count > 0
79
- ? `${formatFileSize(archivedInfo.totalSize)} (${archivedInfo.count})`
80
- : formatFileSize(0),
81
- formatFileSize(total),
82
- ]);
83
- }
84
-
85
- console.log(chalk.bold('\nServer Logs Overview:'));
86
- console.log(table.toString());
87
-
88
- console.log(chalk.dim('\nTotals:'));
89
- console.log(chalk.dim(` Current logs: ${formatFileSize(totalCurrent)}`));
90
- console.log(chalk.dim(` Archived logs: ${formatFileSize(totalArchived)}`));
91
- console.log(chalk.dim(` Grand total: ${formatFileSize(totalCurrent + totalArchived)}`));
92
-
93
- console.log(chalk.dim('\nBatch operations:'));
94
- console.log(chalk.dim(' llamacpp logs --clear Clear all current logs'));
95
- console.log(chalk.dim(' llamacpp logs --clear-archived Delete only archived logs'));
96
- console.log(chalk.dim(' llamacpp logs --clear-all Clear current + delete archives'));
97
- console.log(chalk.dim(' llamacpp logs --rotate Rotate all logs with timestamps'));
98
- }
99
-
100
- async function handleBatchOperation(
101
- servers: any[],
102
- options: LogsAllOptions
103
- ): Promise<void> {
104
- if (options.clearArchived) {
105
- console.log(chalk.blue('🗑️ Deleting archived logs for all servers...'));
106
- console.log();
107
-
108
- let totalFreed = 0;
109
- let totalFiles = 0;
110
- let serversProcessed = 0;
111
-
112
- for (const server of servers) {
113
- const archivedInfo = await deleteArchivedLogs(server.id);
114
-
115
- if (archivedInfo.count > 0) {
116
- console.log(chalk.dim(` ${server.id}: ${formatFileSize(archivedInfo.totalSize)} (${archivedInfo.count} file${archivedInfo.count !== 1 ? 's' : ''})`));
117
- totalFreed += archivedInfo.totalSize;
118
- totalFiles += archivedInfo.count;
119
- serversProcessed++;
120
- }
121
- }
122
-
123
- console.log();
124
- if (serversProcessed === 0) {
125
- console.log(chalk.yellow('⚠️ No archived logs found'));
126
- console.log(chalk.dim(' Archived logs are created via --rotate or automatic rotation'));
127
- } else {
128
- console.log(chalk.green(`✅ Deleted archived logs for ${serversProcessed} server${serversProcessed !== 1 ? 's' : ''}`));
129
- console.log(chalk.dim(` Files deleted: ${totalFiles}`));
130
- console.log(chalk.dim(` Total freed: ${formatFileSize(totalFreed)}`));
131
- console.log(chalk.dim(` Current logs preserved`));
132
- }
133
- } else if (options.clearAll) {
134
- console.log(chalk.blue('🗑️ Clearing all logs (current + archived) for all servers...'));
135
- console.log();
136
-
137
- let totalFreed = 0;
138
- let serversProcessed = 0;
139
-
140
- for (const server of servers) {
141
- let serverTotal = 0;
142
-
143
- // Clear current stderr
144
- if (await fileExists(server.stderrPath)) {
145
- serverTotal += await getFileSize(server.stderrPath);
146
- await clearLogFile(server.stderrPath);
147
- }
148
-
149
- // Clear current stdout
150
- if (await fileExists(server.stdoutPath)) {
151
- serverTotal += await getFileSize(server.stdoutPath);
152
- await clearLogFile(server.stdoutPath);
153
- }
154
-
155
- // Delete archived logs
156
- const archivedInfo = await deleteArchivedLogs(server.id);
157
- serverTotal += archivedInfo.totalSize;
158
-
159
- if (serverTotal > 0) {
160
- console.log(chalk.dim(` ${server.id}: ${formatFileSize(serverTotal)}`));
161
- totalFreed += serverTotal;
162
- serversProcessed++;
163
- }
164
- }
165
-
166
- console.log();
167
- console.log(chalk.green(`✅ Cleared all logs for ${serversProcessed} server${serversProcessed !== 1 ? 's' : ''}`));
168
- console.log(chalk.dim(` Total freed: ${formatFileSize(totalFreed)}`));
169
- } else if (options.clear) {
170
- console.log(chalk.blue('🗑️ Clearing current logs for all servers...'));
171
- console.log();
172
-
173
- let totalFreed = 0;
174
- let serversProcessed = 0;
175
-
176
- for (const server of servers) {
177
- let serverTotal = 0;
178
-
179
- // Clear current stderr
180
- if (await fileExists(server.stderrPath)) {
181
- serverTotal += await getFileSize(server.stderrPath);
182
- await clearLogFile(server.stderrPath);
183
- }
184
-
185
- // Clear current stdout
186
- if (await fileExists(server.stdoutPath)) {
187
- serverTotal += await getFileSize(server.stdoutPath);
188
- await clearLogFile(server.stdoutPath);
189
- }
190
-
191
- if (serverTotal > 0) {
192
- console.log(chalk.dim(` ${server.id}: ${formatFileSize(serverTotal)}`));
193
- totalFreed += serverTotal;
194
- serversProcessed++;
195
- }
196
- }
197
-
198
- console.log();
199
- console.log(chalk.green(`✅ Cleared current logs for ${serversProcessed} server${serversProcessed !== 1 ? 's' : ''}`));
200
- console.log(chalk.dim(` Total freed: ${formatFileSize(totalFreed)}`));
201
- console.log(chalk.dim(` Archived logs preserved`));
202
- } else if (options.rotate) {
203
- console.log(chalk.blue('🔄 Rotating logs for all servers...'));
204
- console.log();
205
-
206
- let totalRotated = 0;
207
- let filesRotated = 0;
208
-
209
- for (const server of servers) {
210
- const rotatedFiles: string[] = [];
211
-
212
- // Rotate stderr if it has content
213
- if (await fileExists(server.stderrPath)) {
214
- const size = await getFileSize(server.stderrPath);
215
- if (size > 0) {
216
- try {
217
- const archivedPath = await rotateLogFile(server.stderrPath);
218
- rotatedFiles.push(archivedPath);
219
- totalRotated += size;
220
- filesRotated++;
221
- } catch {
222
- // Ignore empty files
223
- }
224
- }
225
- }
226
-
227
- // Rotate stdout if it has content
228
- if (await fileExists(server.stdoutPath)) {
229
- const size = await getFileSize(server.stdoutPath);
230
- if (size > 0) {
231
- try {
232
- const archivedPath = await rotateLogFile(server.stdoutPath);
233
- rotatedFiles.push(archivedPath);
234
- totalRotated += size;
235
- filesRotated++;
236
- } catch {
237
- // Ignore empty files
238
- }
239
- }
240
- }
241
-
242
- if (rotatedFiles.length > 0) {
243
- console.log(chalk.dim(` ${server.id}: ${rotatedFiles.length} file${rotatedFiles.length !== 1 ? 's' : ''}`));
244
- }
245
- }
246
-
247
- console.log();
248
- console.log(chalk.green(`✅ Rotated ${filesRotated} log file${filesRotated !== 1 ? 's' : ''}`));
249
- console.log(chalk.dim(` Total archived: ${formatFileSize(totalRotated)}`));
250
- }
251
- }
@@ -1,321 +0,0 @@
1
- import chalk from 'chalk';
2
- import { spawn } from 'child_process';
3
- import * as readline from 'readline';
4
- import * as fs from 'fs';
5
- import { stateManager } from '../lib/state-manager';
6
- import { fileExists } from '../utils/file-utils';
7
- import { execCommand } from '../utils/process-utils';
8
- import { logParser } from '../utils/log-parser';
9
- import {
10
- getFileSize,
11
- formatFileSize,
12
- rotateLogFile,
13
- clearLogFile,
14
- getArchivedLogInfo,
15
- deleteArchivedLogs,
16
- } from '../utils/log-utils';
17
-
18
- interface LogsOptions {
19
- follow?: boolean;
20
- lines?: number;
21
- errors?: boolean;
22
- verbose?: boolean;
23
- http?: boolean;
24
- stdout?: boolean;
25
- filter?: string;
26
- clear?: boolean;
27
- rotate?: boolean;
28
- clearArchived?: boolean;
29
- clearAll?: boolean;
30
- }
31
-
32
- export async function logsCommand(identifier: string, options: LogsOptions): Promise<void> {
33
- // Find server
34
- const server = await stateManager.findServer(identifier);
35
- if (!server) {
36
- throw new Error(`Server not found: ${identifier}\n\nUse: llamacpp ps`);
37
- }
38
-
39
- // Determine log file (default to stderr where verbose logs go)
40
- const logPath = options.stdout ? server.stdoutPath : server.stderrPath;
41
- const logType = options.stdout ? 'stdout' : 'stderr';
42
-
43
- // Handle --clear-archived option (deletes only archived logs)
44
- if (options.clearArchived) {
45
- const archivedInfo = await deleteArchivedLogs(server.id);
46
-
47
- if (archivedInfo.count === 0) {
48
- console.log(chalk.yellow(`⚠️ No archived logs found for ${server.modelName}`));
49
- console.log(chalk.dim(` Archived logs are created via --rotate or automatic rotation`));
50
- return;
51
- }
52
-
53
- console.log(chalk.green(`✅ Deleted archived logs for ${server.modelName}`));
54
- console.log(chalk.dim(` Files deleted: ${archivedInfo.count}`));
55
- console.log(chalk.dim(` Space freed: ${formatFileSize(archivedInfo.totalSize)}`));
56
- console.log(chalk.dim(` Current logs preserved`));
57
- return;
58
- }
59
-
60
- // Handle --clear-all option (clears both current and archived logs)
61
- if (options.clearAll) {
62
- let totalFreed = 0;
63
- let currentSize = 0;
64
- let archivedSize = 0;
65
-
66
- // Clear current stderr
67
- if (await fileExists(server.stderrPath)) {
68
- currentSize += await getFileSize(server.stderrPath);
69
- await clearLogFile(server.stderrPath);
70
- }
71
-
72
- // Clear current stdout
73
- if (await fileExists(server.stdoutPath)) {
74
- currentSize += await getFileSize(server.stdoutPath);
75
- await clearLogFile(server.stdoutPath);
76
- }
77
-
78
- // Delete all archived logs
79
- const archivedInfo = await deleteArchivedLogs(server.id);
80
- archivedSize = archivedInfo.totalSize;
81
-
82
- totalFreed = currentSize + archivedSize;
83
-
84
- console.log(chalk.green(`✅ Cleared all logs for ${server.modelName}`));
85
- if (currentSize > 0) {
86
- console.log(chalk.dim(` Current logs: ${formatFileSize(currentSize)}`));
87
- }
88
- if (archivedSize > 0) {
89
- console.log(chalk.dim(` Archived logs: ${formatFileSize(archivedSize)} (${archivedInfo.count} file${archivedInfo.count > 1 ? 's' : ''})`));
90
- }
91
- console.log(chalk.dim(` Total freed: ${formatFileSize(totalFreed)}`));
92
- return;
93
- }
94
-
95
- // Handle --clear option
96
- if (options.clear) {
97
- if (!(await fileExists(logPath))) {
98
- console.log(chalk.yellow(`⚠️ No ${logType} found for ${server.modelName}`));
99
- console.log(chalk.dim(` Log file does not exist: ${logPath}`));
100
- return;
101
- }
102
-
103
- const sizeBefore = await getFileSize(logPath);
104
- await clearLogFile(logPath);
105
-
106
- console.log(chalk.green(`✅ Cleared ${logType} for ${server.modelName}`));
107
- console.log(chalk.dim(` Freed: ${formatFileSize(sizeBefore)}`));
108
- console.log(chalk.dim(` ${logPath}`));
109
- return;
110
- }
111
-
112
- // Handle --rotate option
113
- if (options.rotate) {
114
- if (!(await fileExists(logPath))) {
115
- console.log(chalk.yellow(`⚠️ No ${logType} found for ${server.modelName}`));
116
- console.log(chalk.dim(` Log file does not exist: ${logPath}`));
117
- return;
118
- }
119
-
120
- try {
121
- const archivedPath = await rotateLogFile(logPath);
122
- const size = await getFileSize(archivedPath);
123
-
124
- console.log(chalk.green(`✅ Rotated ${logType} for ${server.modelName}`));
125
- console.log(chalk.dim(` Archived: ${formatFileSize(size)}`));
126
- console.log(chalk.dim(` → ${archivedPath}`));
127
- } catch (error) {
128
- throw new Error(`Failed to rotate log: ${(error as Error).message}`);
129
- }
130
- return;
131
- }
132
-
133
- // Check if log file exists
134
- if (!(await fileExists(logPath))) {
135
- console.log(chalk.yellow(`⚠️ No ${logType} found for ${server.modelName}`));
136
- console.log(chalk.dim(` Log file does not exist: ${logPath}`));
137
- return;
138
- }
139
-
140
- // Determine filter pattern and mode
141
- let filterPattern: string | null = null;
142
- let filterDesc = '';
143
- let useCompactMode = false;
144
-
145
- if (options.verbose) {
146
- // Show everything (no filter)
147
- filterDesc = ' (all messages)';
148
- } else if (options.errors) {
149
- // Show only errors
150
- filterPattern = 'error|Error|ERROR|failed|Failed|FAILED';
151
- filterDesc = ' (errors only)';
152
- } else if (options.http) {
153
- // Full HTTP JSON logs
154
- filterPattern = 'log_server_r';
155
- filterDesc = ' (HTTP JSON)';
156
- } else if (options.filter) {
157
- // Custom filter
158
- filterPattern = options.filter;
159
- filterDesc = ` (filter: ${options.filter})`;
160
- } else {
161
- // Default: Compact one-liner format
162
- filterPattern = 'log_server_r';
163
- filterDesc = ' (compact)';
164
- useCompactMode = true;
165
- }
166
-
167
- console.log(chalk.blue(`📋 Logs for ${server.modelName} (${logType}${filterDesc})`));
168
- console.log(chalk.dim(` ${logPath}`));
169
-
170
- // Show log size information
171
- const currentSize = await getFileSize(logPath);
172
- const archivedInfo = await getArchivedLogInfo(server.id);
173
-
174
- if (archivedInfo.count > 0) {
175
- console.log(chalk.dim(` Current: ${formatFileSize(currentSize)} | Archived: ${formatFileSize(archivedInfo.totalSize)} (${archivedInfo.count} file${archivedInfo.count > 1 ? 's' : ''})`));
176
- } else {
177
- console.log(chalk.dim(` Current: ${formatFileSize(currentSize)}`));
178
- }
179
-
180
- // Show subtle note if verbose logging is not enabled
181
- if (!server.verbose && !options.verbose && !options.errors && !options.http && !options.filter) {
182
- console.log(chalk.dim(` verbosity is disabled`));
183
- }
184
- console.log();
185
-
186
- if (options.follow) {
187
- // Follow logs in real-time with optional filtering
188
- if (useCompactMode) {
189
- // Compact mode with follow: parse lines in real-time
190
- const tailProcess = spawn('tail', ['-f', logPath]);
191
- const rl = readline.createInterface({
192
- input: tailProcess.stdout,
193
- crlfDelay: Infinity,
194
- });
195
-
196
- rl.on('line', (line) => {
197
- if (line.includes('log_server_r')) {
198
- logParser.processLine(line, (compactLine) => {
199
- console.log(compactLine);
200
- });
201
- }
202
- });
203
-
204
- // Handle Ctrl+C gracefully
205
- process.on('SIGINT', () => {
206
- tailProcess.kill();
207
- rl.close();
208
- console.log();
209
- process.exit(0);
210
- });
211
-
212
- tailProcess.on('exit', () => {
213
- process.exit(0);
214
- });
215
- } else if (filterPattern) {
216
- // Use tail piped to grep for filtering
217
- const grepProcess = spawn('sh', ['-c', `tail -f "${logPath}" | grep --line-buffered -E "${filterPattern}"`], {
218
- stdio: 'inherit',
219
- });
220
-
221
- // Handle Ctrl+C gracefully
222
- process.on('SIGINT', () => {
223
- grepProcess.kill();
224
- console.log();
225
- process.exit(0);
226
- });
227
-
228
- grepProcess.on('exit', () => {
229
- process.exit(0);
230
- });
231
- } else {
232
- // No filter, just tail
233
- const tail = spawn('tail', ['-f', logPath], {
234
- stdio: 'inherit',
235
- });
236
-
237
- process.on('SIGINT', () => {
238
- tail.kill();
239
- console.log();
240
- process.exit(0);
241
- });
242
-
243
- tail.on('exit', () => {
244
- process.exit(0);
245
- });
246
- }
247
- } else {
248
- // Show last N lines with optional filtering
249
- const lines = options.lines || 50;
250
-
251
- if (useCompactMode) {
252
- // Compact mode: read file and parse
253
- try {
254
- // Use large multiplier to account for verbose debug output between requests
255
- // Add || true to prevent grep from failing when no matches found
256
- const command = `tail -n ${lines * 100} "${logPath}" | grep -E "log_server_r" || true`;
257
- const output = await execCommand(command);
258
- const logLines = output.split('\n').filter((l) => l.trim());
259
-
260
- if (logLines.length === 0) {
261
- console.log(chalk.dim('No HTTP request logs in compact format.'));
262
- console.log(chalk.dim('The server may be starting up, or only simple GET requests have been made.'));
263
- console.log(chalk.dim('\nTip: Use --http to see raw HTTP logs, or --verbose for all server logs.'));
264
- return;
265
- }
266
-
267
- const compactLines: string[] = [];
268
- for (const line of logLines) {
269
- logParser.processLine(line, (compactLine) => {
270
- compactLines.push(compactLine);
271
- });
272
- }
273
-
274
- // Flush any remaining buffered logs (handles simple format)
275
- logParser.flush((compactLine) => {
276
- compactLines.push(compactLine);
277
- });
278
-
279
- // Check if we got any parsed output
280
- if (compactLines.length === 0) {
281
- console.log(chalk.dim('HTTP request logs found, but could not parse in compact format.'));
282
- console.log(chalk.dim('This usually happens with simple GET requests (health checks, slots, etc.).'));
283
- console.log(chalk.dim('\nTip: Use --http to see raw HTTP logs instead.'));
284
- return;
285
- }
286
-
287
- // Show only the last N compact lines
288
- const limitedLines = compactLines.slice(-lines);
289
- limitedLines.forEach((line) => console.log(line));
290
- } catch (error) {
291
- throw new Error(`Failed to read logs: ${(error as Error).message}`);
292
- }
293
- } else {
294
- // Regular filtering
295
- try {
296
- let command: string;
297
-
298
- if (filterPattern) {
299
- // Use tail piped to grep
300
- // Add || true to prevent grep from failing when no matches found
301
- command = `tail -n ${lines} "${logPath}" | grep -E "${filterPattern}" || true`;
302
- } else {
303
- // No filter
304
- command = `tail -n ${lines} "${logPath}"`;
305
- }
306
-
307
- const output = await execCommand(command);
308
-
309
- if (filterPattern && output.trim() === '') {
310
- console.log(chalk.dim(`No logs matching pattern: ${filterPattern}`));
311
- console.log(chalk.dim('\nTip: Try --verbose to see all logs, or adjust your filter pattern.'));
312
- return;
313
- }
314
-
315
- console.log(output);
316
- } catch (error) {
317
- throw new Error(`Failed to read logs: ${(error as Error).message}`);
318
- }
319
- }
320
- }
321
- }
@@ -1,110 +0,0 @@
1
- import chalk from 'chalk';
2
- import blessed from 'blessed';
3
- import { stateManager } from '../lib/state-manager.js';
4
- import { createMonitorUI } from '../tui/MonitorApp.js';
5
- import { createMultiServerMonitorUI } from '../tui/MultiServerMonitorApp.js';
6
-
7
- export async function monitorCommand(identifier?: string): Promise<void> {
8
- // Initialize state manager
9
- await stateManager.initialize();
10
-
11
- // Get all servers
12
- const allServers = await stateManager.getAllServers();
13
- if (allServers.length === 0) {
14
- throw new Error(
15
- `No servers configured.\n\n` +
16
- `Create a server first: llamacpp server create <model>`
17
- );
18
- }
19
-
20
- // Create blessed screen
21
- const screen = blessed.screen({
22
- smartCSR: true,
23
- title: 'llama.cpp Server Monitor',
24
- });
25
-
26
- // Determine which UI to launch
27
- if (identifier) {
28
- // User specified a server - single server mode
29
- const server = await stateManager.findServer(identifier);
30
- if (!server) {
31
- screen.destroy();
32
- throw new Error(
33
- `Server not found: ${identifier}\n\n` +
34
- `Use: llamacpp ps (to list servers)\n` +
35
- `Or create a new server: llamacpp server create <model>`
36
- );
37
- }
38
-
39
- // Update server status to reflect actual launchctl state
40
- const { statusChecker } = await import('../lib/status-checker.js');
41
- const status = await statusChecker.checkServer(server);
42
- server.status = status.isRunning ? 'running' : 'stopped';
43
- server.pid = status.pid || undefined;
44
-
45
- // Check if server is running
46
- if (server.status !== 'running') {
47
- screen.destroy();
48
- throw new Error(
49
- `Server ${server.modelName} is not running.\n\n` +
50
- `Start it first: llamacpp server start ${server.id}`
51
- );
52
- }
53
-
54
- // Launch single-server TUI
55
- await createMonitorUI(screen, server);
56
- } else if (allServers.length === 1) {
57
- // Only one server - single server mode
58
- const server = allServers[0];
59
-
60
- // Update server status to reflect actual launchctl state
61
- const { statusChecker } = await import('../lib/status-checker.js');
62
- const status = await statusChecker.checkServer(server);
63
- server.status = status.isRunning ? 'running' : 'stopped';
64
- server.pid = status.pid || undefined;
65
-
66
- // Check if server is running
67
- if (server.status !== 'running') {
68
- screen.destroy();
69
- throw new Error(
70
- `Server ${server.modelName} is not running.\n\n` +
71
- `Start it first: llamacpp server start ${server.id}`
72
- );
73
- }
74
-
75
- // Launch single-server TUI
76
- await createMonitorUI(screen, server);
77
- } else {
78
- // Multiple servers - multi-server mode
79
- // Update all server statuses to reflect actual launchctl state
80
- const { statusChecker } = await import('../lib/status-checker.js');
81
- for (const server of allServers) {
82
- const status = await statusChecker.checkServer(server);
83
- server.status = status.isRunning ? 'running' : 'stopped';
84
- server.pid = status.pid || undefined;
85
- }
86
-
87
- // Filter to only running servers for monitoring
88
- const runningServers = allServers.filter(s => s.status === 'running');
89
-
90
- if (runningServers.length === 0) {
91
- screen.destroy();
92
- throw new Error(
93
- `No servers are currently running.\n\n` +
94
- `Start a server first:\n` +
95
- allServers
96
- .map((s) => ` llamacpp server start ${s.id} # ${s.modelName}`)
97
- .join('\n')
98
- );
99
- }
100
-
101
- // Launch multi-server TUI (pass all servers so we can see stopped ones too)
102
- await createMultiServerMonitorUI(screen, allServers);
103
- }
104
-
105
- // Render the screen
106
- screen.render();
107
-
108
- // Note: TUI functions handle their own key events and exit directly
109
- // The process will stay alive until user presses Q/Ctrl+C
110
- }