@appkit/llamacpp-cli 1.12.0 → 1.13.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 (136) hide show
  1. package/README.md +294 -168
  2. package/dist/cli.js +35 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/launch/claude.d.ts +6 -0
  5. package/dist/commands/launch/claude.d.ts.map +1 -0
  6. package/dist/commands/launch/claude.js +277 -0
  7. package/dist/commands/launch/claude.js.map +1 -0
  8. package/dist/lib/integration-checker.d.ts +26 -0
  9. package/dist/lib/integration-checker.d.ts.map +1 -0
  10. package/dist/lib/integration-checker.js +77 -0
  11. package/dist/lib/integration-checker.js.map +1 -0
  12. package/dist/lib/router-manager.d.ts +4 -0
  13. package/dist/lib/router-manager.d.ts.map +1 -1
  14. package/dist/lib/router-manager.js +10 -0
  15. package/dist/lib/router-manager.js.map +1 -1
  16. package/dist/lib/router-server.d.ts +13 -0
  17. package/dist/lib/router-server.d.ts.map +1 -1
  18. package/dist/lib/router-server.js +267 -7
  19. package/dist/lib/router-server.js.map +1 -1
  20. package/dist/types/integration-config.d.ts +28 -0
  21. package/dist/types/integration-config.d.ts.map +1 -0
  22. package/dist/types/integration-config.js +3 -0
  23. package/dist/types/integration-config.js.map +1 -0
  24. package/package.json +10 -2
  25. package/web/dist/assets/index-Bin89Lwr.css +1 -0
  26. package/web/dist/assets/index-CVmonw3T.js +17 -0
  27. package/web/{index.html → dist/index.html} +2 -1
  28. package/.versionrc.json +0 -16
  29. package/CHANGELOG.md +0 -213
  30. package/docs/images/.gitkeep +0 -1
  31. package/docs/images/web-ui-servers.png +0 -0
  32. package/src/cli.ts +0 -523
  33. package/src/commands/admin/config.ts +0 -121
  34. package/src/commands/admin/logs.ts +0 -91
  35. package/src/commands/admin/restart.ts +0 -26
  36. package/src/commands/admin/start.ts +0 -27
  37. package/src/commands/admin/status.ts +0 -84
  38. package/src/commands/admin/stop.ts +0 -16
  39. package/src/commands/config-global.ts +0 -38
  40. package/src/commands/config.ts +0 -323
  41. package/src/commands/create.ts +0 -183
  42. package/src/commands/delete.ts +0 -74
  43. package/src/commands/list.ts +0 -37
  44. package/src/commands/logs-all.ts +0 -251
  45. package/src/commands/logs.ts +0 -345
  46. package/src/commands/monitor.ts +0 -110
  47. package/src/commands/ps.ts +0 -84
  48. package/src/commands/pull.ts +0 -44
  49. package/src/commands/rm.ts +0 -107
  50. package/src/commands/router/config.ts +0 -116
  51. package/src/commands/router/logs.ts +0 -256
  52. package/src/commands/router/restart.ts +0 -36
  53. package/src/commands/router/start.ts +0 -60
  54. package/src/commands/router/status.ts +0 -119
  55. package/src/commands/router/stop.ts +0 -33
  56. package/src/commands/run.ts +0 -233
  57. package/src/commands/search.ts +0 -107
  58. package/src/commands/server-show.ts +0 -161
  59. package/src/commands/show.ts +0 -207
  60. package/src/commands/start.ts +0 -101
  61. package/src/commands/stop.ts +0 -39
  62. package/src/commands/tui.ts +0 -25
  63. package/src/lib/admin-manager.ts +0 -435
  64. package/src/lib/admin-server.ts +0 -1243
  65. package/src/lib/config-generator.ts +0 -130
  66. package/src/lib/download-job-manager.ts +0 -213
  67. package/src/lib/history-manager.ts +0 -172
  68. package/src/lib/launchctl-manager.ts +0 -225
  69. package/src/lib/metrics-aggregator.ts +0 -257
  70. package/src/lib/model-downloader.ts +0 -328
  71. package/src/lib/model-scanner.ts +0 -157
  72. package/src/lib/model-search.ts +0 -114
  73. package/src/lib/models-dir-setup.ts +0 -46
  74. package/src/lib/port-manager.ts +0 -80
  75. package/src/lib/router-logger.ts +0 -201
  76. package/src/lib/router-manager.ts +0 -414
  77. package/src/lib/router-server.ts +0 -538
  78. package/src/lib/state-manager.ts +0 -206
  79. package/src/lib/status-checker.ts +0 -113
  80. package/src/lib/system-collector.ts +0 -315
  81. package/src/tui/ConfigApp.ts +0 -1085
  82. package/src/tui/HistoricalMonitorApp.ts +0 -587
  83. package/src/tui/ModelsApp.ts +0 -368
  84. package/src/tui/MonitorApp.ts +0 -386
  85. package/src/tui/MultiServerMonitorApp.ts +0 -1833
  86. package/src/tui/RootNavigator.ts +0 -74
  87. package/src/tui/SearchApp.ts +0 -511
  88. package/src/tui/SplashScreen.ts +0 -149
  89. package/src/types/admin-config.ts +0 -25
  90. package/src/types/global-config.ts +0 -26
  91. package/src/types/history-types.ts +0 -39
  92. package/src/types/model-info.ts +0 -8
  93. package/src/types/monitor-types.ts +0 -162
  94. package/src/types/router-config.ts +0 -25
  95. package/src/types/server-config.ts +0 -46
  96. package/src/utils/downsample-utils.ts +0 -128
  97. package/src/utils/file-utils.ts +0 -146
  98. package/src/utils/format-utils.ts +0 -98
  99. package/src/utils/log-parser.ts +0 -284
  100. package/src/utils/log-utils.ts +0 -178
  101. package/src/utils/process-utils.ts +0 -316
  102. package/src/utils/prompt-utils.ts +0 -47
  103. package/test-load.sh +0 -100
  104. package/tsconfig.json +0 -20
  105. package/web/eslint.config.js +0 -23
  106. package/web/llamacpp-web-dist.tar.gz +0 -0
  107. package/web/package-lock.json +0 -4017
  108. package/web/package.json +0 -38
  109. package/web/postcss.config.js +0 -6
  110. package/web/src/App.css +0 -42
  111. package/web/src/App.tsx +0 -86
  112. package/web/src/assets/react.svg +0 -1
  113. package/web/src/components/ApiKeyPrompt.tsx +0 -71
  114. package/web/src/components/CreateServerModal.tsx +0 -372
  115. package/web/src/components/DownloadProgress.tsx +0 -123
  116. package/web/src/components/Nav.tsx +0 -89
  117. package/web/src/components/RouterConfigModal.tsx +0 -240
  118. package/web/src/components/SearchModal.tsx +0 -306
  119. package/web/src/components/ServerConfigModal.tsx +0 -291
  120. package/web/src/hooks/useApi.ts +0 -259
  121. package/web/src/index.css +0 -42
  122. package/web/src/lib/api.ts +0 -226
  123. package/web/src/main.tsx +0 -10
  124. package/web/src/pages/Dashboard.tsx +0 -103
  125. package/web/src/pages/Models.tsx +0 -258
  126. package/web/src/pages/Router.tsx +0 -270
  127. package/web/src/pages/RouterLogs.tsx +0 -201
  128. package/web/src/pages/ServerLogs.tsx +0 -553
  129. package/web/src/pages/Servers.tsx +0 -358
  130. package/web/src/types/api.ts +0 -140
  131. package/web/tailwind.config.js +0 -31
  132. package/web/tsconfig.app.json +0 -28
  133. package/web/tsconfig.json +0 -7
  134. package/web/tsconfig.node.json +0 -26
  135. package/web/vite.config.ts +0 -25
  136. /package/web/{public → dist}/vite.svg +0 -0
@@ -1,345 +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
- includeHealth?: boolean;
31
- }
32
-
33
- export async function logsCommand(identifier: string, options: LogsOptions): Promise<void> {
34
- // Find server
35
- const server = await stateManager.findServer(identifier);
36
- if (!server) {
37
- throw new Error(`Server not found: ${identifier}\n\nUse: llamacpp ps`);
38
- }
39
-
40
- // Determine log file (default to stderr where verbose logs go)
41
- const logPath = options.stdout ? server.stdoutPath : server.stderrPath;
42
- const logType = options.stdout ? 'stdout' : 'stderr';
43
-
44
- // Handle --clear-archived option (deletes only archived logs)
45
- if (options.clearArchived) {
46
- const archivedInfo = await deleteArchivedLogs(server.id);
47
-
48
- if (archivedInfo.count === 0) {
49
- console.log(chalk.yellow(`⚠️ No archived logs found for ${server.modelName}`));
50
- console.log(chalk.dim(` Archived logs are created via --rotate or automatic rotation`));
51
- return;
52
- }
53
-
54
- console.log(chalk.green(`✅ Deleted archived logs for ${server.modelName}`));
55
- console.log(chalk.dim(` Files deleted: ${archivedInfo.count}`));
56
- console.log(chalk.dim(` Space freed: ${formatFileSize(archivedInfo.totalSize)}`));
57
- console.log(chalk.dim(` Current logs preserved`));
58
- return;
59
- }
60
-
61
- // Handle --clear-all option (clears both current and archived logs)
62
- if (options.clearAll) {
63
- let totalFreed = 0;
64
- let currentSize = 0;
65
- let archivedSize = 0;
66
-
67
- // Clear current stderr
68
- if (await fileExists(server.stderrPath)) {
69
- currentSize += await getFileSize(server.stderrPath);
70
- await clearLogFile(server.stderrPath);
71
- }
72
-
73
- // Clear current stdout
74
- if (await fileExists(server.stdoutPath)) {
75
- currentSize += await getFileSize(server.stdoutPath);
76
- await clearLogFile(server.stdoutPath);
77
- }
78
-
79
- // Delete all archived logs
80
- const archivedInfo = await deleteArchivedLogs(server.id);
81
- archivedSize = archivedInfo.totalSize;
82
-
83
- totalFreed = currentSize + archivedSize;
84
-
85
- console.log(chalk.green(`✅ Cleared all logs for ${server.modelName}`));
86
- if (currentSize > 0) {
87
- console.log(chalk.dim(` Current logs: ${formatFileSize(currentSize)}`));
88
- }
89
- if (archivedSize > 0) {
90
- console.log(chalk.dim(` Archived logs: ${formatFileSize(archivedSize)} (${archivedInfo.count} file${archivedInfo.count > 1 ? 's' : ''})`));
91
- }
92
- console.log(chalk.dim(` Total freed: ${formatFileSize(totalFreed)}`));
93
- return;
94
- }
95
-
96
- // Handle --clear option
97
- if (options.clear) {
98
- if (!(await fileExists(logPath))) {
99
- console.log(chalk.yellow(`⚠️ No ${logType} found for ${server.modelName}`));
100
- console.log(chalk.dim(` Log file does not exist: ${logPath}`));
101
- return;
102
- }
103
-
104
- const sizeBefore = await getFileSize(logPath);
105
- await clearLogFile(logPath);
106
-
107
- console.log(chalk.green(`✅ Cleared ${logType} for ${server.modelName}`));
108
- console.log(chalk.dim(` Freed: ${formatFileSize(sizeBefore)}`));
109
- console.log(chalk.dim(` ${logPath}`));
110
- return;
111
- }
112
-
113
- // Handle --rotate option
114
- if (options.rotate) {
115
- if (!(await fileExists(logPath))) {
116
- console.log(chalk.yellow(`⚠️ No ${logType} found for ${server.modelName}`));
117
- console.log(chalk.dim(` Log file does not exist: ${logPath}`));
118
- return;
119
- }
120
-
121
- try {
122
- const archivedPath = await rotateLogFile(logPath);
123
- const size = await getFileSize(archivedPath);
124
-
125
- console.log(chalk.green(`✅ Rotated ${logType} for ${server.modelName}`));
126
- console.log(chalk.dim(` Archived: ${formatFileSize(size)}`));
127
- console.log(chalk.dim(` → ${archivedPath}`));
128
- } catch (error) {
129
- throw new Error(`Failed to rotate log: ${(error as Error).message}`);
130
- }
131
- return;
132
- }
133
-
134
- // Check if log file exists
135
- if (!(await fileExists(logPath))) {
136
- console.log(chalk.yellow(`⚠️ No ${logType} found for ${server.modelName}`));
137
- console.log(chalk.dim(` Log file does not exist: ${logPath}`));
138
- return;
139
- }
140
-
141
- // Determine filter pattern and mode
142
- let filterPattern: string | null = null;
143
- let filterDesc = '';
144
- let useCompactMode = false;
145
-
146
- // Whether to include health check requests (filtered by default)
147
- const includeHealth = options.includeHealth ?? false;
148
-
149
- if (options.verbose) {
150
- // Show everything (no filter)
151
- filterDesc = ' (all messages)';
152
- } else if (options.errors) {
153
- // Show only errors
154
- filterPattern = 'error|Error|ERROR|failed|Failed|FAILED';
155
- filterDesc = ' (errors only)';
156
- } else if (options.http) {
157
- // Full HTTP JSON logs
158
- filterPattern = 'log_server_r';
159
- filterDesc = ' (HTTP JSON)';
160
- } else if (options.filter) {
161
- // Custom filter
162
- filterPattern = options.filter;
163
- filterDesc = ` (filter: ${options.filter})`;
164
- } else {
165
- // Default: Compact one-liner format
166
- filterPattern = 'log_server_r';
167
- filterDesc = ' (compact)';
168
- useCompactMode = true;
169
- }
170
-
171
- console.log(chalk.blue(`📋 Logs for ${server.modelName} (${logType}${filterDesc})`));
172
- console.log(chalk.dim(` ${logPath}`));
173
-
174
- // Show log size information
175
- const currentSize = await getFileSize(logPath);
176
- const archivedInfo = await getArchivedLogInfo(server.id);
177
-
178
- if (archivedInfo.count > 0) {
179
- console.log(chalk.dim(` Current: ${formatFileSize(currentSize)} | Archived: ${formatFileSize(archivedInfo.totalSize)} (${archivedInfo.count} file${archivedInfo.count > 1 ? 's' : ''})`));
180
- } else {
181
- console.log(chalk.dim(` Current: ${formatFileSize(currentSize)}`));
182
- }
183
-
184
- // Show subtle note if verbose logging is not enabled
185
- if (!server.verbose && !options.verbose && !options.errors && !options.http && !options.filter) {
186
- console.log(chalk.dim(` verbosity is disabled`));
187
- }
188
- console.log();
189
-
190
- if (options.follow) {
191
- // Follow logs in real-time with optional filtering
192
- if (useCompactMode) {
193
- // Compact mode with follow: parse lines in real-time
194
- const tailProcess = spawn('tail', ['-f', logPath]);
195
- const rl = readline.createInterface({
196
- input: tailProcess.stdout,
197
- crlfDelay: Infinity,
198
- });
199
-
200
- rl.on('line', (line) => {
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
- }
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
- }
211
- console.log(compactLine);
212
- });
213
- }
214
- });
215
-
216
- // Handle Ctrl+C gracefully
217
- process.on('SIGINT', () => {
218
- tailProcess.kill();
219
- rl.close();
220
- console.log();
221
- process.exit(0);
222
- });
223
-
224
- tailProcess.on('exit', () => {
225
- process.exit(0);
226
- });
227
- } else if (filterPattern) {
228
- // Use tail piped to grep for filtering
229
- const grepProcess = spawn('sh', ['-c', `tail -f "${logPath}" | grep --line-buffered -E "${filterPattern}"`], {
230
- stdio: 'inherit',
231
- });
232
-
233
- // Handle Ctrl+C gracefully
234
- process.on('SIGINT', () => {
235
- grepProcess.kill();
236
- console.log();
237
- process.exit(0);
238
- });
239
-
240
- grepProcess.on('exit', () => {
241
- process.exit(0);
242
- });
243
- } else {
244
- // No filter, just tail
245
- const tail = spawn('tail', ['-f', logPath], {
246
- stdio: 'inherit',
247
- });
248
-
249
- process.on('SIGINT', () => {
250
- tail.kill();
251
- console.log();
252
- process.exit(0);
253
- });
254
-
255
- tail.on('exit', () => {
256
- process.exit(0);
257
- });
258
- }
259
- } else {
260
- // Show last N lines with optional filtering
261
- const lines = options.lines || 50;
262
-
263
- if (useCompactMode) {
264
- // Compact mode: read file and parse
265
- try {
266
- // Use large multiplier to account for verbose debug output between requests
267
- // Add || true to prevent grep from failing when no matches found
268
- const command = `tail -n ${lines * 100} "${logPath}" | grep -E "log_server_r" || true`;
269
- const output = await execCommand(command);
270
- const logLines = output.split('\n').filter((l) => l.trim());
271
-
272
- if (logLines.length === 0) {
273
- console.log(chalk.dim('No HTTP request logs in compact format.'));
274
- console.log(chalk.dim('The server may be starting up, or only simple GET requests have been made.'));
275
- console.log(chalk.dim('\nTip: Use --http to see raw HTTP logs, or --verbose for all server logs.'));
276
- return;
277
- }
278
-
279
- const compactLines: string[] = [];
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
- }
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
- }
290
- compactLines.push(compactLine);
291
- });
292
- }
293
-
294
- // Flush any remaining buffered logs (handles simple format)
295
- logParser.flush((compactLine) => {
296
- // Filter health checks from flushed lines too
297
- if (!includeHealth && logParser.isHealthCheckRequest(compactLine)) {
298
- return;
299
- }
300
- compactLines.push(compactLine);
301
- });
302
-
303
- // Check if we got any parsed output
304
- if (compactLines.length === 0) {
305
- console.log(chalk.dim('HTTP request logs found, but could not parse in compact format.'));
306
- console.log(chalk.dim('This usually happens with simple GET requests (health checks, slots, etc.).'));
307
- console.log(chalk.dim('\nTip: Use --http to see raw HTTP logs instead.'));
308
- return;
309
- }
310
-
311
- // Show only the last N compact lines
312
- const limitedLines = compactLines.slice(-lines);
313
- limitedLines.forEach((line) => console.log(line));
314
- } catch (error) {
315
- throw new Error(`Failed to read logs: ${(error as Error).message}`);
316
- }
317
- } else {
318
- // Regular filtering
319
- try {
320
- let command: string;
321
-
322
- if (filterPattern) {
323
- // Use tail piped to grep
324
- // Add || true to prevent grep from failing when no matches found
325
- command = `tail -n ${lines} "${logPath}" | grep -E "${filterPattern}" || true`;
326
- } else {
327
- // No filter
328
- command = `tail -n ${lines} "${logPath}"`;
329
- }
330
-
331
- const output = await execCommand(command);
332
-
333
- if (filterPattern && output.trim() === '') {
334
- console.log(chalk.dim(`No logs matching pattern: ${filterPattern}`));
335
- console.log(chalk.dim('\nTip: Try --verbose to see all logs, or adjust your filter pattern.'));
336
- return;
337
- }
338
-
339
- console.log(output);
340
- } catch (error) {
341
- throw new Error(`Failed to read logs: ${(error as Error).message}`);
342
- }
343
- }
344
- }
345
- }
@@ -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
- }
@@ -1,84 +0,0 @@
1
- import chalk from 'chalk';
2
- import Table from 'cli-table3';
3
- import { stateManager } from '../lib/state-manager.js';
4
- import { statusChecker } from '../lib/status-checker.js';
5
- import { formatUptime, formatBytes } from '../utils/format-utils.js';
6
- import { getProcessMemory } from '../utils/process-utils.js';
7
- import { ServerConfig } from '../types/server-config.js';
8
-
9
- const STATUS_CONFIG = {
10
- running: { text: '✅ RUNNING', color: chalk.green },
11
- crashed: { text: '❌ CRASHED', color: chalk.red },
12
- stopped: { text: '⚠️ STOPPED', color: chalk.yellow },
13
- } as const;
14
-
15
- async function getServerMemory(server: ServerConfig): Promise<string> {
16
- if (server.status !== 'running' || !server.pid) {
17
- return '-';
18
- }
19
-
20
- const cpuMemoryBytes = await getProcessMemory(server.pid);
21
- if (cpuMemoryBytes === null) {
22
- return '-';
23
- }
24
-
25
- const metalMemoryBytes = server.metalMemoryMB ? server.metalMemoryMB * 1024 * 1024 : 0;
26
- return formatBytes(cpuMemoryBytes + metalMemoryBytes);
27
- }
28
-
29
- export async function psCommand(): Promise<void> {
30
- const servers = await stateManager.getAllServers();
31
-
32
- if (servers.length === 0) {
33
- console.log(chalk.yellow('No servers configured.'));
34
- console.log(chalk.dim('\nCreate a server: llamacpp server create <model-filename>'));
35
- return;
36
- }
37
-
38
- console.log(chalk.dim('Checking server statuses...\n'));
39
- const serversWithStatus = await statusChecker.updateAllServerStatuses();
40
-
41
- const table = new Table({
42
- head: ['SERVER ID', 'MODEL', 'PORT', 'STATUS', 'PID', 'MEMORY', 'UPTIME'],
43
- });
44
-
45
- const counts = { running: 0, stopped: 0, crashed: 0 };
46
-
47
- for (const server of serversWithStatus) {
48
- const status = server.status || 'stopped';
49
- const config = STATUS_CONFIG[status] || STATUS_CONFIG.stopped;
50
- counts[status]++;
51
-
52
- const uptime = server.status === 'running' && server.lastStarted
53
- ? formatUptime(server.lastStarted)
54
- : '-';
55
-
56
- const memoryText = await getServerMemory(server);
57
-
58
- table.push([
59
- server.id,
60
- server.modelName,
61
- server.port.toString(),
62
- config.color(config.text),
63
- server.pid?.toString() || '-',
64
- memoryText,
65
- uptime,
66
- ]);
67
- }
68
-
69
- console.log(table.toString());
70
-
71
- const summary = [
72
- chalk.green(`${counts.running} running`),
73
- chalk.yellow(`${counts.stopped} stopped`),
74
- ];
75
- if (counts.crashed > 0) {
76
- summary.push(chalk.red(`${counts.crashed} crashed`));
77
- }
78
-
79
- console.log(chalk.dim(`\nTotal: ${servers.length} servers (${summary.join(', ')})`));
80
-
81
- if (counts.crashed > 0) {
82
- console.log(chalk.red('\n⚠️ Some servers have crashed. Check logs with: llamacpp server logs <id> --errors'));
83
- }
84
- }
@@ -1,44 +0,0 @@
1
- import chalk from 'chalk';
2
- import { modelDownloader } from '../lib/model-downloader';
3
- import { ensureModelsDirectory } from '../lib/models-dir-setup';
4
-
5
- interface PullOptions {
6
- file?: string;
7
- }
8
-
9
- export async function pullCommand(identifier: string, options: PullOptions): Promise<void> {
10
- // Parse repository identifier
11
- const parsed = modelDownloader.parseHFIdentifier(identifier);
12
-
13
- // Determine filename - from --file flag or from identifier path
14
- let filename = options.file || parsed.file;
15
-
16
- if (!filename) {
17
- throw new Error(
18
- 'Please specify a file to download:\n\n' +
19
- 'Option 1: llamacpp pull owner/repo/filename.gguf\n' +
20
- 'Option 2: llamacpp pull owner/repo --file filename.gguf'
21
- );
22
- }
23
-
24
- // Ensure filename ends with .gguf
25
- if (!filename.toLowerCase().endsWith('.gguf')) {
26
- filename += '.gguf';
27
- }
28
-
29
- // Ensure models directory exists (prompts user if needed)
30
- const modelsDir = await ensureModelsDirectory();
31
-
32
- // Download the model
33
- try {
34
- const modelPath = await modelDownloader.downloadModel(parsed.repo, filename, undefined, modelsDir);
35
-
36
- console.log();
37
- console.log(chalk.dim(`Create server: llamacpp server create ${filename}`));
38
- } catch (error) {
39
- if ((error as Error).message.includes('interrupted')) {
40
- console.log(chalk.dim('\nDownload was interrupted. Run the same command again to retry.'));
41
- }
42
- throw error;
43
- }
44
- }
@@ -1,107 +0,0 @@
1
- import chalk from 'chalk';
2
- import * as readline from 'readline';
3
- import * as fs from 'fs/promises';
4
- import { modelScanner } from '../lib/model-scanner';
5
- import { stateManager } from '../lib/state-manager';
6
- import { launchctlManager } from '../lib/launchctl-manager';
7
-
8
- export async function rmCommand(modelIdentifier: string): Promise<void> {
9
- await stateManager.initialize();
10
-
11
- // 1. Resolve model path
12
- const modelPath = await modelScanner.resolveModelPath(modelIdentifier);
13
- if (!modelPath) {
14
- throw new Error(`Model not found: ${modelIdentifier}\n\nRun: llamacpp ls`);
15
- }
16
-
17
- // 2. Check if any servers are using this model
18
- const allServers = await stateManager.getAllServers();
19
- const serversUsingModel = allServers.filter((s) => s.modelPath === modelPath);
20
-
21
- // 3. Confirm deletion
22
- console.log(chalk.yellow(`⚠️ Delete model file: ${modelPath}`));
23
-
24
- if (serversUsingModel.length > 0) {
25
- console.log(chalk.yellow(`\n This model has ${serversUsingModel.length} server(s) configured:`));
26
- for (const server of serversUsingModel) {
27
- const statusColor = server.status === 'running' ? chalk.green : chalk.dim;
28
- console.log(chalk.yellow(` - ${server.id} (${statusColor(server.status)})`));
29
- }
30
- console.log(chalk.yellow(`\n These servers will be removed before deleting the model.`));
31
- }
32
-
33
- console.log();
34
-
35
- const confirmed = await confirmDeletion();
36
- if (!confirmed) {
37
- console.log(chalk.dim('Cancelled'));
38
- return;
39
- }
40
-
41
- console.log();
42
-
43
- // 4. Delete all servers using this model
44
- if (serversUsingModel.length > 0) {
45
- console.log(chalk.blue(`🗑️ Removing ${serversUsingModel.length} server(s)...\n`));
46
-
47
- for (const server of serversUsingModel) {
48
- console.log(chalk.dim(` Removing server: ${server.id}`));
49
-
50
- // Unload service (stops and removes from launchd)
51
- try {
52
- await launchctlManager.unloadService(server.plistPath);
53
- if (server.status === 'running') {
54
- await launchctlManager.waitForServiceStop(server.label, 5000);
55
- }
56
- } catch (error) {
57
- console.log(chalk.yellow(` ⚠️ Failed to unload service gracefully`));
58
- }
59
-
60
- // Delete plist
61
- await launchctlManager.deletePlist(server.plistPath);
62
-
63
- // Delete server config
64
- await stateManager.deleteServerConfig(server.id);
65
-
66
- console.log(chalk.dim(` ✓ Server removed`));
67
- }
68
-
69
- console.log();
70
- }
71
-
72
- // 5. Delete model file
73
- console.log(chalk.blue(`🗑️ Deleting model file...`));
74
-
75
- try {
76
- await fs.unlink(modelPath);
77
- } catch (error) {
78
- throw new Error(`Failed to delete model file: ${(error as Error).message}`);
79
- }
80
-
81
- // Success
82
- console.log();
83
- console.log(chalk.green('✅ Model deleted successfully'));
84
-
85
- if (serversUsingModel.length > 0) {
86
- console.log(chalk.dim(` Removed ${serversUsingModel.length} server(s)`));
87
- }
88
-
89
- console.log(chalk.dim(` Deleted: ${modelPath}`));
90
- }
91
-
92
- /**
93
- * Prompt user for confirmation
94
- */
95
- function confirmDeletion(): Promise<boolean> {
96
- return new Promise((resolve) => {
97
- const rl = readline.createInterface({
98
- input: process.stdin,
99
- output: process.stdout,
100
- });
101
-
102
- rl.question(chalk.yellow(" Type 'yes' to confirm: "), (answer) => {
103
- rl.close();
104
- resolve(answer.toLowerCase() === 'yes');
105
- });
106
- });
107
- }