@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.
Files changed (136) hide show
  1. package/README.md +294 -168
  2. package/dist/cli.js +35 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/launch/claude.d.ts +6 -0
  5. package/dist/commands/launch/claude.d.ts.map +1 -0
  6. package/dist/commands/launch/claude.js +277 -0
  7. package/dist/commands/launch/claude.js.map +1 -0
  8. package/dist/lib/integration-checker.d.ts +26 -0
  9. package/dist/lib/integration-checker.d.ts.map +1 -0
  10. package/dist/lib/integration-checker.js +77 -0
  11. package/dist/lib/integration-checker.js.map +1 -0
  12. package/dist/lib/router-manager.d.ts +4 -0
  13. package/dist/lib/router-manager.d.ts.map +1 -1
  14. package/dist/lib/router-manager.js +10 -0
  15. package/dist/lib/router-manager.js.map +1 -1
  16. package/dist/lib/router-server.d.ts +13 -0
  17. package/dist/lib/router-server.d.ts.map +1 -1
  18. package/dist/lib/router-server.js +267 -7
  19. package/dist/lib/router-server.js.map +1 -1
  20. package/dist/types/integration-config.d.ts +28 -0
  21. package/dist/types/integration-config.d.ts.map +1 -0
  22. package/dist/types/integration-config.js +3 -0
  23. package/dist/types/integration-config.js.map +1 -0
  24. package/package.json +10 -2
  25. package/web/dist/assets/index-Bin89Lwr.css +1 -0
  26. package/web/dist/assets/index-CVmonw3T.js +17 -0
  27. package/web/{index.html → dist/index.html} +2 -1
  28. package/.versionrc.json +0 -16
  29. package/CHANGELOG.md +0 -213
  30. package/docs/images/.gitkeep +0 -1
  31. package/docs/images/web-ui-servers.png +0 -0
  32. package/src/cli.ts +0 -523
  33. package/src/commands/admin/config.ts +0 -121
  34. package/src/commands/admin/logs.ts +0 -91
  35. package/src/commands/admin/restart.ts +0 -26
  36. package/src/commands/admin/start.ts +0 -27
  37. package/src/commands/admin/status.ts +0 -84
  38. package/src/commands/admin/stop.ts +0 -16
  39. package/src/commands/config-global.ts +0 -38
  40. package/src/commands/config.ts +0 -323
  41. package/src/commands/create.ts +0 -183
  42. package/src/commands/delete.ts +0 -74
  43. package/src/commands/list.ts +0 -37
  44. package/src/commands/logs-all.ts +0 -251
  45. package/src/commands/logs.ts +0 -345
  46. package/src/commands/monitor.ts +0 -110
  47. package/src/commands/ps.ts +0 -84
  48. package/src/commands/pull.ts +0 -44
  49. package/src/commands/rm.ts +0 -107
  50. package/src/commands/router/config.ts +0 -116
  51. package/src/commands/router/logs.ts +0 -256
  52. package/src/commands/router/restart.ts +0 -36
  53. package/src/commands/router/start.ts +0 -60
  54. package/src/commands/router/status.ts +0 -119
  55. package/src/commands/router/stop.ts +0 -33
  56. package/src/commands/run.ts +0 -233
  57. package/src/commands/search.ts +0 -107
  58. package/src/commands/server-show.ts +0 -161
  59. package/src/commands/show.ts +0 -207
  60. package/src/commands/start.ts +0 -101
  61. package/src/commands/stop.ts +0 -39
  62. package/src/commands/tui.ts +0 -25
  63. package/src/lib/admin-manager.ts +0 -435
  64. package/src/lib/admin-server.ts +0 -1243
  65. package/src/lib/config-generator.ts +0 -130
  66. package/src/lib/download-job-manager.ts +0 -213
  67. package/src/lib/history-manager.ts +0 -172
  68. package/src/lib/launchctl-manager.ts +0 -225
  69. package/src/lib/metrics-aggregator.ts +0 -257
  70. package/src/lib/model-downloader.ts +0 -328
  71. package/src/lib/model-scanner.ts +0 -157
  72. package/src/lib/model-search.ts +0 -114
  73. package/src/lib/models-dir-setup.ts +0 -46
  74. package/src/lib/port-manager.ts +0 -80
  75. package/src/lib/router-logger.ts +0 -201
  76. package/src/lib/router-manager.ts +0 -414
  77. package/src/lib/router-server.ts +0 -538
  78. package/src/lib/state-manager.ts +0 -206
  79. package/src/lib/status-checker.ts +0 -113
  80. package/src/lib/system-collector.ts +0 -315
  81. package/src/tui/ConfigApp.ts +0 -1085
  82. package/src/tui/HistoricalMonitorApp.ts +0 -587
  83. package/src/tui/ModelsApp.ts +0 -368
  84. package/src/tui/MonitorApp.ts +0 -386
  85. package/src/tui/MultiServerMonitorApp.ts +0 -1833
  86. package/src/tui/RootNavigator.ts +0 -74
  87. package/src/tui/SearchApp.ts +0 -511
  88. package/src/tui/SplashScreen.ts +0 -149
  89. package/src/types/admin-config.ts +0 -25
  90. package/src/types/global-config.ts +0 -26
  91. package/src/types/history-types.ts +0 -39
  92. package/src/types/model-info.ts +0 -8
  93. package/src/types/monitor-types.ts +0 -162
  94. package/src/types/router-config.ts +0 -25
  95. package/src/types/server-config.ts +0 -46
  96. package/src/utils/downsample-utils.ts +0 -128
  97. package/src/utils/file-utils.ts +0 -146
  98. package/src/utils/format-utils.ts +0 -98
  99. package/src/utils/log-parser.ts +0 -284
  100. package/src/utils/log-utils.ts +0 -178
  101. package/src/utils/process-utils.ts +0 -316
  102. package/src/utils/prompt-utils.ts +0 -47
  103. package/test-load.sh +0 -100
  104. package/tsconfig.json +0 -20
  105. package/web/eslint.config.js +0 -23
  106. package/web/llamacpp-web-dist.tar.gz +0 -0
  107. package/web/package-lock.json +0 -4017
  108. package/web/package.json +0 -38
  109. package/web/postcss.config.js +0 -6
  110. package/web/src/App.css +0 -42
  111. package/web/src/App.tsx +0 -86
  112. package/web/src/assets/react.svg +0 -1
  113. package/web/src/components/ApiKeyPrompt.tsx +0 -71
  114. package/web/src/components/CreateServerModal.tsx +0 -372
  115. package/web/src/components/DownloadProgress.tsx +0 -123
  116. package/web/src/components/Nav.tsx +0 -89
  117. package/web/src/components/RouterConfigModal.tsx +0 -240
  118. package/web/src/components/SearchModal.tsx +0 -306
  119. package/web/src/components/ServerConfigModal.tsx +0 -291
  120. package/web/src/hooks/useApi.ts +0 -259
  121. package/web/src/index.css +0 -42
  122. package/web/src/lib/api.ts +0 -226
  123. package/web/src/main.tsx +0 -10
  124. package/web/src/pages/Dashboard.tsx +0 -103
  125. package/web/src/pages/Models.tsx +0 -258
  126. package/web/src/pages/Router.tsx +0 -270
  127. package/web/src/pages/RouterLogs.tsx +0 -201
  128. package/web/src/pages/ServerLogs.tsx +0 -553
  129. package/web/src/pages/Servers.tsx +0 -358
  130. package/web/src/types/api.ts +0 -140
  131. package/web/tailwind.config.js +0 -31
  132. package/web/tsconfig.app.json +0 -28
  133. package/web/tsconfig.json +0 -7
  134. package/web/tsconfig.node.json +0 -26
  135. package/web/vite.config.ts +0 -25
  136. /package/web/{public → dist}/vite.svg +0 -0
@@ -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
- }
@@ -1,161 +0,0 @@
1
- import chalk from 'chalk';
2
- import { stateManager } from '../lib/state-manager';
3
- import { statusChecker } from '../lib/status-checker';
4
- import { formatUptime, formatBytes, formatContextSize } from '../utils/format-utils';
5
- import { getProcessMemory } from '../utils/process-utils';
6
- import { getFileSize, formatFileSize, getArchivedLogInfo } from '../utils/log-utils';
7
- import { fileExists } from '../utils/file-utils';
8
-
9
- export async function serverShowCommand(identifier: string): Promise<void> {
10
- // Find the server
11
- const server = await stateManager.findServer(identifier);
12
-
13
- if (!server) {
14
- console.error(chalk.red(`❌ Server not found: ${identifier}`));
15
- console.log(chalk.dim('\nAvailable servers:'));
16
- const allServers = await stateManager.getAllServers();
17
- if (allServers.length === 0) {
18
- console.log(chalk.dim(' (none)'));
19
- console.log(chalk.dim('\nCreate a server: llamacpp server create <model-filename>'));
20
- } else {
21
- allServers.forEach(s => {
22
- console.log(chalk.dim(` - ${s.id} (port ${s.port})`));
23
- });
24
- }
25
- process.exit(1);
26
- }
27
-
28
- // Update status to get real-time info
29
- console.log(chalk.dim('Checking server status...\n'));
30
- const updatedServer = await statusChecker.updateServerStatus(server);
31
-
32
- // Display server information
33
- console.log(chalk.bold('Server Configuration:'));
34
- console.log('─'.repeat(70));
35
-
36
- // Basic info
37
- console.log(`${chalk.bold('Server ID:')} ${updatedServer.id}`);
38
- console.log(`${chalk.bold('Model Name:')} ${updatedServer.modelName}`);
39
- console.log(`${chalk.bold('Model Path:')} ${chalk.dim(updatedServer.modelPath)}`);
40
- console.log(`${chalk.bold('Host:')} ${updatedServer.host}`);
41
- console.log(`${chalk.bold('Port:')} http://${updatedServer.host}:${updatedServer.port}`);
42
-
43
- // Status with color
44
- let statusText: string;
45
- let statusColor: (text: string) => string;
46
- switch (updatedServer.status) {
47
- case 'running':
48
- statusText = '✅ RUNNING';
49
- statusColor = chalk.green;
50
- break;
51
- case 'crashed':
52
- statusText = '❌ CRASHED';
53
- statusColor = chalk.red;
54
- break;
55
- default:
56
- statusText = '⚠️ STOPPED';
57
- statusColor = chalk.yellow;
58
- }
59
- console.log(`${chalk.bold('Status:')} ${statusColor(statusText)}`);
60
-
61
- if (updatedServer.pid) {
62
- console.log(`${chalk.bold('PID:')} ${updatedServer.pid}`);
63
- }
64
-
65
- // Runtime info for running servers
66
- if (updatedServer.status === 'running') {
67
- if (updatedServer.lastStarted) {
68
- const uptime = formatUptime(updatedServer.lastStarted);
69
- console.log(`${chalk.bold('Uptime:')} ${uptime}`);
70
- }
71
-
72
- if (updatedServer.pid) {
73
- const cpuMemoryBytes = await getProcessMemory(updatedServer.pid);
74
- if (cpuMemoryBytes !== null) {
75
- const metalMemoryBytes = updatedServer.metalMemoryMB ? updatedServer.metalMemoryMB * 1024 * 1024 : 0;
76
- const totalMemoryBytes = cpuMemoryBytes + metalMemoryBytes;
77
-
78
- if (metalMemoryBytes > 0) {
79
- console.log(`${chalk.bold('Memory:')} ${formatBytes(totalMemoryBytes)} (CPU: ${formatBytes(cpuMemoryBytes)}, GPU: ${formatBytes(metalMemoryBytes)})`);
80
- } else {
81
- console.log(`${chalk.bold('Memory:')} ${formatBytes(cpuMemoryBytes)} (CPU only)`);
82
- }
83
- }
84
- }
85
- }
86
-
87
- // Configuration section
88
- console.log('\n' + '─'.repeat(70));
89
- console.log(chalk.bold('Configuration:'));
90
- console.log('─'.repeat(70));
91
- console.log(`${chalk.bold('Threads:')} ${updatedServer.threads}`);
92
- console.log(`${chalk.bold('Context Size:')} ${formatContextSize(updatedServer.ctxSize)} (total)`);
93
- console.log(`${chalk.bold('GPU Layers:')} ${updatedServer.gpuLayers}`);
94
- console.log(`${chalk.bold('Embeddings:')} ${updatedServer.embeddings ? 'enabled' : 'disabled'}`);
95
- console.log(`${chalk.bold('Jinja:')} ${updatedServer.jinja ? 'enabled' : 'disabled'}`);
96
- console.log(`${chalk.bold('Verbose Logs:')} ${updatedServer.verbose ? chalk.green('enabled') : chalk.dim('disabled')}`);
97
- if (updatedServer.customFlags && updatedServer.customFlags.length > 0) {
98
- console.log(`${chalk.bold('Custom Flags:')} ${updatedServer.customFlags.join(' ')}`);
99
- }
100
-
101
- // Logs section
102
- console.log('\n' + '─'.repeat(70));
103
- console.log(chalk.bold('Logs:'));
104
- console.log('─'.repeat(70));
105
-
106
- // Get current log sizes
107
- const stderrSize = (await fileExists(updatedServer.stderrPath))
108
- ? await getFileSize(updatedServer.stderrPath)
109
- : 0;
110
- const stdoutSize = (await fileExists(updatedServer.stdoutPath))
111
- ? await getFileSize(updatedServer.stdoutPath)
112
- : 0;
113
-
114
- // Get archived log info
115
- const archivedInfo = await getArchivedLogInfo(updatedServer.id);
116
-
117
- console.log(`${chalk.bold('Stderr:')} ${formatFileSize(stderrSize)} (current)`);
118
- console.log(`${chalk.bold('Stdout:')} ${formatFileSize(stdoutSize)} (current)`);
119
-
120
- if (archivedInfo.count > 0) {
121
- console.log(`${chalk.bold('Archived:')} ${formatFileSize(archivedInfo.totalSize)} (${archivedInfo.count} file${archivedInfo.count > 1 ? 's' : ''})`);
122
- }
123
-
124
- // Timestamps section
125
- console.log('\n' + '─'.repeat(70));
126
- console.log(chalk.bold('Timestamps:'));
127
- console.log('─'.repeat(70));
128
- console.log(`${chalk.bold('Created:')} ${new Date(updatedServer.createdAt).toLocaleString()}`);
129
- if (updatedServer.lastStarted) {
130
- console.log(`${chalk.bold('Last Started:')} ${new Date(updatedServer.lastStarted).toLocaleString()}`);
131
- }
132
- if (updatedServer.lastStopped) {
133
- console.log(`${chalk.bold('Last Stopped:')} ${new Date(updatedServer.lastStopped).toLocaleString()}`);
134
- }
135
-
136
- // System paths section
137
- console.log('\n' + '─'.repeat(70));
138
- console.log(chalk.bold('System Paths:'));
139
- console.log('─'.repeat(70));
140
- console.log(`${chalk.bold('Service Label:')} ${updatedServer.label}`);
141
- console.log(`${chalk.bold('Plist File:')} ${chalk.dim(updatedServer.plistPath)}`);
142
- console.log(`${chalk.bold('Stdout Log:')} ${chalk.dim(updatedServer.stdoutPath)}`);
143
- console.log(`${chalk.bold('Stderr Log:')} ${chalk.dim(updatedServer.stderrPath)}`);
144
-
145
- // Helpful commands
146
- console.log('\n' + '─'.repeat(70));
147
- console.log(chalk.bold('Quick Commands:'));
148
- console.log('─'.repeat(70));
149
-
150
- if (updatedServer.status === 'running') {
151
- console.log(chalk.dim(' View logs: ') + `llamacpp server logs ${updatedServer.id}`);
152
- console.log(chalk.dim(' Interactive chat: ') + `llamacpp server run ${updatedServer.id}`);
153
- console.log(chalk.dim(' Stop server: ') + `llamacpp server stop ${updatedServer.id}`);
154
- } else {
155
- console.log(chalk.dim(' Start server: ') + `llamacpp server start ${updatedServer.id}`);
156
- if (updatedServer.status === 'crashed') {
157
- console.log(chalk.dim(' View error logs: ') + `llamacpp server logs ${updatedServer.id} --errors`);
158
- }
159
- }
160
- console.log(chalk.dim(' Remove server: ') + `llamacpp server rm ${updatedServer.id}`);
161
- }
@@ -1,207 +0,0 @@
1
- import chalk from 'chalk';
2
- import Table from 'cli-table3';
3
- import { modelSearch } from '../lib/model-search';
4
- import { modelDownloader } from '../lib/model-downloader';
5
- import { formatBytes } from '../utils/format-utils';
6
- import * as https from 'https';
7
-
8
- interface ShowOptions {
9
- file?: string;
10
- }
11
-
12
- interface ModelDetails {
13
- modelId: string;
14
- author: string;
15
- modelName: string;
16
- downloads: number;
17
- likes: number;
18
- lastModified: string;
19
- tags: string[];
20
- library?: string;
21
- license?: string;
22
- }
23
-
24
- interface FileDetails {
25
- filename: string;
26
- size: number;
27
- lfs?: {
28
- oid: string;
29
- size: number;
30
- };
31
- }
32
-
33
- export async function showCommand(identifier: string, options: ShowOptions): Promise<void> {
34
- // Parse identifier
35
- const parsed = modelDownloader.parseHFIdentifier(identifier);
36
- const filename = options.file || parsed.file;
37
-
38
- console.log(chalk.blue('📋 Fetching model information...\n'));
39
-
40
- try {
41
- // Get model details
42
- const modelDetails = await getModelDetails(parsed.repo);
43
-
44
- // Display model information
45
- displayModelInfo(modelDetails);
46
-
47
- // If specific file requested, show file details
48
- if (filename) {
49
- console.log(chalk.blue('\n📄 File Details:\n'));
50
- await displayFileInfo(parsed.repo, filename);
51
- } else {
52
- // Show all GGUF files
53
- console.log(chalk.blue('\n📦 Available GGUF Files:\n'));
54
- await displayAllFiles(parsed.repo);
55
- }
56
- } catch (error) {
57
- throw new Error(`Failed to fetch model details: ${(error as Error).message}`);
58
- }
59
- }
60
-
61
- async function getModelDetails(modelId: string): Promise<ModelDetails> {
62
- return new Promise((resolve, reject) => {
63
- const url = `https://huggingface.co/api/models/${modelId}`;
64
-
65
- https.get(url, (response) => {
66
- let data = '';
67
-
68
- response.on('data', (chunk) => {
69
- data += chunk;
70
- });
71
-
72
- response.on('end', () => {
73
- try {
74
- const json = JSON.parse(data);
75
- const parts = modelId.split('/');
76
-
77
- resolve({
78
- modelId,
79
- author: parts[0] || '',
80
- modelName: parts.slice(1).join('/') || '',
81
- downloads: json.downloads || 0,
82
- likes: json.likes || 0,
83
- lastModified: json.lastModified || '',
84
- tags: json.tags || [],
85
- library: json.library_name,
86
- license: json.cardData?.license,
87
- });
88
- } catch (error) {
89
- reject(new Error(`Failed to parse model data: ${(error as Error).message}`));
90
- }
91
- });
92
- }).on('error', (error) => {
93
- reject(error);
94
- });
95
- });
96
- }
97
-
98
- function displayModelInfo(details: ModelDetails): void {
99
- console.log(chalk.bold('Model Information:'));
100
- console.log(chalk.dim('─'.repeat(60)));
101
- console.log(`${chalk.bold('ID:')} ${details.modelId}`);
102
- console.log(`${chalk.bold('Author:')} ${details.author}`);
103
- console.log(`${chalk.bold('Downloads:')} ${details.downloads.toLocaleString()}`);
104
- console.log(`${chalk.bold('Likes:')} ${details.likes.toLocaleString()}`);
105
- console.log(`${chalk.bold('Last Updated:')} ${new Date(details.lastModified).toLocaleDateString()}`);
106
-
107
- if (details.license) {
108
- console.log(`${chalk.bold('License:')} ${details.license}`);
109
- }
110
-
111
- if (details.tags.length > 0) {
112
- const relevantTags = details.tags
113
- .filter(tag => !tag.startsWith('arxiv:') && !tag.startsWith('dataset:'))
114
- .slice(0, 5);
115
- if (relevantTags.length > 0) {
116
- console.log(`${chalk.bold('Tags:')} ${relevantTags.join(', ')}`);
117
- }
118
- }
119
- }
120
-
121
- async function displayFileInfo(modelId: string, filename: string): Promise<void> {
122
- const files = await getModelFiles(modelId);
123
- const file = files.find(f => f.filename === filename);
124
-
125
- if (!file) {
126
- console.log(chalk.yellow(`⚠️ File not found: ${filename}`));
127
- console.log(chalk.dim('\nAvailable files:'));
128
- files.forEach(f => console.log(chalk.dim(` - ${f.filename}`)));
129
- return;
130
- }
131
-
132
- const size = file.lfs?.size || file.size || 0;
133
-
134
- console.log(`${chalk.bold('Filename:')} ${file.filename}`);
135
- console.log(`${chalk.bold('Size:')} ${formatBytes(size)}`);
136
-
137
- if (file.lfs) {
138
- console.log(`${chalk.bold('SHA256:')} ${file.lfs.oid.substring(0, 16)}...`);
139
- }
140
-
141
- console.log(chalk.dim('\nTo download:'));
142
- console.log(chalk.dim(` llamacpp pull ${modelId}/${filename}`));
143
- }
144
-
145
- async function displayAllFiles(modelId: string): Promise<void> {
146
- const files = await getModelFiles(modelId);
147
- const ggufFiles = files.filter(f => f.filename.toLowerCase().endsWith('.gguf'));
148
-
149
- if (ggufFiles.length === 0) {
150
- console.log(chalk.yellow('No GGUF files found in this model.'));
151
- return;
152
- }
153
-
154
- const table = new Table({
155
- head: ['FILENAME', 'SIZE'],
156
- colWidths: [55, 12],
157
- });
158
-
159
- for (const file of ggufFiles) {
160
- // LFS files store size in the lfs object, regular files in size field
161
- const size = (file.lfs && typeof file.lfs.size === 'number') ? file.lfs.size : file.size;
162
- table.push([
163
- file.filename,
164
- size > 0 ? formatBytes(size) : chalk.dim('Unknown'),
165
- ]);
166
- }
167
-
168
- console.log(table.toString());
169
-
170
- const totalSize = ggufFiles.reduce((sum, f) => {
171
- const size = (f.lfs && typeof f.lfs.size === 'number') ? f.lfs.size : f.size;
172
- return sum + size;
173
- }, 0);
174
- console.log(chalk.dim(`\nTotal: ${ggufFiles.length} files (${formatBytes(totalSize)})`));
175
- console.log(chalk.dim('\nTo download a specific file:'));
176
- console.log(chalk.dim(` llamacpp pull ${modelId}/<filename>`));
177
- }
178
-
179
- async function getModelFiles(modelId: string): Promise<FileDetails[]> {
180
- return new Promise((resolve, reject) => {
181
- const url = `https://huggingface.co/api/models/${modelId}`;
182
-
183
- https.get(url, (response) => {
184
- let data = '';
185
-
186
- response.on('data', (chunk) => {
187
- data += chunk;
188
- });
189
-
190
- response.on('end', () => {
191
- try {
192
- const json = JSON.parse(data);
193
- const files: FileDetails[] = (json.siblings || []).map((file: any) => ({
194
- filename: file.rfilename,
195
- size: file.size || 0,
196
- lfs: file.lfs,
197
- }));
198
- resolve(files);
199
- } catch (error) {
200
- reject(new Error(`Failed to parse file data: ${(error as Error).message}`));
201
- }
202
- });
203
- }).on('error', (error) => {
204
- reject(error);
205
- });
206
- });
207
- }