@appkit/llamacpp-cli 1.6.0 → 1.8.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/CHANGELOG.md +7 -0
- package/README.md +71 -1
- package/dist/cli.js +24 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/config.d.ts +1 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +182 -13
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +0 -1
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/delete.js +12 -10
- package/dist/commands/delete.js.map +1 -1
- package/dist/commands/logs-all.d.ts +9 -0
- package/dist/commands/logs-all.d.ts.map +1 -0
- package/dist/commands/logs-all.js +209 -0
- package/dist/commands/logs-all.js.map +1 -0
- package/dist/commands/logs.d.ts +4 -0
- package/dist/commands/logs.d.ts.map +1 -1
- package/dist/commands/logs.js +108 -2
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/rm.d.ts.map +1 -1
- package/dist/commands/rm.js +5 -12
- package/dist/commands/rm.js.map +1 -1
- package/dist/commands/server-show.d.ts.map +1 -1
- package/dist/commands/server-show.js +20 -0
- package/dist/commands/server-show.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +22 -7
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/stop.js +3 -3
- package/dist/commands/stop.js.map +1 -1
- package/dist/utils/log-utils.d.ts +43 -0
- package/dist/utils/log-utils.d.ts.map +1 -0
- package/dist/utils/log-utils.js +190 -0
- package/dist/utils/log-utils.js.map +1 -0
- package/package.json +1 -1
- package/src/cli.ts +24 -1
- package/src/commands/config.ts +161 -15
- package/src/commands/create.ts +0 -1
- package/src/commands/delete.ts +10 -10
- package/src/commands/logs-all.ts +251 -0
- package/src/commands/logs.ts +138 -2
- package/src/commands/rm.ts +5 -12
- package/src/commands/server-show.ts +25 -0
- package/src/commands/start.ts +22 -7
- package/src/commands/stop.ts +3 -3
- package/src/utils/log-utils.ts +178 -0
package/src/commands/config.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs/promises';
|
|
2
4
|
import { stateManager } from '../lib/state-manager';
|
|
3
5
|
import { statusChecker } from '../lib/status-checker';
|
|
4
6
|
import { launchctlManager } from '../lib/launchctl-manager';
|
|
5
7
|
import { configGenerator } from '../lib/config-generator';
|
|
8
|
+
import { autoRotateIfNeeded } from '../utils/log-utils';
|
|
9
|
+
import { modelScanner } from '../lib/model-scanner';
|
|
10
|
+
import { sanitizeModelName } from '../types/server-config';
|
|
11
|
+
import { getLogsDir, getLaunchAgentsDir } from '../utils/file-utils';
|
|
6
12
|
|
|
7
13
|
export interface ConfigUpdateOptions {
|
|
14
|
+
model?: string;
|
|
8
15
|
host?: string;
|
|
9
16
|
threads?: number;
|
|
10
17
|
ctxSize?: number;
|
|
@@ -37,7 +44,8 @@ export async function serverConfigCommand(
|
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
// Check if any config options were provided
|
|
40
|
-
const hasChanges = options.
|
|
47
|
+
const hasChanges = options.model !== undefined ||
|
|
48
|
+
options.host !== undefined ||
|
|
41
49
|
options.threads !== undefined ||
|
|
42
50
|
options.ctxSize !== undefined ||
|
|
43
51
|
options.gpuLayers !== undefined ||
|
|
@@ -47,6 +55,7 @@ export async function serverConfigCommand(
|
|
|
47
55
|
if (!hasChanges) {
|
|
48
56
|
console.error(chalk.red('❌ No configuration changes specified'));
|
|
49
57
|
console.log(chalk.dim('\nAvailable options:'));
|
|
58
|
+
console.log(chalk.dim(' --model <filename> Model filename or path'));
|
|
50
59
|
console.log(chalk.dim(' --host <address> Bind address (127.0.0.1 or 0.0.0.0)'));
|
|
51
60
|
console.log(chalk.dim(' --threads <n> Number of threads'));
|
|
52
61
|
console.log(chalk.dim(' --ctx-size <n> Context size'));
|
|
@@ -55,12 +64,50 @@ export async function serverConfigCommand(
|
|
|
55
64
|
console.log(chalk.dim(' --no-verbose Disable verbose logging'));
|
|
56
65
|
console.log(chalk.dim(' --flags <flags> Custom llama-server flags (comma-separated)'));
|
|
57
66
|
console.log(chalk.dim(' --restart Auto-restart if running'));
|
|
58
|
-
console.log(chalk.dim('\
|
|
67
|
+
console.log(chalk.dim('\nExamples:'));
|
|
68
|
+
console.log(chalk.dim(` llamacpp server config ${server.id} --model llama-3.2-1b.gguf --restart`));
|
|
59
69
|
console.log(chalk.dim(` llamacpp server config ${server.id} --ctx-size 8192 --restart`));
|
|
60
70
|
console.log(chalk.dim(` llamacpp server config ${server.id} --flags="--pooling,mean" --restart`));
|
|
61
71
|
process.exit(1);
|
|
62
72
|
}
|
|
63
73
|
|
|
74
|
+
// Resolve model path if model option is provided
|
|
75
|
+
let newModelPath: string | undefined;
|
|
76
|
+
let newModelName: string | undefined;
|
|
77
|
+
let newServerId: string | undefined;
|
|
78
|
+
let isModelMigration = false;
|
|
79
|
+
|
|
80
|
+
if (options.model !== undefined) {
|
|
81
|
+
const resolvedPath = await modelScanner.resolveModelPath(options.model);
|
|
82
|
+
if (!resolvedPath) {
|
|
83
|
+
console.error(chalk.red(`❌ Model not found: ${options.model}`));
|
|
84
|
+
console.log(chalk.dim('\nRun: llamacpp ls'));
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
newModelPath = resolvedPath;
|
|
88
|
+
newModelName = path.basename(resolvedPath);
|
|
89
|
+
newServerId = sanitizeModelName(newModelName);
|
|
90
|
+
|
|
91
|
+
// Check if this is a model migration (ID will change)
|
|
92
|
+
if (newServerId !== server.id) {
|
|
93
|
+
isModelMigration = true;
|
|
94
|
+
|
|
95
|
+
// Check for ID conflict
|
|
96
|
+
const existingServer = await stateManager.loadServerConfig(newServerId);
|
|
97
|
+
if (existingServer) {
|
|
98
|
+
console.error(chalk.red(`❌ A server with ID "${newServerId}" already exists`));
|
|
99
|
+
console.log(chalk.dim('\nChanging the model would create this server ID, but it conflicts with an existing server.'));
|
|
100
|
+
console.log(chalk.dim('Delete the existing server first:'));
|
|
101
|
+
console.log(chalk.dim(` llamacpp server rm ${newServerId}`));
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
console.log(chalk.yellow('⚠️ Changing the model will migrate to a new server ID'));
|
|
106
|
+
console.log(chalk.dim(` Old ID: ${server.id}`));
|
|
107
|
+
console.log(chalk.dim(` New ID: ${newServerId}\n`));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
64
111
|
// Check current status
|
|
65
112
|
const updatedServer = await statusChecker.updateServerStatus(server);
|
|
66
113
|
const wasRunning = updatedServer.status === 'running';
|
|
@@ -75,6 +122,15 @@ export async function serverConfigCommand(
|
|
|
75
122
|
console.log(chalk.bold('Configuration Changes:'));
|
|
76
123
|
console.log('─'.repeat(70));
|
|
77
124
|
|
|
125
|
+
if (newModelPath !== undefined && newModelName !== undefined) {
|
|
126
|
+
const oldModelName = path.basename(server.modelPath);
|
|
127
|
+
console.log(`${chalk.bold('Model:')} ${chalk.dim(oldModelName)} → ${chalk.green(newModelName)}`);
|
|
128
|
+
console.log(`${chalk.dim(' ')}${chalk.dim(server.modelPath)}`);
|
|
129
|
+
console.log(`${chalk.dim(' ')}${chalk.dim(newModelPath)}`);
|
|
130
|
+
if (isModelMigration && newServerId) {
|
|
131
|
+
console.log(`${chalk.bold('Server ID:')} ${chalk.dim(server.id)} → ${chalk.green(newServerId)}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
78
134
|
if (options.host !== undefined) {
|
|
79
135
|
console.log(`${chalk.bold('Host:')} ${chalk.dim(server.host)} → ${chalk.green(options.host)}`);
|
|
80
136
|
|
|
@@ -106,30 +162,107 @@ export async function serverConfigCommand(
|
|
|
106
162
|
}
|
|
107
163
|
console.log('');
|
|
108
164
|
|
|
109
|
-
// Unload service if running and restart flag is set (forces plist re-read)
|
|
110
|
-
if (wasRunning && options.restart) {
|
|
111
|
-
console.log(chalk.dim('Stopping server...'));
|
|
112
|
-
await launchctlManager.stopService(server.label);
|
|
113
|
-
await launchctlManager.unloadService(server.plistPath);
|
|
114
|
-
|
|
115
|
-
// Wait a moment for clean shutdown
|
|
116
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
117
|
-
}
|
|
118
|
-
|
|
119
165
|
// Parse custom flags if provided
|
|
120
166
|
let customFlags: string[] | undefined;
|
|
121
167
|
if (options.flags !== undefined) {
|
|
122
168
|
if (options.flags === '') {
|
|
123
|
-
// Empty string means clear flags
|
|
124
169
|
customFlags = undefined;
|
|
125
170
|
} else {
|
|
126
171
|
customFlags = options.flags.split(',').map(f => f.trim()).filter(f => f.length > 0);
|
|
127
172
|
}
|
|
128
173
|
}
|
|
129
174
|
|
|
130
|
-
//
|
|
175
|
+
// Handle model migration (different code path when server ID changes)
|
|
176
|
+
if (isModelMigration && newServerId && newModelPath && newModelName) {
|
|
177
|
+
// Stop old server if running
|
|
178
|
+
if (wasRunning) {
|
|
179
|
+
console.log(chalk.dim('Stopping old server...'));
|
|
180
|
+
await launchctlManager.unloadService(server.plistPath);
|
|
181
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Delete old plist and config
|
|
185
|
+
console.log(chalk.dim('Removing old server configuration...'));
|
|
186
|
+
try {
|
|
187
|
+
await fs.unlink(server.plistPath);
|
|
188
|
+
} catch (err) {
|
|
189
|
+
// Plist might not exist, that's ok
|
|
190
|
+
}
|
|
191
|
+
await stateManager.deleteServerConfig(server.id);
|
|
192
|
+
|
|
193
|
+
// Create new config with new ID and all settings
|
|
194
|
+
const logsDir = getLogsDir();
|
|
195
|
+
const plistDir = getLaunchAgentsDir();
|
|
196
|
+
|
|
197
|
+
const newConfig = {
|
|
198
|
+
...server,
|
|
199
|
+
id: newServerId,
|
|
200
|
+
modelPath: newModelPath,
|
|
201
|
+
modelName: newModelName,
|
|
202
|
+
...(options.host !== undefined && { host: options.host }),
|
|
203
|
+
...(options.threads !== undefined && { threads: options.threads }),
|
|
204
|
+
...(options.ctxSize !== undefined && { ctxSize: options.ctxSize }),
|
|
205
|
+
...(options.gpuLayers !== undefined && { gpuLayers: options.gpuLayers }),
|
|
206
|
+
...(options.verbose !== undefined && { verbose: options.verbose }),
|
|
207
|
+
...(options.flags !== undefined && { customFlags }),
|
|
208
|
+
// Update plist-related paths
|
|
209
|
+
label: `com.llama.${newServerId}`,
|
|
210
|
+
plistPath: path.join(plistDir, `com.llama.${newServerId}.plist`),
|
|
211
|
+
stdoutPath: path.join(logsDir, `${newServerId}.stdout`),
|
|
212
|
+
stderrPath: path.join(logsDir, `${newServerId}.stderr`),
|
|
213
|
+
status: 'stopped' as const,
|
|
214
|
+
pid: undefined,
|
|
215
|
+
lastStopped: new Date().toISOString(),
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
console.log(chalk.dim('Creating new server configuration...'));
|
|
219
|
+
await stateManager.saveServerConfig(newConfig);
|
|
220
|
+
await launchctlManager.createPlist(newConfig);
|
|
221
|
+
|
|
222
|
+
// Start new server if restart flag is set
|
|
223
|
+
if (wasRunning && options.restart) {
|
|
224
|
+
console.log(chalk.dim('Starting new server...'));
|
|
225
|
+
await launchctlManager.loadService(newConfig.plistPath);
|
|
226
|
+
await launchctlManager.startService(newConfig.label);
|
|
227
|
+
|
|
228
|
+
// Wait and verify
|
|
229
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
230
|
+
const finalStatus = await statusChecker.updateServerStatus(newConfig);
|
|
231
|
+
|
|
232
|
+
if (finalStatus.status === 'running') {
|
|
233
|
+
console.log(chalk.green(`✅ Server migrated successfully to new ID: ${newServerId}`));
|
|
234
|
+
console.log(chalk.dim(` Port: http://localhost:${finalStatus.port}`));
|
|
235
|
+
if (finalStatus.pid) {
|
|
236
|
+
console.log(chalk.dim(` PID: ${finalStatus.pid}`));
|
|
237
|
+
}
|
|
238
|
+
} else {
|
|
239
|
+
console.error(chalk.red('❌ Server failed to start with new configuration'));
|
|
240
|
+
console.log(chalk.dim(' Check logs: ') + `llamacpp server logs ${newServerId} --errors`);
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
console.log(chalk.green(`✅ Server migrated successfully to new ID: ${newServerId}`));
|
|
245
|
+
if (!wasRunning) {
|
|
246
|
+
console.log(chalk.dim('\n Start server: ') + `llamacpp server start ${newServerId}`);
|
|
247
|
+
} else {
|
|
248
|
+
console.log(chalk.dim('\n Start server: ') + `llamacpp server start ${newServerId}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return; // Exit early for migration path
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Normal config update (no model migration)
|
|
256
|
+
// Unload service if running and restart flag is set
|
|
257
|
+
if (wasRunning && options.restart) {
|
|
258
|
+
console.log(chalk.dim('Stopping server...'));
|
|
259
|
+
await launchctlManager.unloadService(server.plistPath);
|
|
260
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
261
|
+
}
|
|
262
|
+
|
|
131
263
|
const updatedConfig = {
|
|
132
264
|
...server,
|
|
265
|
+
...(newModelPath !== undefined && { modelPath: newModelPath, modelName: newModelName }),
|
|
133
266
|
...(options.host !== undefined && { host: options.host }),
|
|
134
267
|
...(options.threads !== undefined && { threads: options.threads }),
|
|
135
268
|
...(options.ctxSize !== undefined && { ctxSize: options.ctxSize }),
|
|
@@ -140,12 +273,25 @@ export async function serverConfigCommand(
|
|
|
140
273
|
|
|
141
274
|
await stateManager.updateServerConfig(server.id, updatedConfig);
|
|
142
275
|
|
|
143
|
-
// Regenerate plist with new configuration
|
|
144
276
|
console.log(chalk.dim('Regenerating service configuration...'));
|
|
145
277
|
await launchctlManager.createPlist(updatedConfig);
|
|
146
278
|
|
|
147
279
|
// Restart server if it was running and restart flag is set
|
|
148
280
|
if (wasRunning && options.restart) {
|
|
281
|
+
// Auto-rotate logs if they exceed 100MB
|
|
282
|
+
try {
|
|
283
|
+
const result = await autoRotateIfNeeded(updatedConfig.stdoutPath, updatedConfig.stderrPath, 100);
|
|
284
|
+
if (result.rotated) {
|
|
285
|
+
console.log(chalk.dim('Auto-rotated large log files:'));
|
|
286
|
+
for (const file of result.files) {
|
|
287
|
+
console.log(chalk.dim(` → ${file}`));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
} catch (error) {
|
|
291
|
+
// Non-fatal, just warn
|
|
292
|
+
console.log(chalk.yellow(`⚠️ Failed to rotate logs: ${(error as Error).message}`));
|
|
293
|
+
}
|
|
294
|
+
|
|
149
295
|
console.log(chalk.dim('Starting server with new configuration...'));
|
|
150
296
|
await launchctlManager.loadService(updatedConfig.plistPath);
|
|
151
297
|
await launchctlManager.startService(updatedConfig.label);
|
package/src/commands/create.ts
CHANGED
|
@@ -152,7 +152,6 @@ export async function createCommand(model: string, options: CreateOptions): Prom
|
|
|
152
152
|
|
|
153
153
|
if (!started) {
|
|
154
154
|
// Clean up if startup fails
|
|
155
|
-
await launchctlManager.stopService(config.label);
|
|
156
155
|
await launchctlManager.unloadService(config.plistPath);
|
|
157
156
|
await launchctlManager.deletePlist(config.plistPath);
|
|
158
157
|
throw new Error('Server failed to start. Check logs with: llamacpp server logs --errors');
|
package/src/commands/delete.ts
CHANGED
|
@@ -24,21 +24,21 @@ export async function deleteCommand(identifier: string): Promise<void> {
|
|
|
24
24
|
console.log();
|
|
25
25
|
console.log(chalk.blue(`🗑️ Deleting server ${server.modelName}...`));
|
|
26
26
|
|
|
27
|
-
//
|
|
27
|
+
// Unload service (stops and removes from launchd)
|
|
28
28
|
if (server.status === 'running') {
|
|
29
|
-
console.log(chalk.dim('Stopping
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
console.log(chalk.dim('Stopping and unloading service...'));
|
|
30
|
+
} else {
|
|
31
|
+
console.log(chalk.dim('Unloading service...'));
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
await launchctlManager.unloadService(server.plistPath);
|
|
35
|
+
if (server.status === 'running') {
|
|
32
36
|
await launchctlManager.waitForServiceStop(server.label, 5000);
|
|
33
|
-
} catch (error) {
|
|
34
|
-
console.log(chalk.yellow('⚠️ Failed to stop server gracefully'));
|
|
35
37
|
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.log(chalk.yellow('⚠️ Failed to unload service gracefully'));
|
|
36
40
|
}
|
|
37
41
|
|
|
38
|
-
// Unload service
|
|
39
|
-
console.log(chalk.dim('Unloading launchctl service...'));
|
|
40
|
-
await launchctlManager.unloadService(server.plistPath);
|
|
41
|
-
|
|
42
42
|
// Delete plist
|
|
43
43
|
console.log(chalk.dim('Deleting plist file...'));
|
|
44
44
|
await launchctlManager.deletePlist(server.plistPath);
|
|
@@ -0,0 +1,251 @@
|
|
|
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
|
+
}
|