@appkit/llamacpp-cli 1.12.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 (114) hide show
  1. package/README.md +217 -168
  2. package/package.json +10 -2
  3. package/web/dist/assets/index-Bin89Lwr.css +1 -0
  4. package/web/dist/assets/index-CVmonw3T.js +17 -0
  5. package/web/{index.html → dist/index.html} +2 -1
  6. package/.versionrc.json +0 -16
  7. package/CHANGELOG.md +0 -213
  8. package/docs/images/.gitkeep +0 -1
  9. package/docs/images/web-ui-servers.png +0 -0
  10. package/src/cli.ts +0 -523
  11. package/src/commands/admin/config.ts +0 -121
  12. package/src/commands/admin/logs.ts +0 -91
  13. package/src/commands/admin/restart.ts +0 -26
  14. package/src/commands/admin/start.ts +0 -27
  15. package/src/commands/admin/status.ts +0 -84
  16. package/src/commands/admin/stop.ts +0 -16
  17. package/src/commands/config-global.ts +0 -38
  18. package/src/commands/config.ts +0 -323
  19. package/src/commands/create.ts +0 -183
  20. package/src/commands/delete.ts +0 -74
  21. package/src/commands/list.ts +0 -37
  22. package/src/commands/logs-all.ts +0 -251
  23. package/src/commands/logs.ts +0 -345
  24. package/src/commands/monitor.ts +0 -110
  25. package/src/commands/ps.ts +0 -84
  26. package/src/commands/pull.ts +0 -44
  27. package/src/commands/rm.ts +0 -107
  28. package/src/commands/router/config.ts +0 -116
  29. package/src/commands/router/logs.ts +0 -256
  30. package/src/commands/router/restart.ts +0 -36
  31. package/src/commands/router/start.ts +0 -60
  32. package/src/commands/router/status.ts +0 -119
  33. package/src/commands/router/stop.ts +0 -33
  34. package/src/commands/run.ts +0 -233
  35. package/src/commands/search.ts +0 -107
  36. package/src/commands/server-show.ts +0 -161
  37. package/src/commands/show.ts +0 -207
  38. package/src/commands/start.ts +0 -101
  39. package/src/commands/stop.ts +0 -39
  40. package/src/commands/tui.ts +0 -25
  41. package/src/lib/admin-manager.ts +0 -435
  42. package/src/lib/admin-server.ts +0 -1243
  43. package/src/lib/config-generator.ts +0 -130
  44. package/src/lib/download-job-manager.ts +0 -213
  45. package/src/lib/history-manager.ts +0 -172
  46. package/src/lib/launchctl-manager.ts +0 -225
  47. package/src/lib/metrics-aggregator.ts +0 -257
  48. package/src/lib/model-downloader.ts +0 -328
  49. package/src/lib/model-scanner.ts +0 -157
  50. package/src/lib/model-search.ts +0 -114
  51. package/src/lib/models-dir-setup.ts +0 -46
  52. package/src/lib/port-manager.ts +0 -80
  53. package/src/lib/router-logger.ts +0 -201
  54. package/src/lib/router-manager.ts +0 -414
  55. package/src/lib/router-server.ts +0 -538
  56. package/src/lib/state-manager.ts +0 -206
  57. package/src/lib/status-checker.ts +0 -113
  58. package/src/lib/system-collector.ts +0 -315
  59. package/src/tui/ConfigApp.ts +0 -1085
  60. package/src/tui/HistoricalMonitorApp.ts +0 -587
  61. package/src/tui/ModelsApp.ts +0 -368
  62. package/src/tui/MonitorApp.ts +0 -386
  63. package/src/tui/MultiServerMonitorApp.ts +0 -1833
  64. package/src/tui/RootNavigator.ts +0 -74
  65. package/src/tui/SearchApp.ts +0 -511
  66. package/src/tui/SplashScreen.ts +0 -149
  67. package/src/types/admin-config.ts +0 -25
  68. package/src/types/global-config.ts +0 -26
  69. package/src/types/history-types.ts +0 -39
  70. package/src/types/model-info.ts +0 -8
  71. package/src/types/monitor-types.ts +0 -162
  72. package/src/types/router-config.ts +0 -25
  73. package/src/types/server-config.ts +0 -46
  74. package/src/utils/downsample-utils.ts +0 -128
  75. package/src/utils/file-utils.ts +0 -146
  76. package/src/utils/format-utils.ts +0 -98
  77. package/src/utils/log-parser.ts +0 -284
  78. package/src/utils/log-utils.ts +0 -178
  79. package/src/utils/process-utils.ts +0 -316
  80. package/src/utils/prompt-utils.ts +0 -47
  81. package/test-load.sh +0 -100
  82. package/tsconfig.json +0 -20
  83. package/web/eslint.config.js +0 -23
  84. package/web/llamacpp-web-dist.tar.gz +0 -0
  85. package/web/package-lock.json +0 -4017
  86. package/web/package.json +0 -38
  87. package/web/postcss.config.js +0 -6
  88. package/web/src/App.css +0 -42
  89. package/web/src/App.tsx +0 -86
  90. package/web/src/assets/react.svg +0 -1
  91. package/web/src/components/ApiKeyPrompt.tsx +0 -71
  92. package/web/src/components/CreateServerModal.tsx +0 -372
  93. package/web/src/components/DownloadProgress.tsx +0 -123
  94. package/web/src/components/Nav.tsx +0 -89
  95. package/web/src/components/RouterConfigModal.tsx +0 -240
  96. package/web/src/components/SearchModal.tsx +0 -306
  97. package/web/src/components/ServerConfigModal.tsx +0 -291
  98. package/web/src/hooks/useApi.ts +0 -259
  99. package/web/src/index.css +0 -42
  100. package/web/src/lib/api.ts +0 -226
  101. package/web/src/main.tsx +0 -10
  102. package/web/src/pages/Dashboard.tsx +0 -103
  103. package/web/src/pages/Models.tsx +0 -258
  104. package/web/src/pages/Router.tsx +0 -270
  105. package/web/src/pages/RouterLogs.tsx +0 -201
  106. package/web/src/pages/ServerLogs.tsx +0 -553
  107. package/web/src/pages/Servers.tsx +0 -358
  108. package/web/src/types/api.ts +0 -140
  109. package/web/tailwind.config.js +0 -31
  110. package/web/tsconfig.app.json +0 -28
  111. package/web/tsconfig.json +0 -7
  112. package/web/tsconfig.node.json +0 -26
  113. package/web/vite.config.ts +0 -25
  114. /package/web/{public → dist}/vite.svg +0 -0
@@ -1,386 +0,0 @@
1
- import blessed from 'blessed';
2
- import { ServerConfig } from '../types/server-config.js';
3
- import { MetricsAggregator } from '../lib/metrics-aggregator.js';
4
- import { MonitorData } from '../types/monitor-types.js';
5
- import { HistoryManager } from '../lib/history-manager.js';
6
- import { createHistoricalUI } from './HistoricalMonitorApp.js';
7
-
8
- export async function createMonitorUI(
9
- screen: blessed.Widgets.Screen,
10
- server: ServerConfig
11
- ): Promise<void> {
12
- let updateInterval = 2000;
13
- let intervalId: NodeJS.Timeout | null = null;
14
- let consecutiveFailures = 0;
15
- let lastGoodData: MonitorData | null = null;
16
- const STALE_THRESHOLD = 5;
17
- const metricsAggregator = new MetricsAggregator(server);
18
- const historyManager = new HistoryManager(server.id);
19
-
20
- // Single scrollable content box
21
- const contentBox = blessed.box({
22
- top: 0,
23
- left: 0,
24
- width: '100%',
25
- height: '100%',
26
- tags: true,
27
- scrollable: true,
28
- alwaysScroll: true,
29
- keys: true,
30
- vi: true,
31
- mouse: true,
32
- scrollbar: {
33
- ch: '█',
34
- style: {
35
- fg: 'blue',
36
- },
37
- },
38
- });
39
- screen.append(contentBox);
40
-
41
- // Helper to create progress bar
42
- function createProgressBar(percentage: number, width: number = 30): string {
43
- const filled = Math.round((percentage / 100) * width);
44
- const empty = width - filled;
45
- return '[' + '█'.repeat(Math.max(0, filled)) + '░'.repeat(Math.max(0, empty)) + ']';
46
- }
47
-
48
- // Fetch and update display
49
- async function fetchData() {
50
- try {
51
- const data = await metricsAggregator.collectMonitorData(server, updateInterval);
52
-
53
- // Reset failure count on success
54
- consecutiveFailures = 0;
55
- lastGoodData = data;
56
-
57
- // Append to history (silent failure)
58
- // Only save history for servers that are healthy and not stale
59
- if (!data.server.stale && data.server.healthy) {
60
- historyManager.appendSnapshot(data.server, data.system).catch(() => {
61
- // Don't interrupt monitoring on history write failure
62
- });
63
- }
64
-
65
- const termWidth = (screen.width as number) || 80;
66
- const divider = '─'.repeat(termWidth - 2); // Account for padding
67
-
68
- let content = '';
69
-
70
- // Header
71
- content += `{bold}{blue-fg}═══ ${server.modelName} (${server.port}){/blue-fg}{/bold}\n\n`;
72
-
73
- // Server Info
74
- content += '{bold}Server Information{/bold}\n';
75
- content += divider + '\n';
76
-
77
- const statusIcon = data.server.healthy ? '{green-fg}●{/green-fg}' : '{red-fg}●{/red-fg}';
78
- const statusText = data.server.healthy ? 'RUNNING' : 'UNHEALTHY';
79
- content += `Status: ${statusIcon} ${statusText}\n`;
80
-
81
- if (data.server.uptime) {
82
- content += `Uptime: ${data.server.uptime}\n`;
83
- }
84
-
85
- content += `Model: ${server.modelName}\n`;
86
- // Handle null host (legacy configs) by defaulting to 127.0.0.1
87
- const displayHost = server.host || '127.0.0.1';
88
- content += `Endpoint: http://${displayHost}:${server.port}\n`;
89
- content += `Slots: ${data.server.activeSlots} active / ${data.server.totalSlots} total\n`;
90
- content += '\n';
91
-
92
- // Request Metrics
93
- if (data.server.totalSlots > 0) {
94
- content += '{bold}Request Metrics{/bold}\n';
95
- content += divider + '\n';
96
- content += `Active: ${data.server.activeSlots} / ${data.server.totalSlots}\n`;
97
- content += `Idle: ${data.server.idleSlots} / ${data.server.totalSlots}\n`;
98
-
99
- if (data.server.avgPromptSpeed !== undefined && data.server.avgPromptSpeed > 0) {
100
- content += `Prompt: ${Math.round(data.server.avgPromptSpeed)} tokens/sec\n`;
101
- }
102
-
103
- if (data.server.avgGenerateSpeed !== undefined && data.server.avgGenerateSpeed > 0) {
104
- content += `Generate: ${Math.round(data.server.avgGenerateSpeed)} tokens/sec\n`;
105
- }
106
-
107
- content += '\n';
108
- }
109
-
110
- // Active Slots Detail
111
- if (data.server.slots.length > 0) {
112
- const activeSlots = data.server.slots.filter(s => s.state === 'processing');
113
-
114
- if (activeSlots.length > 0) {
115
- content += '{bold}Active Slots{/bold}\n';
116
- content += divider + '\n';
117
-
118
- activeSlots.forEach((slot) => {
119
- content += `Slot #${slot.id}: {yellow-fg}PROCESSING{/yellow-fg}`;
120
-
121
- if (slot.timings?.predicted_per_second) {
122
- content += ` - ${Math.round(slot.timings.predicted_per_second)} tok/s`;
123
- }
124
-
125
- if (slot.n_decoded !== undefined) {
126
- content += ` - ${slot.n_decoded} tokens`;
127
- }
128
-
129
- content += '\n';
130
- });
131
-
132
- content += '\n';
133
- }
134
- }
135
-
136
- // Model Resources (per-process metrics)
137
- content += '{bold}Model Resources{/bold}\n';
138
- content += divider + '\n';
139
-
140
- // GPU: System-wide (can't get per-process on macOS)
141
- if (data.system && data.system.gpuUsage !== undefined) {
142
- const bar = createProgressBar(data.system.gpuUsage);
143
- content += `GPU: {cyan-fg}${bar}{/cyan-fg} ${Math.round(data.system.gpuUsage)}% {gray-fg}(system){/gray-fg}`;
144
-
145
- if (data.system.temperature !== undefined) {
146
- content += ` - ${Math.round(data.system.temperature)}°C`;
147
- }
148
-
149
- content += '\n';
150
- }
151
-
152
- // CPU: Per-process
153
- if (data.server.processCpuUsage !== undefined) {
154
- const bar = createProgressBar(data.server.processCpuUsage);
155
- content += `CPU: {cyan-fg}${bar}{/cyan-fg} ${Math.round(data.server.processCpuUsage)}%\n`;
156
- }
157
-
158
- // Memory: Per-process
159
- if (data.server.processMemory !== undefined) {
160
- const memoryGB = data.server.processMemory / (1024 ** 3);
161
- // For progress bar, estimate against typical model sizes (e.g., 8GB max)
162
- const estimatedMax = 8;
163
- const memoryPercentage = Math.min((memoryGB / estimatedMax) * 100, 100);
164
- const bar = createProgressBar(memoryPercentage);
165
- content += `Memory: {cyan-fg}${bar}{/cyan-fg} ${memoryGB.toFixed(2)} GB\n`;
166
- }
167
-
168
- if (data.system && data.system.warnings && data.system.warnings.length > 0) {
169
- content += `\n{yellow-fg}⚠ ${data.system.warnings.join(', ')}{/yellow-fg}\n`;
170
- }
171
-
172
- content += '\n';
173
-
174
- // Footer
175
- content += divider + '\n';
176
- content += `{gray-fg}Updated: ${data.lastUpdated.toLocaleTimeString()} | `;
177
- content += `Interval: ${updateInterval}ms | `;
178
- content += `[H]istory [R]efresh [+/-]Speed [Q]uit{/gray-fg}`;
179
-
180
- contentBox.setContent(content);
181
- screen.render();
182
-
183
- } catch (err) {
184
- consecutiveFailures++;
185
- const isStale = consecutiveFailures >= STALE_THRESHOLD;
186
-
187
- // If we have last good data and we're stale, show it with indicator
188
- if (lastGoodData && isStale) {
189
- const termWidth = (screen.width as number) || 80;
190
- const divider = '─'.repeat(termWidth - 2);
191
-
192
- let content = '';
193
-
194
- // Header with stale warning
195
- content += `{bold}{blue-fg}═══ ${server.modelName} (${server.port}){/blue-fg}{/bold}\n`;
196
- content += '{bold}{yellow-fg}⚠ CONNECTION LOST - SHOWING STALE DATA{/yellow-fg}{/bold}\n\n';
197
-
198
- // Server Info
199
- content += '{bold}Server Information{/bold}\n';
200
- content += divider + '\n';
201
-
202
- const statusIcon = '{yellow-fg}●{/yellow-fg}';
203
- const statusText = 'STALE';
204
- content += `Status: ${statusIcon} ${statusText}\n`;
205
-
206
- if (lastGoodData.server.uptime) {
207
- content += `Uptime: ${lastGoodData.server.uptime}\n`;
208
- }
209
-
210
- content += `Model: ${server.modelName}\n`;
211
- // Handle null host (legacy configs) by defaulting to 127.0.0.1
212
- const displayHost = server.host || '127.0.0.1';
213
- content += `Endpoint: http://${displayHost}:${server.port}\n`;
214
- content += `Slots: ${lastGoodData.server.activeSlots} active / ${lastGoodData.server.totalSlots} total\n\n`;
215
-
216
- // Request Metrics
217
- if (lastGoodData.server.totalSlots > 0) {
218
- content += '{bold}Request Metrics{/bold} {yellow-fg}(stale){/yellow-fg}\n';
219
- content += divider + '\n';
220
- content += `Active: ${lastGoodData.server.activeSlots} / ${lastGoodData.server.totalSlots}\n`;
221
- content += `Idle: ${lastGoodData.server.idleSlots} / ${lastGoodData.server.totalSlots}\n`;
222
-
223
- if (lastGoodData.server.avgPromptSpeed !== undefined && lastGoodData.server.avgPromptSpeed > 0) {
224
- content += `Prompt: ${Math.round(lastGoodData.server.avgPromptSpeed)} tokens/sec\n`;
225
- }
226
-
227
- if (lastGoodData.server.avgGenerateSpeed !== undefined && lastGoodData.server.avgGenerateSpeed > 0) {
228
- content += `Generate: ${Math.round(lastGoodData.server.avgGenerateSpeed)} tokens/sec\n`;
229
- }
230
-
231
- content += '\n';
232
- }
233
-
234
- // Active Slots Detail
235
- if (lastGoodData.server.slots.length > 0) {
236
- const activeSlots = lastGoodData.server.slots.filter(s => s.state === 'processing');
237
-
238
- if (activeSlots.length > 0) {
239
- content += '{bold}Active Slots{/bold} {yellow-fg}(stale){/yellow-fg}\n';
240
- content += divider + '\n';
241
-
242
- activeSlots.forEach((slot) => {
243
- content += `Slot #${slot.id}: {yellow-fg}PROCESSING{/yellow-fg}`;
244
-
245
- if (slot.timings?.predicted_per_second) {
246
- content += ` - ${Math.round(slot.timings.predicted_per_second)} tok/s`;
247
- }
248
-
249
- if (slot.n_decoded !== undefined) {
250
- content += ` - ${slot.n_decoded} tokens`;
251
- }
252
-
253
- content += '\n';
254
- });
255
-
256
- content += '\n';
257
- }
258
- }
259
-
260
- // Model Resources (per-process metrics)
261
- content += '{bold}Model Resources{/bold} {yellow-fg}(stale){/yellow-fg}\n';
262
- content += divider + '\n';
263
-
264
- // GPU: System-wide (can't get per-process on macOS)
265
- if (lastGoodData.system && lastGoodData.system.gpuUsage !== undefined) {
266
- const bar = createProgressBar(lastGoodData.system.gpuUsage);
267
- content += `GPU: {cyan-fg}${bar}{/cyan-fg} ${Math.round(lastGoodData.system.gpuUsage)}% {gray-fg}(system){/gray-fg}`;
268
-
269
- if (lastGoodData.system.temperature !== undefined) {
270
- content += ` - ${Math.round(lastGoodData.system.temperature)}°C`;
271
- }
272
-
273
- content += '\n';
274
- }
275
-
276
- // CPU: Per-process
277
- if (lastGoodData.server.processCpuUsage !== undefined) {
278
- const bar = createProgressBar(lastGoodData.server.processCpuUsage);
279
- content += `CPU: {cyan-fg}${bar}{/cyan-fg} ${Math.round(lastGoodData.server.processCpuUsage)}%\n`;
280
- }
281
-
282
- // Memory: Per-process
283
- if (lastGoodData.server.processMemory !== undefined) {
284
- const memoryGB = lastGoodData.server.processMemory / (1024 ** 3);
285
- const estimatedMax = 8;
286
- const memoryPercentage = Math.min((memoryGB / estimatedMax) * 100, 100);
287
- const bar = createProgressBar(memoryPercentage);
288
- content += `Memory: {cyan-fg}${bar}{/cyan-fg} ${memoryGB.toFixed(2)} GB\n`;
289
- }
290
-
291
- if (lastGoodData.system && lastGoodData.system.warnings && lastGoodData.system.warnings.length > 0) {
292
- content += `\n{yellow-fg}⚠ ${lastGoodData.system.warnings.join(', ')}{/yellow-fg}\n`;
293
- }
294
-
295
- content += '\n';
296
-
297
- // Footer
298
- content += divider + '\n';
299
- content += `{yellow-fg}Last good data: ${lastGoodData.lastUpdated.toLocaleTimeString()}{/yellow-fg}\n`;
300
- content += `{yellow-fg}Connection failures: ${consecutiveFailures}{/yellow-fg}\n`;
301
- content += `{gray-fg}Interval: ${updateInterval}ms | [H]istory [R]efresh [+/-]Speed [Q]uit{/gray-fg}`;
302
-
303
- contentBox.setContent(content);
304
- screen.render();
305
- } else if (!lastGoodData || consecutiveFailures < STALE_THRESHOLD) {
306
- // Show connection error (either no last data or not stale yet)
307
- const errorMsg = err instanceof Error ? err.message : 'Unknown error';
308
- const retryMsg = consecutiveFailures < STALE_THRESHOLD
309
- ? `Retrying... (${consecutiveFailures}/${STALE_THRESHOLD})`
310
- : 'Connection lost';
311
-
312
- contentBox.setContent(
313
- '{bold}{red-fg}Connection Error{/red-fg}{/bold}\n\n' +
314
- `{red-fg}${errorMsg}{/red-fg}\n\n` +
315
- `{yellow-fg}${retryMsg}{/yellow-fg}\n\n` +
316
- '{gray-fg}Press [R] to retry or [Q] to quit{/gray-fg}'
317
- );
318
- screen.render();
319
- }
320
- }
321
- }
322
-
323
- // Polling
324
- function startPolling() {
325
- if (intervalId) clearInterval(intervalId);
326
- fetchData();
327
- intervalId = setInterval(fetchData, updateInterval);
328
- }
329
-
330
- // Keyboard shortcuts
331
- screen.key(['r', 'R'], () => {
332
- fetchData();
333
- });
334
-
335
- screen.key(['+', '='], () => {
336
- updateInterval = Math.max(500, updateInterval - 500);
337
- startPolling();
338
- });
339
-
340
- screen.key(['-', '_'], () => {
341
- updateInterval = Math.min(10000, updateInterval + 500);
342
- startPolling();
343
- });
344
-
345
- // Track whether we're in historical view to prevent H key conflicts
346
- let inHistoricalView = false;
347
-
348
- screen.key(['h', 'H'], async () => {
349
- // Prevent entering historical view if already there
350
- if (inHistoricalView) return;
351
-
352
- // Keep polling in background for live historical updates
353
- // Remove current content box
354
- screen.remove(contentBox);
355
-
356
- // Mark that we're in historical view
357
- inHistoricalView = true;
358
-
359
- // Show historical view (polling continues in background)
360
- await createHistoricalUI(screen, server, () => {
361
- // Mark that we've left historical view
362
- inHistoricalView = false;
363
- // Re-attach content box when returning from history
364
- screen.append(contentBox);
365
- });
366
- });
367
-
368
- screen.key(['q', 'Q', 'C-c'], () => {
369
- if (intervalId) clearInterval(intervalId);
370
- screen.destroy();
371
- process.exit(0);
372
- });
373
-
374
- // Initial display
375
- contentBox.setContent('{cyan-fg}⏳ Connecting to server...{/cyan-fg}');
376
- screen.render();
377
-
378
- startPolling();
379
-
380
- // Cleanup
381
- screen.on('destroy', () => {
382
- if (intervalId) clearInterval(intervalId);
383
- // Note: macmon child processes will automatically die when parent exits
384
- // since they're spawned with detached: false
385
- });
386
- }