@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,183 +0,0 @@
1
- import chalk from 'chalk';
2
- import * as path from 'path';
3
- import * as fs from 'fs';
4
- import { modelScanner } from '../lib/model-scanner';
5
- import { stateManager } from '../lib/state-manager';
6
- import { configGenerator, ServerOptions } from '../lib/config-generator';
7
- import { portManager } from '../lib/port-manager';
8
- import { launchctlManager } from '../lib/launchctl-manager';
9
- import { statusChecker } from '../lib/status-checker';
10
- import { commandExists } from '../utils/process-utils';
11
- import { formatBytes } from '../utils/format-utils';
12
- import { ensureDir, parseMetalMemoryFromLog } from '../utils/file-utils';
13
- import { ensureModelsDirectory } from '../lib/models-dir-setup';
14
-
15
- interface CreateOptions {
16
- port?: number;
17
- host?: string;
18
- threads?: number;
19
- ctxSize?: number;
20
- gpuLayers?: number;
21
- verbose?: boolean;
22
- flags?: string;
23
- }
24
-
25
- export async function createCommand(model: string, options: CreateOptions): Promise<void> {
26
- // Initialize state manager
27
- await stateManager.initialize();
28
-
29
- // 1. Check if llama-server exists
30
- if (!(await commandExists('llama-server'))) {
31
- throw new Error('llama-server not found. Install with: brew install llama.cpp');
32
- }
33
-
34
- // 2. Ensure models directory exists if model is not an absolute path
35
- if (!path.isAbsolute(model)) {
36
- const modelsDir = await stateManager.getModelsDirectory();
37
- if (!fs.existsSync(modelsDir)) {
38
- await ensureModelsDirectory();
39
- }
40
- }
41
-
42
- // 3. Resolve model path
43
- const modelPath = await modelScanner.resolveModelPath(model);
44
- if (!modelPath) {
45
- throw new Error(`Model not found: ${model}\n\nRun: llamacpp ls`);
46
- }
47
-
48
- const modelName = path.basename(modelPath);
49
-
50
- // 4. Check if server already exists for this model
51
- const existingServer = await stateManager.serverExistsForModel(modelPath);
52
- if (existingServer) {
53
- throw new Error(`Server already exists for ${modelName}\n\nUse: llamacpp server start ${modelName}`);
54
- }
55
-
56
- // 5. Get model size
57
- const modelSize = await modelScanner.getModelSize(modelName);
58
- if (!modelSize) {
59
- throw new Error(`Failed to read model file: ${modelPath}`);
60
- }
61
-
62
- // 6. Determine port
63
- let port: number;
64
- if (options.port) {
65
- portManager.validatePort(options.port);
66
- const available = await portManager.isPortAvailable(options.port);
67
- if (!available) {
68
- throw new Error(`Port ${options.port} is already in use`);
69
- }
70
- port = options.port;
71
- } else {
72
- port = await portManager.findAvailablePort();
73
- }
74
-
75
- // 7. Generate server configuration
76
- console.log(chalk.blue(`🚀 Creating server for ${modelName}\n`));
77
-
78
- // Parse custom flags if provided
79
- let customFlags: string[] | undefined;
80
- if (options.flags) {
81
- customFlags = options.flags.split(',').map(f => f.trim()).filter(f => f.length > 0);
82
- }
83
-
84
- const serverOptions: ServerOptions = {
85
- port: options.port,
86
- host: options.host,
87
- threads: options.threads,
88
- ctxSize: options.ctxSize,
89
- gpuLayers: options.gpuLayers,
90
- verbose: options.verbose,
91
- customFlags,
92
- };
93
-
94
- const config = await configGenerator.generateConfig(
95
- modelPath,
96
- modelName,
97
- modelSize,
98
- port,
99
- serverOptions
100
- );
101
-
102
- // Security warning for 0.0.0.0
103
- if (config.host === '0.0.0.0') {
104
- console.log(chalk.yellow('⚠️ WARNING: Binding to 0.0.0.0 allows remote access from any network interface.'));
105
- console.log(chalk.yellow(' This exposes your server to your local network and potentially the internet.'));
106
- console.log(chalk.yellow(' Use 127.0.0.1 for localhost-only access (recommended for local development).\n'));
107
- }
108
-
109
- // Display configuration
110
- console.log(chalk.dim(`Model: ${modelPath}`));
111
- console.log(chalk.dim(`Size: ${formatBytes(modelSize)}`));
112
- console.log(chalk.dim(`Host: ${config.host}`));
113
- console.log(chalk.dim(`Port: ${config.port}${options.port ? '' : ' (auto-assigned)'}`));
114
- console.log(chalk.dim(`Threads: ${config.threads}`));
115
- console.log(chalk.dim(`Context Size: ${config.ctxSize}`));
116
- console.log(chalk.dim(`GPU Layers: ${config.gpuLayers}`));
117
- console.log(chalk.dim(`Verbose Logging: ${config.verbose ? 'enabled' : 'disabled'}`));
118
- if (config.customFlags && config.customFlags.length > 0) {
119
- console.log(chalk.dim(`Custom Flags: ${config.customFlags.join(' ')}`));
120
- }
121
- console.log();
122
-
123
- // 7. Ensure log directory exists
124
- await ensureDir(path.dirname(config.stdoutPath));
125
-
126
- // 8. Create plist file
127
- console.log(chalk.dim('Creating launchctl service...'));
128
- await launchctlManager.createPlist(config);
129
-
130
- // 9. Load service
131
- try {
132
- await launchctlManager.loadService(config.plistPath);
133
- } catch (error) {
134
- // Clean up plist if load fails
135
- await launchctlManager.deletePlist(config.plistPath);
136
- throw new Error(`Failed to load service: ${(error as Error).message}`);
137
- }
138
-
139
- // 10. Start service
140
- try {
141
- await launchctlManager.startService(config.label);
142
- } catch (error) {
143
- // Clean up if start fails
144
- await launchctlManager.unloadService(config.plistPath);
145
- await launchctlManager.deletePlist(config.plistPath);
146
- throw new Error(`Failed to start service: ${(error as Error).message}`);
147
- }
148
-
149
- // 11. Wait for startup
150
- console.log(chalk.dim('Waiting for server to start...'));
151
- const started = await launchctlManager.waitForServiceStart(config.label, 5000);
152
-
153
- if (!started) {
154
- // Clean up if startup fails
155
- await launchctlManager.unloadService(config.plistPath);
156
- await launchctlManager.deletePlist(config.plistPath);
157
- throw new Error('Server failed to start. Check logs with: llamacpp server logs --errors');
158
- }
159
-
160
- // 12. Update config with running status
161
- let updatedConfig = await statusChecker.updateServerStatus(config);
162
-
163
- // 13. Parse Metal (GPU) memory allocation from logs
164
- // Wait a few seconds for model to start loading (large models take time)
165
- console.log(chalk.dim('Detecting Metal (GPU) memory allocation...'));
166
- await new Promise(resolve => setTimeout(resolve, 8000)); // 8 second delay
167
- const metalMemoryMB = await parseMetalMemoryFromLog(updatedConfig.stderrPath);
168
- if (metalMemoryMB) {
169
- updatedConfig = { ...updatedConfig, metalMemoryMB };
170
- console.log(chalk.dim(`Metal memory: ${metalMemoryMB.toFixed(0)} MB`));
171
- }
172
-
173
- // 14. Save server config
174
- await stateManager.saveServerConfig(updatedConfig);
175
-
176
- // 15. Display success message
177
- console.log();
178
- console.log(chalk.green('✅ Server created and started successfully!'));
179
- console.log();
180
- console.log(chalk.dim(`Connect: http://${config.host}:${config.port}`));
181
- console.log(chalk.dim(`View logs: llamacpp server logs ${config.id}`));
182
- console.log(chalk.dim(`Stop: llamacpp server stop ${config.id}`));
183
- }
@@ -1,74 +0,0 @@
1
- import chalk from 'chalk';
2
- import * as readline from 'readline';
3
- import { stateManager } from '../lib/state-manager';
4
- import { launchctlManager } from '../lib/launchctl-manager';
5
-
6
- export async function deleteCommand(identifier: string): Promise<void> {
7
- // Find server
8
- const server = await stateManager.findServer(identifier);
9
- if (!server) {
10
- throw new Error(`Server not found: ${identifier}\n\nUse: llamacpp ps`);
11
- }
12
-
13
- // Confirm deletion
14
- console.log(chalk.yellow(`⚠️ Delete server configuration for ${server.modelName}?`));
15
- console.log(chalk.dim(' This will remove the launchd service but keep the model file.'));
16
- console.log();
17
-
18
- const confirmed = await confirmDeletion();
19
- if (!confirmed) {
20
- console.log(chalk.dim('Cancelled'));
21
- return;
22
- }
23
-
24
- console.log();
25
- console.log(chalk.blue(`🗑️ Deleting server ${server.modelName}...`));
26
-
27
- // Unload service (stops and removes from launchd)
28
- if (server.status === 'running') {
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') {
36
- await launchctlManager.waitForServiceStop(server.label, 5000);
37
- }
38
- } catch (error) {
39
- console.log(chalk.yellow('⚠️ Failed to unload service gracefully'));
40
- }
41
-
42
- // Delete plist
43
- console.log(chalk.dim('Deleting plist file...'));
44
- await launchctlManager.deletePlist(server.plistPath);
45
-
46
- // Delete server config
47
- console.log(chalk.dim('Deleting server configuration...'));
48
- await stateManager.deleteServerConfig(server.id);
49
-
50
- // Success
51
- console.log();
52
- console.log(chalk.green('✅ Server deleted'));
53
- console.log(chalk.dim(` Plist removed: ${server.plistPath}`));
54
- console.log(chalk.dim(` Config removed`));
55
- console.log();
56
- console.log(chalk.dim(` Model file preserved at: ${server.modelPath}`));
57
- }
58
-
59
- /**
60
- * Prompt user for confirmation
61
- */
62
- function confirmDeletion(): Promise<boolean> {
63
- return new Promise((resolve) => {
64
- const rl = readline.createInterface({
65
- input: process.stdin,
66
- output: process.stdout,
67
- });
68
-
69
- rl.question(chalk.yellow(" Type 'yes' to confirm: "), (answer) => {
70
- rl.close();
71
- resolve(answer.toLowerCase() === 'yes');
72
- });
73
- });
74
- }
@@ -1,37 +0,0 @@
1
- import chalk from 'chalk';
2
- import Table from 'cli-table3';
3
- import { modelScanner } from '../lib/model-scanner';
4
- import { formatBytes, formatDateShort } from '../utils/format-utils';
5
- import { stateManager } from '../lib/state-manager';
6
-
7
- export async function listCommand(): Promise<void> {
8
- const modelsDir = await stateManager.getModelsDirectory();
9
- console.log(chalk.blue(`📦 Available models in ${modelsDir}\n`));
10
-
11
- const models = await modelScanner.scanModels();
12
-
13
- if (models.length === 0) {
14
- console.log(chalk.yellow('No GGUF models found.'));
15
- console.log(chalk.dim(`\nDownload models with: llamacpp pull <repo> --file <filename>`));
16
- return;
17
- }
18
-
19
- const table = new Table({
20
- head: ['MODEL', 'SIZE', 'MODIFIED'],
21
- colWidths: [50, 12, 15],
22
- });
23
-
24
- for (const model of models) {
25
- table.push([
26
- model.filename,
27
- model.sizeFormatted,
28
- formatDateShort(model.modified),
29
- ]);
30
- }
31
-
32
- console.log(table.toString());
33
-
34
- const totalSize = models.reduce((sum, m) => sum + m.size, 0);
35
- console.log(chalk.dim(`\nTotal: ${models.length} models (${formatBytes(totalSize)})`));
36
- console.log(chalk.dim(`\nCreate a server: llamacpp server create <model-filename>`));
37
- }
@@ -1,251 +0,0 @@
1
- import chalk from 'chalk';
2
- import Table from 'cli-table3';
3
- import { stateManager } from '../lib/state-manager';
4
- import { fileExists } from '../utils/file-utils';
5
- import {
6
- getFileSize,
7
- formatFileSize,
8
- getArchivedLogInfo,
9
- clearLogFile,
10
- rotateLogFile,
11
- deleteArchivedLogs,
12
- } from '../utils/log-utils';
13
-
14
- interface LogsAllOptions {
15
- clear?: boolean;
16
- clearArchived?: boolean;
17
- clearAll?: boolean;
18
- rotate?: boolean;
19
- }
20
-
21
- export async function logsAllCommand(options: LogsAllOptions): Promise<void> {
22
- // Get all servers
23
- const servers = await stateManager.getAllServers();
24
-
25
- if (servers.length === 0) {
26
- console.log(chalk.yellow('⚠️ No servers found'));
27
- console.log(chalk.dim('\nCreate a server: llamacpp server create <model-filename>'));
28
- return;
29
- }
30
-
31
- // Handle batch operations
32
- if (options.clear || options.clearArchived || options.clearAll || options.rotate) {
33
- await handleBatchOperation(servers, options);
34
- return;
35
- }
36
-
37
- // Show table of log information
38
- await showLogsTable(servers);
39
- }
40
-
41
- async function showLogsTable(servers: any[]): Promise<void> {
42
- const table = new Table({
43
- head: [
44
- chalk.bold('Server ID'),
45
- chalk.bold('Current Stderr'),
46
- chalk.bold('Current Stdout'),
47
- chalk.bold('Archived'),
48
- chalk.bold('Total'),
49
- ],
50
- colWidths: [30, 18, 18, 18, 18],
51
- });
52
-
53
- let totalCurrent = 0;
54
- let totalArchived = 0;
55
-
56
- for (const server of servers) {
57
- // Get current log sizes
58
- const stderrSize = (await fileExists(server.stderrPath))
59
- ? await getFileSize(server.stderrPath)
60
- : 0;
61
- const stdoutSize = (await fileExists(server.stdoutPath))
62
- ? await getFileSize(server.stdoutPath)
63
- : 0;
64
-
65
- // Get archived info
66
- const archivedInfo = await getArchivedLogInfo(server.id);
67
-
68
- const currentTotal = stderrSize + stdoutSize;
69
- const total = currentTotal + archivedInfo.totalSize;
70
-
71
- totalCurrent += currentTotal;
72
- totalArchived += archivedInfo.totalSize;
73
-
74
- table.push([
75
- server.id,
76
- formatFileSize(stderrSize),
77
- formatFileSize(stdoutSize),
78
- archivedInfo.count > 0
79
- ? `${formatFileSize(archivedInfo.totalSize)} (${archivedInfo.count})`
80
- : formatFileSize(0),
81
- formatFileSize(total),
82
- ]);
83
- }
84
-
85
- console.log(chalk.bold('\nServer Logs Overview:'));
86
- console.log(table.toString());
87
-
88
- console.log(chalk.dim('\nTotals:'));
89
- console.log(chalk.dim(` Current logs: ${formatFileSize(totalCurrent)}`));
90
- console.log(chalk.dim(` Archived logs: ${formatFileSize(totalArchived)}`));
91
- console.log(chalk.dim(` Grand total: ${formatFileSize(totalCurrent + totalArchived)}`));
92
-
93
- console.log(chalk.dim('\nBatch operations:'));
94
- console.log(chalk.dim(' llamacpp logs --clear Clear all current logs'));
95
- console.log(chalk.dim(' llamacpp logs --clear-archived Delete only archived logs'));
96
- console.log(chalk.dim(' llamacpp logs --clear-all Clear current + delete archives'));
97
- console.log(chalk.dim(' llamacpp logs --rotate Rotate all logs with timestamps'));
98
- }
99
-
100
- async function handleBatchOperation(
101
- servers: any[],
102
- options: LogsAllOptions
103
- ): Promise<void> {
104
- if (options.clearArchived) {
105
- console.log(chalk.blue('🗑️ Deleting archived logs for all servers...'));
106
- console.log();
107
-
108
- let totalFreed = 0;
109
- let totalFiles = 0;
110
- let serversProcessed = 0;
111
-
112
- for (const server of servers) {
113
- const archivedInfo = await deleteArchivedLogs(server.id);
114
-
115
- if (archivedInfo.count > 0) {
116
- console.log(chalk.dim(` ${server.id}: ${formatFileSize(archivedInfo.totalSize)} (${archivedInfo.count} file${archivedInfo.count !== 1 ? 's' : ''})`));
117
- totalFreed += archivedInfo.totalSize;
118
- totalFiles += archivedInfo.count;
119
- serversProcessed++;
120
- }
121
- }
122
-
123
- console.log();
124
- if (serversProcessed === 0) {
125
- console.log(chalk.yellow('⚠️ No archived logs found'));
126
- console.log(chalk.dim(' Archived logs are created via --rotate or automatic rotation'));
127
- } else {
128
- console.log(chalk.green(`✅ Deleted archived logs for ${serversProcessed} server${serversProcessed !== 1 ? 's' : ''}`));
129
- console.log(chalk.dim(` Files deleted: ${totalFiles}`));
130
- console.log(chalk.dim(` Total freed: ${formatFileSize(totalFreed)}`));
131
- console.log(chalk.dim(` Current logs preserved`));
132
- }
133
- } else if (options.clearAll) {
134
- console.log(chalk.blue('🗑️ Clearing all logs (current + archived) for all servers...'));
135
- console.log();
136
-
137
- let totalFreed = 0;
138
- let serversProcessed = 0;
139
-
140
- for (const server of servers) {
141
- let serverTotal = 0;
142
-
143
- // Clear current stderr
144
- if (await fileExists(server.stderrPath)) {
145
- serverTotal += await getFileSize(server.stderrPath);
146
- await clearLogFile(server.stderrPath);
147
- }
148
-
149
- // Clear current stdout
150
- if (await fileExists(server.stdoutPath)) {
151
- serverTotal += await getFileSize(server.stdoutPath);
152
- await clearLogFile(server.stdoutPath);
153
- }
154
-
155
- // Delete archived logs
156
- const archivedInfo = await deleteArchivedLogs(server.id);
157
- serverTotal += archivedInfo.totalSize;
158
-
159
- if (serverTotal > 0) {
160
- console.log(chalk.dim(` ${server.id}: ${formatFileSize(serverTotal)}`));
161
- totalFreed += serverTotal;
162
- serversProcessed++;
163
- }
164
- }
165
-
166
- console.log();
167
- console.log(chalk.green(`✅ Cleared all logs for ${serversProcessed} server${serversProcessed !== 1 ? 's' : ''}`));
168
- console.log(chalk.dim(` Total freed: ${formatFileSize(totalFreed)}`));
169
- } else if (options.clear) {
170
- console.log(chalk.blue('🗑️ Clearing current logs for all servers...'));
171
- console.log();
172
-
173
- let totalFreed = 0;
174
- let serversProcessed = 0;
175
-
176
- for (const server of servers) {
177
- let serverTotal = 0;
178
-
179
- // Clear current stderr
180
- if (await fileExists(server.stderrPath)) {
181
- serverTotal += await getFileSize(server.stderrPath);
182
- await clearLogFile(server.stderrPath);
183
- }
184
-
185
- // Clear current stdout
186
- if (await fileExists(server.stdoutPath)) {
187
- serverTotal += await getFileSize(server.stdoutPath);
188
- await clearLogFile(server.stdoutPath);
189
- }
190
-
191
- if (serverTotal > 0) {
192
- console.log(chalk.dim(` ${server.id}: ${formatFileSize(serverTotal)}`));
193
- totalFreed += serverTotal;
194
- serversProcessed++;
195
- }
196
- }
197
-
198
- console.log();
199
- console.log(chalk.green(`✅ Cleared current logs for ${serversProcessed} server${serversProcessed !== 1 ? 's' : ''}`));
200
- console.log(chalk.dim(` Total freed: ${formatFileSize(totalFreed)}`));
201
- console.log(chalk.dim(` Archived logs preserved`));
202
- } else if (options.rotate) {
203
- console.log(chalk.blue('🔄 Rotating logs for all servers...'));
204
- console.log();
205
-
206
- let totalRotated = 0;
207
- let filesRotated = 0;
208
-
209
- for (const server of servers) {
210
- const rotatedFiles: string[] = [];
211
-
212
- // Rotate stderr if it has content
213
- if (await fileExists(server.stderrPath)) {
214
- const size = await getFileSize(server.stderrPath);
215
- if (size > 0) {
216
- try {
217
- const archivedPath = await rotateLogFile(server.stderrPath);
218
- rotatedFiles.push(archivedPath);
219
- totalRotated += size;
220
- filesRotated++;
221
- } catch {
222
- // Ignore empty files
223
- }
224
- }
225
- }
226
-
227
- // Rotate stdout if it has content
228
- if (await fileExists(server.stdoutPath)) {
229
- const size = await getFileSize(server.stdoutPath);
230
- if (size > 0) {
231
- try {
232
- const archivedPath = await rotateLogFile(server.stdoutPath);
233
- rotatedFiles.push(archivedPath);
234
- totalRotated += size;
235
- filesRotated++;
236
- } catch {
237
- // Ignore empty files
238
- }
239
- }
240
- }
241
-
242
- if (rotatedFiles.length > 0) {
243
- console.log(chalk.dim(` ${server.id}: ${rotatedFiles.length} file${rotatedFiles.length !== 1 ? 's' : ''}`));
244
- }
245
- }
246
-
247
- console.log();
248
- console.log(chalk.green(`✅ Rotated ${filesRotated} log file${filesRotated !== 1 ? 's' : ''}`));
249
- console.log(chalk.dim(` Total archived: ${formatFileSize(totalRotated)}`));
250
- }
251
- }