@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.
- package/README.md +294 -168
- package/dist/cli.js +35 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/launch/claude.d.ts +6 -0
- package/dist/commands/launch/claude.d.ts.map +1 -0
- package/dist/commands/launch/claude.js +277 -0
- package/dist/commands/launch/claude.js.map +1 -0
- package/dist/lib/integration-checker.d.ts +26 -0
- package/dist/lib/integration-checker.d.ts.map +1 -0
- package/dist/lib/integration-checker.js +77 -0
- package/dist/lib/integration-checker.js.map +1 -0
- package/dist/lib/router-manager.d.ts +4 -0
- package/dist/lib/router-manager.d.ts.map +1 -1
- package/dist/lib/router-manager.js +10 -0
- package/dist/lib/router-manager.js.map +1 -1
- package/dist/lib/router-server.d.ts +13 -0
- package/dist/lib/router-server.d.ts.map +1 -1
- package/dist/lib/router-server.js +267 -7
- package/dist/lib/router-server.js.map +1 -1
- package/dist/types/integration-config.d.ts +28 -0
- package/dist/types/integration-config.d.ts.map +1 -0
- package/dist/types/integration-config.js +3 -0
- package/dist/types/integration-config.js.map +1 -0
- package/package.json +10 -2
- package/web/dist/assets/index-Bin89Lwr.css +1 -0
- package/web/dist/assets/index-CVmonw3T.js +17 -0
- package/web/{index.html → dist/index.html} +2 -1
- package/.versionrc.json +0 -16
- package/CHANGELOG.md +0 -213
- package/docs/images/.gitkeep +0 -1
- package/docs/images/web-ui-servers.png +0 -0
- package/src/cli.ts +0 -523
- package/src/commands/admin/config.ts +0 -121
- package/src/commands/admin/logs.ts +0 -91
- package/src/commands/admin/restart.ts +0 -26
- package/src/commands/admin/start.ts +0 -27
- package/src/commands/admin/status.ts +0 -84
- package/src/commands/admin/stop.ts +0 -16
- package/src/commands/config-global.ts +0 -38
- package/src/commands/config.ts +0 -323
- package/src/commands/create.ts +0 -183
- package/src/commands/delete.ts +0 -74
- package/src/commands/list.ts +0 -37
- package/src/commands/logs-all.ts +0 -251
- package/src/commands/logs.ts +0 -345
- package/src/commands/monitor.ts +0 -110
- package/src/commands/ps.ts +0 -84
- package/src/commands/pull.ts +0 -44
- package/src/commands/rm.ts +0 -107
- package/src/commands/router/config.ts +0 -116
- package/src/commands/router/logs.ts +0 -256
- package/src/commands/router/restart.ts +0 -36
- package/src/commands/router/start.ts +0 -60
- package/src/commands/router/status.ts +0 -119
- package/src/commands/router/stop.ts +0 -33
- package/src/commands/run.ts +0 -233
- package/src/commands/search.ts +0 -107
- package/src/commands/server-show.ts +0 -161
- package/src/commands/show.ts +0 -207
- package/src/commands/start.ts +0 -101
- package/src/commands/stop.ts +0 -39
- package/src/commands/tui.ts +0 -25
- package/src/lib/admin-manager.ts +0 -435
- package/src/lib/admin-server.ts +0 -1243
- package/src/lib/config-generator.ts +0 -130
- package/src/lib/download-job-manager.ts +0 -213
- package/src/lib/history-manager.ts +0 -172
- package/src/lib/launchctl-manager.ts +0 -225
- package/src/lib/metrics-aggregator.ts +0 -257
- package/src/lib/model-downloader.ts +0 -328
- package/src/lib/model-scanner.ts +0 -157
- package/src/lib/model-search.ts +0 -114
- package/src/lib/models-dir-setup.ts +0 -46
- package/src/lib/port-manager.ts +0 -80
- package/src/lib/router-logger.ts +0 -201
- package/src/lib/router-manager.ts +0 -414
- package/src/lib/router-server.ts +0 -538
- package/src/lib/state-manager.ts +0 -206
- package/src/lib/status-checker.ts +0 -113
- package/src/lib/system-collector.ts +0 -315
- package/src/tui/ConfigApp.ts +0 -1085
- package/src/tui/HistoricalMonitorApp.ts +0 -587
- package/src/tui/ModelsApp.ts +0 -368
- package/src/tui/MonitorApp.ts +0 -386
- package/src/tui/MultiServerMonitorApp.ts +0 -1833
- package/src/tui/RootNavigator.ts +0 -74
- package/src/tui/SearchApp.ts +0 -511
- package/src/tui/SplashScreen.ts +0 -149
- package/src/types/admin-config.ts +0 -25
- package/src/types/global-config.ts +0 -26
- package/src/types/history-types.ts +0 -39
- package/src/types/model-info.ts +0 -8
- package/src/types/monitor-types.ts +0 -162
- package/src/types/router-config.ts +0 -25
- package/src/types/server-config.ts +0 -46
- package/src/utils/downsample-utils.ts +0 -128
- package/src/utils/file-utils.ts +0 -146
- package/src/utils/format-utils.ts +0 -98
- package/src/utils/log-parser.ts +0 -284
- package/src/utils/log-utils.ts +0 -178
- package/src/utils/process-utils.ts +0 -316
- package/src/utils/prompt-utils.ts +0 -47
- package/test-load.sh +0 -100
- package/tsconfig.json +0 -20
- package/web/eslint.config.js +0 -23
- package/web/llamacpp-web-dist.tar.gz +0 -0
- package/web/package-lock.json +0 -4017
- package/web/package.json +0 -38
- package/web/postcss.config.js +0 -6
- package/web/src/App.css +0 -42
- package/web/src/App.tsx +0 -86
- package/web/src/assets/react.svg +0 -1
- package/web/src/components/ApiKeyPrompt.tsx +0 -71
- package/web/src/components/CreateServerModal.tsx +0 -372
- package/web/src/components/DownloadProgress.tsx +0 -123
- package/web/src/components/Nav.tsx +0 -89
- package/web/src/components/RouterConfigModal.tsx +0 -240
- package/web/src/components/SearchModal.tsx +0 -306
- package/web/src/components/ServerConfigModal.tsx +0 -291
- package/web/src/hooks/useApi.ts +0 -259
- package/web/src/index.css +0 -42
- package/web/src/lib/api.ts +0 -226
- package/web/src/main.tsx +0 -10
- package/web/src/pages/Dashboard.tsx +0 -103
- package/web/src/pages/Models.tsx +0 -258
- package/web/src/pages/Router.tsx +0 -270
- package/web/src/pages/RouterLogs.tsx +0 -201
- package/web/src/pages/ServerLogs.tsx +0 -553
- package/web/src/pages/Servers.tsx +0 -358
- package/web/src/types/api.ts +0 -140
- package/web/tailwind.config.js +0 -31
- package/web/tsconfig.app.json +0 -28
- package/web/tsconfig.json +0 -7
- package/web/tsconfig.node.json +0 -26
- package/web/vite.config.ts +0 -25
- /package/web/{public → dist}/vite.svg +0 -0
package/src/commands/logs.ts
DELETED
|
@@ -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
|
-
}
|
package/src/commands/monitor.ts
DELETED
|
@@ -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
|
-
}
|
package/src/commands/ps.ts
DELETED
|
@@ -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
|
-
}
|
package/src/commands/pull.ts
DELETED
|
@@ -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
|
-
}
|
package/src/commands/rm.ts
DELETED
|
@@ -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
|
-
}
|