@appkit/llamacpp-cli 1.11.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 (126) hide show
  1. package/README.md +572 -170
  2. package/dist/cli.js +99 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/admin/config.d.ts +10 -0
  5. package/dist/commands/admin/config.d.ts.map +1 -0
  6. package/dist/commands/admin/config.js +100 -0
  7. package/dist/commands/admin/config.js.map +1 -0
  8. package/dist/commands/admin/logs.d.ts +10 -0
  9. package/dist/commands/admin/logs.d.ts.map +1 -0
  10. package/dist/commands/admin/logs.js +114 -0
  11. package/dist/commands/admin/logs.js.map +1 -0
  12. package/dist/commands/admin/restart.d.ts +2 -0
  13. package/dist/commands/admin/restart.d.ts.map +1 -0
  14. package/dist/commands/admin/restart.js +29 -0
  15. package/dist/commands/admin/restart.js.map +1 -0
  16. package/dist/commands/admin/start.d.ts +2 -0
  17. package/dist/commands/admin/start.d.ts.map +1 -0
  18. package/dist/commands/admin/start.js +30 -0
  19. package/dist/commands/admin/start.js.map +1 -0
  20. package/dist/commands/admin/status.d.ts +2 -0
  21. package/dist/commands/admin/status.d.ts.map +1 -0
  22. package/dist/commands/admin/status.js +82 -0
  23. package/dist/commands/admin/status.js.map +1 -0
  24. package/dist/commands/admin/stop.d.ts +2 -0
  25. package/dist/commands/admin/stop.d.ts.map +1 -0
  26. package/dist/commands/admin/stop.js +21 -0
  27. package/dist/commands/admin/stop.js.map +1 -0
  28. package/dist/commands/logs.d.ts +1 -0
  29. package/dist/commands/logs.d.ts.map +1 -1
  30. package/dist/commands/logs.js +22 -0
  31. package/dist/commands/logs.js.map +1 -1
  32. package/dist/lib/admin-manager.d.ts +111 -0
  33. package/dist/lib/admin-manager.d.ts.map +1 -0
  34. package/dist/lib/admin-manager.js +413 -0
  35. package/dist/lib/admin-manager.js.map +1 -0
  36. package/dist/lib/admin-server.d.ts +148 -0
  37. package/dist/lib/admin-server.d.ts.map +1 -0
  38. package/dist/lib/admin-server.js +1161 -0
  39. package/dist/lib/admin-server.js.map +1 -0
  40. package/dist/lib/download-job-manager.d.ts +64 -0
  41. package/dist/lib/download-job-manager.d.ts.map +1 -0
  42. package/dist/lib/download-job-manager.js +164 -0
  43. package/dist/lib/download-job-manager.js.map +1 -0
  44. package/dist/tui/MultiServerMonitorApp.js +1 -1
  45. package/dist/types/admin-config.d.ts +19 -0
  46. package/dist/types/admin-config.d.ts.map +1 -0
  47. package/dist/types/admin-config.js +3 -0
  48. package/dist/types/admin-config.js.map +1 -0
  49. package/dist/utils/log-parser.d.ts +9 -0
  50. package/dist/utils/log-parser.d.ts.map +1 -1
  51. package/dist/utils/log-parser.js +11 -0
  52. package/dist/utils/log-parser.js.map +1 -1
  53. package/package.json +10 -2
  54. package/web/README.md +429 -0
  55. package/web/dist/assets/index-Bin89Lwr.css +1 -0
  56. package/web/dist/assets/index-CVmonw3T.js +17 -0
  57. package/web/dist/index.html +14 -0
  58. package/web/dist/vite.svg +1 -0
  59. package/.versionrc.json +0 -16
  60. package/CHANGELOG.md +0 -203
  61. package/MONITORING-ACCURACY-FIX.md +0 -199
  62. package/PER-PROCESS-METRICS.md +0 -190
  63. package/docs/images/.gitkeep +0 -1
  64. package/src/cli.ts +0 -423
  65. package/src/commands/config-global.ts +0 -38
  66. package/src/commands/config.ts +0 -323
  67. package/src/commands/create.ts +0 -183
  68. package/src/commands/delete.ts +0 -74
  69. package/src/commands/list.ts +0 -37
  70. package/src/commands/logs-all.ts +0 -251
  71. package/src/commands/logs.ts +0 -321
  72. package/src/commands/monitor.ts +0 -110
  73. package/src/commands/ps.ts +0 -84
  74. package/src/commands/pull.ts +0 -44
  75. package/src/commands/rm.ts +0 -107
  76. package/src/commands/router/config.ts +0 -116
  77. package/src/commands/router/logs.ts +0 -256
  78. package/src/commands/router/restart.ts +0 -36
  79. package/src/commands/router/start.ts +0 -60
  80. package/src/commands/router/status.ts +0 -119
  81. package/src/commands/router/stop.ts +0 -33
  82. package/src/commands/run.ts +0 -233
  83. package/src/commands/search.ts +0 -107
  84. package/src/commands/server-show.ts +0 -161
  85. package/src/commands/show.ts +0 -207
  86. package/src/commands/start.ts +0 -101
  87. package/src/commands/stop.ts +0 -39
  88. package/src/commands/tui.ts +0 -25
  89. package/src/lib/config-generator.ts +0 -130
  90. package/src/lib/history-manager.ts +0 -172
  91. package/src/lib/launchctl-manager.ts +0 -225
  92. package/src/lib/metrics-aggregator.ts +0 -257
  93. package/src/lib/model-downloader.ts +0 -328
  94. package/src/lib/model-scanner.ts +0 -157
  95. package/src/lib/model-search.ts +0 -114
  96. package/src/lib/models-dir-setup.ts +0 -46
  97. package/src/lib/port-manager.ts +0 -80
  98. package/src/lib/router-logger.ts +0 -201
  99. package/src/lib/router-manager.ts +0 -414
  100. package/src/lib/router-server.ts +0 -538
  101. package/src/lib/state-manager.ts +0 -206
  102. package/src/lib/status-checker.ts +0 -113
  103. package/src/lib/system-collector.ts +0 -315
  104. package/src/tui/ConfigApp.ts +0 -1085
  105. package/src/tui/HistoricalMonitorApp.ts +0 -587
  106. package/src/tui/ModelsApp.ts +0 -368
  107. package/src/tui/MonitorApp.ts +0 -386
  108. package/src/tui/MultiServerMonitorApp.ts +0 -1833
  109. package/src/tui/RootNavigator.ts +0 -74
  110. package/src/tui/SearchApp.ts +0 -511
  111. package/src/tui/SplashScreen.ts +0 -149
  112. package/src/types/global-config.ts +0 -26
  113. package/src/types/history-types.ts +0 -39
  114. package/src/types/model-info.ts +0 -8
  115. package/src/types/monitor-types.ts +0 -162
  116. package/src/types/router-config.ts +0 -25
  117. package/src/types/server-config.ts +0 -46
  118. package/src/utils/downsample-utils.ts +0 -128
  119. package/src/utils/file-utils.ts +0 -146
  120. package/src/utils/format-utils.ts +0 -98
  121. package/src/utils/log-parser.ts +0 -271
  122. package/src/utils/log-utils.ts +0 -178
  123. package/src/utils/process-utils.ts +0 -316
  124. package/src/utils/prompt-utils.ts +0 -47
  125. package/test-load.sh +0 -100
  126. package/tsconfig.json +0 -20
@@ -1,60 +0,0 @@
1
- import chalk from 'chalk';
2
- import { routerManager } from '../../lib/router-manager';
3
- import { stateManager } from '../../lib/state-manager';
4
-
5
- export async function routerStartCommand(): Promise<void> {
6
- console.log(chalk.blue('▶️ Starting router...'));
7
-
8
- try {
9
- // Initialize
10
- await routerManager.initialize();
11
- await stateManager.initialize();
12
-
13
- // Check if router already exists
14
- const existingConfig = await routerManager.loadConfig();
15
- if (existingConfig && existingConfig.status === 'running') {
16
- console.log(chalk.yellow(`⚠️ Router is already running on port ${existingConfig.port}`));
17
- return;
18
- }
19
-
20
- // Start router
21
- await routerManager.start();
22
-
23
- // Get updated config
24
- const config = await routerManager.loadConfig();
25
- if (!config) {
26
- throw new Error('Failed to load router configuration after start');
27
- }
28
-
29
- // Get running servers to show available models
30
- const servers = await stateManager.getAllServers();
31
- const runningServers = servers.filter(s => s.status === 'running');
32
-
33
- // Display success
34
- console.log();
35
- console.log(chalk.green('✅ Router started successfully!'));
36
- console.log();
37
- console.log(chalk.dim(`Endpoint: http://${config.host}:${config.port}`));
38
- console.log(chalk.dim(`Available models: ${runningServers.length}`));
39
-
40
- if (runningServers.length > 0) {
41
- console.log();
42
- console.log(chalk.dim('Models:'));
43
- runningServers.forEach(server => {
44
- console.log(chalk.dim(` • ${server.modelName} (port ${server.port})`));
45
- });
46
- } else {
47
- console.log();
48
- console.log(chalk.yellow('⚠️ No running servers found. Start a server first:'));
49
- console.log(chalk.dim(' llamacpp server create <model>'));
50
- }
51
-
52
- console.log();
53
- console.log(chalk.dim('Quick commands:'));
54
- console.log(chalk.dim(` Status: llamacpp router status`));
55
- console.log(chalk.dim(` Stop: llamacpp router stop`));
56
- console.log(chalk.dim(` Logs: tail -f ${config.stderrPath}`));
57
- } catch (error) {
58
- throw new Error(`Failed to start router: ${(error as Error).message}`);
59
- }
60
- }
@@ -1,119 +0,0 @@
1
- import chalk from 'chalk';
2
- import { routerManager } from '../../lib/router-manager';
3
- import { stateManager } from '../../lib/state-manager';
4
-
5
- export async function routerStatusCommand(): Promise<void> {
6
- try {
7
- // Get router status
8
- const result = await routerManager.getStatus();
9
- if (!result) {
10
- console.log(chalk.yellow('Router not configured'));
11
- console.log();
12
- console.log(chalk.dim('Create and start router:'));
13
- console.log(chalk.dim(' llamacpp router start'));
14
- return;
15
- }
16
-
17
- const { config, status } = result;
18
-
19
- // Calculate uptime if running
20
- let uptime = 'N/A';
21
- if (status.isRunning && config.lastStarted) {
22
- const startTime = new Date(config.lastStarted).getTime();
23
- const now = Date.now();
24
- const uptimeSeconds = Math.floor((now - startTime) / 1000);
25
- const hours = Math.floor(uptimeSeconds / 3600);
26
- const minutes = Math.floor((uptimeSeconds % 3600) / 60);
27
- const seconds = uptimeSeconds % 60;
28
-
29
- if (hours > 0) {
30
- uptime = `${hours}h ${minutes}m`;
31
- } else if (minutes > 0) {
32
- uptime = `${minutes}m ${seconds}s`;
33
- } else {
34
- uptime = `${seconds}s`;
35
- }
36
- }
37
-
38
- // Get running servers
39
- const servers = await stateManager.getAllServers();
40
- const runningServers = servers.filter(s => s.status === 'running');
41
-
42
- // Display status
43
- console.log();
44
- console.log(chalk.bold('Router Status'));
45
- console.log(chalk.dim('─'.repeat(50)));
46
- console.log();
47
-
48
- // Status badge
49
- const statusColor = status.isRunning ? chalk.green : chalk.gray;
50
- const statusBadge = status.isRunning ? '● RUN' : '○ OFF';
51
- console.log(`Status: ${statusColor(statusBadge)}`);
52
-
53
- if (status.isRunning) {
54
- console.log(`PID: ${status.pid || 'N/A'}`);
55
- console.log(`Uptime: ${uptime}`);
56
- }
57
-
58
- console.log(`Port: ${config.port}`);
59
- console.log(`Host: ${config.host}`);
60
- console.log(`Endpoint: http://${config.host}:${config.port}`);
61
- console.log();
62
-
63
- // Available models
64
- console.log(chalk.bold('Available Models'));
65
- console.log(chalk.dim('─'.repeat(50)));
66
- console.log();
67
-
68
- if (runningServers.length === 0) {
69
- console.log(chalk.dim('No running servers found'));
70
- console.log();
71
- console.log(chalk.yellow('⚠️ Start a server first:'));
72
- console.log(chalk.dim(' llamacpp server create <model>'));
73
- } else {
74
- runningServers.forEach(server => {
75
- console.log(` ${chalk.green('●')} ${server.modelName}`);
76
- console.log(chalk.dim(` Port: ${server.port}`));
77
- console.log(chalk.dim(` Backend: http://${server.host}:${server.port}`));
78
- console.log();
79
- });
80
- }
81
-
82
- // Configuration
83
- console.log(chalk.bold('Configuration'));
84
- console.log(chalk.dim('─'.repeat(50)));
85
- console.log();
86
- console.log(`Health Check Interval: ${config.healthCheckInterval}ms`);
87
- console.log(`Request Timeout: ${config.requestTimeout}ms`);
88
- console.log();
89
-
90
- // System paths
91
- console.log(chalk.bold('System Paths'));
92
- console.log(chalk.dim('─'.repeat(50)));
93
- console.log();
94
- console.log(chalk.dim(`Config: ${config.plistPath.replace(config.label + '.plist', 'router.json').replace('LaunchAgents', '.llamacpp')}`));
95
- console.log(chalk.dim(`Plist: ${config.plistPath}`));
96
- console.log(chalk.dim(`Stdout: ${config.stdoutPath}`));
97
- console.log(chalk.dim(`Stderr: ${config.stderrPath}`));
98
- console.log();
99
-
100
- // Quick commands
101
- console.log(chalk.bold('Quick Commands'));
102
- console.log(chalk.dim('─'.repeat(50)));
103
- console.log();
104
-
105
- if (status.isRunning) {
106
- console.log(chalk.dim(' Stop: llamacpp router stop'));
107
- console.log(chalk.dim(' Restart: llamacpp router restart'));
108
- console.log(chalk.dim(` Logs: tail -f ${config.stderrPath}`));
109
- console.log(chalk.dim(' Config: llamacpp router config --port <port> --restart'));
110
- } else {
111
- console.log(chalk.dim(' Start: llamacpp router start'));
112
- console.log(chalk.dim(' Config: llamacpp router config --port <port>'));
113
- console.log(chalk.dim(` Logs: cat ${config.stderrPath}`));
114
- }
115
- console.log();
116
- } catch (error) {
117
- throw new Error(`Failed to get router status: ${(error as Error).message}`);
118
- }
119
- }
@@ -1,33 +0,0 @@
1
- import chalk from 'chalk';
2
- import { routerManager } from '../../lib/router-manager';
3
-
4
- export async function routerStopCommand(): Promise<void> {
5
- console.log(chalk.blue('⏹️ Stopping router...'));
6
-
7
- try {
8
- // Check if router exists
9
- const config = await routerManager.loadConfig();
10
- if (!config) {
11
- throw new Error('Router configuration not found. Use "llamacpp router start" to create it.');
12
- }
13
-
14
- // Check if already stopped
15
- if (config.status !== 'running') {
16
- console.log(chalk.yellow('⚠️ Router is not running'));
17
- return;
18
- }
19
-
20
- // Stop router
21
- await routerManager.stop();
22
-
23
- // Display success
24
- console.log();
25
- console.log(chalk.green('✅ Router stopped successfully'));
26
- console.log();
27
- console.log(chalk.dim('Quick commands:'));
28
- console.log(chalk.dim(' Start: llamacpp router start'));
29
- console.log(chalk.dim(' Status: llamacpp router status'));
30
- } catch (error) {
31
- throw new Error(`Failed to stop router: ${(error as Error).message}`);
32
- }
33
- }
@@ -1,233 +0,0 @@
1
- import chalk from 'chalk';
2
- import * as readline from 'readline';
3
- import { stateManager } from '../lib/state-manager';
4
- import { startCommand } from './start';
5
- import { statusChecker } from '../lib/status-checker';
6
- import { ServerConfig } from '../types/server-config';
7
-
8
- interface ChatMessage {
9
- role: 'system' | 'user' | 'assistant';
10
- content: string;
11
- }
12
-
13
- interface ChatCompletionChunk {
14
- id: string;
15
- object: string;
16
- created: number;
17
- model: string;
18
- choices: Array<{
19
- index: number;
20
- delta: {
21
- role?: string;
22
- content?: string;
23
- };
24
- finish_reason: string | null;
25
- }>;
26
- }
27
-
28
- interface RunOptions {
29
- message?: string;
30
- }
31
-
32
- export async function runCommand(modelIdentifier: string, options: RunOptions = {}): Promise<void> {
33
- await stateManager.initialize();
34
-
35
- // 1. Find or start server
36
- let server = await stateManager.findServer(modelIdentifier);
37
-
38
- if (!server) {
39
- // Try to resolve as a model name and start it
40
- console.log(chalk.blue(`🚀 No running server found. Starting ${modelIdentifier}...\n`));
41
- try {
42
- await startCommand(modelIdentifier);
43
- server = await stateManager.findServer(modelIdentifier);
44
- if (!server) {
45
- throw new Error('Failed to start server');
46
- }
47
- console.log(); // Add blank line after start output
48
- } catch (error) {
49
- throw new Error(`Failed to start server: ${(error as Error).message}`);
50
- }
51
- }
52
-
53
- // 2. Verify server is running
54
- const status = await statusChecker.checkServer(server);
55
- if (!status.isRunning) {
56
- throw new Error(`Server exists but is not running. Start it with: llamacpp server start ${server.id}`);
57
- }
58
-
59
- // 3. If message provided, do one-shot mode
60
- if (options.message) {
61
- const conversationHistory: ChatMessage[] = [
62
- {
63
- role: 'user',
64
- content: options.message,
65
- },
66
- ];
67
-
68
- try {
69
- await streamChatCompletion(server, conversationHistory);
70
- console.log(); // Blank line after response
71
- process.exit(0);
72
- } catch (error) {
73
- console.error(chalk.red(`\n❌ Error: ${(error as Error).message}\n`));
74
- process.exit(1);
75
- }
76
- return;
77
- }
78
-
79
- // 4. Start REPL
80
- console.log(chalk.green(`💬 Connected to ${server.modelName} (port ${server.port})`));
81
- console.log(chalk.dim(`Type your message and press Enter. Use /exit to quit, /clear to reset history, /help for commands.\n`));
82
-
83
- const conversationHistory: ChatMessage[] = [];
84
- const rl = readline.createInterface({
85
- input: process.stdin,
86
- output: process.stdout,
87
- prompt: chalk.cyan('You: '),
88
- });
89
-
90
- // Handle graceful shutdown
91
- const cleanup = () => {
92
- rl.close();
93
- console.log(chalk.dim('\n\nGoodbye!'));
94
- process.exit(0);
95
- };
96
-
97
- process.on('SIGINT', cleanup);
98
- process.on('SIGTERM', cleanup);
99
-
100
- rl.prompt();
101
-
102
- rl.on('line', async (input: string) => {
103
- const line = input.trim();
104
-
105
- // Handle special commands
106
- if (line === '/exit' || line === '/quit') {
107
- cleanup();
108
- return;
109
- }
110
-
111
- if (line === '/clear') {
112
- conversationHistory.length = 0;
113
- console.log(chalk.dim('✓ Conversation history cleared\n'));
114
- rl.prompt();
115
- return;
116
- }
117
-
118
- if (line === '/help') {
119
- console.log(chalk.bold('\nAvailable commands:'));
120
- console.log(chalk.dim(' /exit, /quit - Exit the chat'));
121
- console.log(chalk.dim(' /clear - Clear conversation history'));
122
- console.log(chalk.dim(' /help - Show this help message\n'));
123
- rl.prompt();
124
- return;
125
- }
126
-
127
- if (!line) {
128
- rl.prompt();
129
- return;
130
- }
131
-
132
- // Add user message to history
133
- conversationHistory.push({
134
- role: 'user',
135
- content: line,
136
- });
137
-
138
- // Send to API and stream response
139
- try {
140
- await streamChatCompletion(server, conversationHistory);
141
- console.log(); // Blank line after response
142
- } catch (error) {
143
- console.error(chalk.red(`\n❌ Error: ${(error as Error).message}\n`));
144
- }
145
-
146
- rl.prompt();
147
- });
148
-
149
- rl.on('close', () => {
150
- cleanup();
151
- });
152
- }
153
-
154
- async function streamChatCompletion(
155
- server: ServerConfig,
156
- messages: ChatMessage[]
157
- ): Promise<void> {
158
- const url = `http://localhost:${server.port}/v1/chat/completions`;
159
-
160
- const response = await fetch(url, {
161
- method: 'POST',
162
- headers: {
163
- 'Content-Type': 'application/json',
164
- },
165
- body: JSON.stringify({
166
- model: server.modelName,
167
- messages: messages,
168
- stream: true,
169
- temperature: 0.7,
170
- }),
171
- });
172
-
173
- if (!response.ok) {
174
- const errorText = await response.text();
175
- throw new Error(`API request failed (${response.status}): ${errorText}`);
176
- }
177
-
178
- if (!response.body) {
179
- throw new Error('Response body is null');
180
- }
181
-
182
- // Display assistant prefix
183
- process.stdout.write(chalk.magenta('Assistant: '));
184
-
185
- let fullResponse = '';
186
- const reader = response.body.getReader();
187
- const decoder = new TextDecoder();
188
-
189
- try {
190
- while (true) {
191
- const { done, value } = await reader.read();
192
- if (done) break;
193
-
194
- const chunk = decoder.decode(value, { stream: true });
195
- const lines = chunk.split('\n').filter((line) => line.trim().startsWith('data:'));
196
-
197
- for (const line of lines) {
198
- const data = line.replace(/^data:\s*/, '').trim();
199
-
200
- if (data === '[DONE]') {
201
- continue;
202
- }
203
-
204
- if (!data) {
205
- continue;
206
- }
207
-
208
- try {
209
- const parsed: ChatCompletionChunk = JSON.parse(data);
210
- const content = parsed.choices[0]?.delta?.content;
211
-
212
- if (content) {
213
- process.stdout.write(content);
214
- fullResponse += content;
215
- }
216
- } catch (parseError) {
217
- // Skip malformed JSON chunks
218
- continue;
219
- }
220
- }
221
- }
222
- } finally {
223
- reader.releaseLock();
224
- }
225
-
226
- // Add assistant response to history
227
- if (fullResponse) {
228
- messages.push({
229
- role: 'assistant',
230
- content: fullResponse,
231
- });
232
- }
233
- }
@@ -1,107 +0,0 @@
1
- import chalk from 'chalk';
2
- import Table from 'cli-table3';
3
- import { modelSearch } from '../lib/model-search';
4
- import { formatBytes } from '../utils/format-utils';
5
-
6
- interface SearchOptions {
7
- limit?: number;
8
- files?: number | boolean;
9
- }
10
-
11
- export async function searchCommand(query: string, options: SearchOptions): Promise<void> {
12
- const limit = options.limit || 20;
13
-
14
- console.log(chalk.blue(`🔍 Searching Hugging Face for: "${query}"\n`));
15
-
16
- try {
17
- const results = await modelSearch.searchModels(query, limit);
18
-
19
- if (results.length === 0) {
20
- console.log(chalk.yellow('No models found.'));
21
- console.log(chalk.dim('Try a different search query or browse: https://huggingface.co/models'));
22
- return;
23
- }
24
-
25
- const table = new Table({
26
- head: ['#', 'MODEL ID', 'DOWNLOADS', 'LIKES'],
27
- colWidths: [4, 55, 12, 8],
28
- });
29
-
30
- for (let i = 0; i < results.length; i++) {
31
- const model = results[i];
32
- table.push([
33
- chalk.dim((i + 1).toString()),
34
- model.modelId,
35
- model.downloads.toLocaleString(),
36
- model.likes.toString(),
37
- ]);
38
- }
39
-
40
- console.log(table.toString());
41
-
42
- console.log(chalk.dim(`\nShowing ${results.length} results`));
43
- console.log(chalk.dim('\nTo see files in a model:'));
44
- console.log(chalk.dim(' llamacpp search "<query>" --files <number>'));
45
- console.log(chalk.dim(' Example: llamacpp search "llama 3b" --files 1'));
46
- console.log(chalk.dim('\nTo download:'));
47
- console.log(chalk.dim(' llamacpp pull <model-id>/<file.gguf>'));
48
-
49
- // Handle --files flag
50
- if (options.files !== undefined && options.files !== false) {
51
- let selectedIndex: number;
52
-
53
- if (typeof options.files === 'number') {
54
- // User specified a number: --files 1
55
- selectedIndex = options.files - 1; // Convert to 0-based index
56
- } else if (results.length === 1) {
57
- // No number specified but only one result
58
- selectedIndex = 0;
59
- } else {
60
- // Multiple results but no number specified
61
- console.log(chalk.yellow('\n⚠️ Multiple results found. Specify which one:'));
62
- console.log(chalk.dim(' llamacpp search "<query>" --files 1'));
63
- return;
64
- }
65
-
66
- // Validate index
67
- if (selectedIndex < 0 || selectedIndex >= results.length) {
68
- console.log(chalk.red(`\n❌ Invalid index. Please specify a number between 1 and ${results.length}`));
69
- return;
70
- }
71
-
72
- await showModelFiles(results[selectedIndex].modelId, selectedIndex + 1);
73
- }
74
- } catch (error) {
75
- throw new Error(`Search failed: ${(error as Error).message}`);
76
- }
77
- }
78
-
79
- async function showModelFiles(modelId: string, index?: number): Promise<void> {
80
- const indexPrefix = index ? chalk.dim(`[${index}] `) : '';
81
- console.log(chalk.blue(`\n📦 GGUF files in ${indexPrefix}${modelId}:\n`));
82
-
83
- try {
84
- const files = await modelSearch.getModelFiles(modelId);
85
-
86
- if (files.length === 0) {
87
- console.log(chalk.yellow('No GGUF files found in this model.'));
88
- return;
89
- }
90
-
91
- const table = new Table({
92
- head: ['FILENAME'],
93
- colWidths: [70],
94
- });
95
-
96
- for (const file of files) {
97
- table.push([file]);
98
- }
99
-
100
- console.log(table.toString());
101
-
102
- console.log(chalk.dim(`\nTo download:`));
103
- console.log(chalk.dim(` llamacpp pull ${modelId}/${files[0]}`));
104
- } catch (error) {
105
- console.log(chalk.yellow(`\n⚠️ Could not fetch file list: ${(error as Error).message}`));
106
- }
107
- }