@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,259 +0,0 @@
1
- import { useQuery, useMutation, useQueryClient, keepPreviousData } from '@tanstack/react-query';
2
- import { api } from '../lib/api';
3
- import type { CreateServerRequest, UpdateServerRequest, UpdateRouterRequest } from '../types/api';
4
-
5
- // System
6
- export function useSystemStatus() {
7
- return useQuery({
8
- queryKey: ['system', 'status'],
9
- queryFn: () => api.getSystemStatus(),
10
- refetchInterval: 5000, // Auto-refresh every 5s
11
- });
12
- }
13
-
14
- // Servers
15
- export function useServers() {
16
- return useQuery({
17
- queryKey: ['servers'],
18
- queryFn: () => api.listServers(),
19
- refetchInterval: 5000, // Auto-refresh every 5s
20
- });
21
- }
22
-
23
- export function useServer(id: string) {
24
- return useQuery({
25
- queryKey: ['servers', id],
26
- queryFn: () => api.getServer(id),
27
- enabled: !!id,
28
- });
29
- }
30
-
31
- export function useCreateServer() {
32
- const queryClient = useQueryClient();
33
-
34
- return useMutation({
35
- mutationFn: (data: CreateServerRequest) => api.createServer(data),
36
- onSuccess: () => {
37
- queryClient.invalidateQueries({ queryKey: ['servers'] });
38
- queryClient.invalidateQueries({ queryKey: ['system'] });
39
- },
40
- });
41
- }
42
-
43
- export function useUpdateServer() {
44
- const queryClient = useQueryClient();
45
-
46
- return useMutation({
47
- mutationFn: ({ id, data }: { id: string; data: UpdateServerRequest }) =>
48
- api.updateServer(id, data),
49
- onSuccess: (_, variables) => {
50
- queryClient.invalidateQueries({ queryKey: ['servers'] });
51
- queryClient.invalidateQueries({ queryKey: ['servers', variables.id] });
52
- },
53
- });
54
- }
55
-
56
- export function useDeleteServer() {
57
- const queryClient = useQueryClient();
58
-
59
- return useMutation({
60
- mutationFn: (id: string) => api.deleteServer(id),
61
- onSuccess: () => {
62
- queryClient.invalidateQueries({ queryKey: ['servers'] });
63
- queryClient.invalidateQueries({ queryKey: ['system'] });
64
- },
65
- });
66
- }
67
-
68
- export function useStartServer() {
69
- const queryClient = useQueryClient();
70
-
71
- return useMutation({
72
- mutationFn: (id: string) => api.startServer(id),
73
- onSuccess: (_, id) => {
74
- queryClient.invalidateQueries({ queryKey: ['servers'] });
75
- queryClient.invalidateQueries({ queryKey: ['servers', id] });
76
- },
77
- });
78
- }
79
-
80
- export function useStopServer() {
81
- const queryClient = useQueryClient();
82
-
83
- return useMutation({
84
- mutationFn: (id: string) => api.stopServer(id),
85
- onSuccess: (_, id) => {
86
- queryClient.invalidateQueries({ queryKey: ['servers'] });
87
- queryClient.invalidateQueries({ queryKey: ['servers', id] });
88
- },
89
- });
90
- }
91
-
92
- export function useRestartServer() {
93
- const queryClient = useQueryClient();
94
-
95
- return useMutation({
96
- mutationFn: (id: string) => api.restartServer(id),
97
- onSuccess: (_, id) => {
98
- queryClient.invalidateQueries({ queryKey: ['servers'] });
99
- queryClient.invalidateQueries({ queryKey: ['servers', id] });
100
- },
101
- });
102
- }
103
-
104
- export function useServerLogs(serverId: string | null, lines = 500) {
105
- return useQuery({
106
- queryKey: ['serverLogs', serverId, lines],
107
- queryFn: () => api.getServerLogs(serverId!, 'both', lines),
108
- enabled: !!serverId,
109
- refetchInterval: 2000, // Auto-refresh every 2s
110
- });
111
- }
112
-
113
- // Models
114
- export function useModels() {
115
- return useQuery({
116
- queryKey: ['models'],
117
- queryFn: () => api.listModels(),
118
- refetchInterval: 10000, // Auto-refresh every 10s
119
- });
120
- }
121
-
122
- export function useModel(name: string) {
123
- return useQuery({
124
- queryKey: ['models', name],
125
- queryFn: () => api.getModel(name),
126
- enabled: !!name,
127
- });
128
- }
129
-
130
- export function useDeleteModel() {
131
- const queryClient = useQueryClient();
132
-
133
- return useMutation({
134
- mutationFn: ({ name, cascade }: { name: string; cascade: boolean }) =>
135
- api.deleteModel(name, cascade),
136
- onSuccess: () => {
137
- queryClient.invalidateQueries({ queryKey: ['models'] });
138
- queryClient.invalidateQueries({ queryKey: ['servers'] });
139
- queryClient.invalidateQueries({ queryKey: ['system'] });
140
- },
141
- });
142
- }
143
-
144
- export function useDownloadModel() {
145
- return useMutation({
146
- mutationFn: ({ repo, filename }: { repo: string; filename: string }) =>
147
- api.downloadModel(repo, filename),
148
- // Don't invalidate immediately - download is background job
149
- });
150
- }
151
-
152
- // Search
153
- export function useSearchModels() {
154
- return useMutation({
155
- mutationFn: ({ query, limit = 20 }: { query: string; limit?: number }) =>
156
- api.searchModels(query, limit),
157
- });
158
- }
159
-
160
- export function useModelFiles(repoId: string | null) {
161
- return useQuery({
162
- queryKey: ['modelFiles', repoId],
163
- queryFn: () => api.getModelFiles(repoId!),
164
- enabled: !!repoId,
165
- });
166
- }
167
-
168
- // Download Jobs
169
- export function useDownloadJobs(enabled = true) {
170
- return useQuery({
171
- queryKey: ['downloadJobs'],
172
- queryFn: () => api.listDownloadJobs(),
173
- refetchInterval: enabled ? 1000 : false, // Poll every 1s when enabled
174
- enabled,
175
- });
176
- }
177
-
178
- export function useDownloadJob(jobId: string | null) {
179
- return useQuery({
180
- queryKey: ['downloadJobs', jobId],
181
- queryFn: () => api.getDownloadJob(jobId!),
182
- enabled: !!jobId,
183
- refetchInterval: 500, // Fast polling for active job
184
- });
185
- }
186
-
187
- export function useCancelDownload() {
188
- const queryClient = useQueryClient();
189
-
190
- return useMutation({
191
- mutationFn: (jobId: string) => api.cancelDownloadJob(jobId),
192
- onSuccess: () => {
193
- queryClient.invalidateQueries({ queryKey: ['downloadJobs'] });
194
- },
195
- });
196
- }
197
-
198
- // Router
199
- export function useRouter() {
200
- return useQuery({
201
- queryKey: ['router'],
202
- queryFn: () => api.getRouter(),
203
- refetchInterval: 5000, // Auto-refresh every 5s
204
- placeholderData: keepPreviousData, // Keep previous data during refetch to prevent flash
205
- });
206
- }
207
-
208
- export function useStartRouter() {
209
- const queryClient = useQueryClient();
210
-
211
- return useMutation({
212
- mutationFn: () => api.startRouter(),
213
- onSuccess: () => {
214
- queryClient.invalidateQueries({ queryKey: ['router'] });
215
- },
216
- });
217
- }
218
-
219
- export function useStopRouter() {
220
- const queryClient = useQueryClient();
221
-
222
- return useMutation({
223
- mutationFn: () => api.stopRouter(),
224
- onSuccess: () => {
225
- queryClient.invalidateQueries({ queryKey: ['router'] });
226
- },
227
- });
228
- }
229
-
230
- export function useRestartRouter() {
231
- const queryClient = useQueryClient();
232
-
233
- return useMutation({
234
- mutationFn: () => api.restartRouter(),
235
- onSuccess: () => {
236
- queryClient.invalidateQueries({ queryKey: ['router'] });
237
- },
238
- });
239
- }
240
-
241
- export function useRouterLogs(lines = 500) {
242
- return useQuery({
243
- queryKey: ['routerLogs', lines],
244
- queryFn: () => api.getRouterLogs('both', lines),
245
- refetchInterval: 2000, // Auto-refresh every 2s
246
- placeholderData: keepPreviousData, // Keep previous data during refetch to prevent flash
247
- });
248
- }
249
-
250
- export function useUpdateRouter() {
251
- const queryClient = useQueryClient();
252
-
253
- return useMutation({
254
- mutationFn: (data: UpdateRouterRequest) => api.updateRouter(data),
255
- onSuccess: () => {
256
- queryClient.invalidateQueries({ queryKey: ['router'] });
257
- },
258
- });
259
- }
package/web/src/index.css DELETED
@@ -1,42 +0,0 @@
1
- @import "tailwindcss";
2
-
3
- :root {
4
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
5
- line-height: 1.5;
6
- font-weight: 400;
7
- font-synthesis: none;
8
- text-rendering: optimizeLegibility;
9
- -webkit-font-smoothing: antialiased;
10
- -moz-osx-font-smoothing: grayscale;
11
- }
12
-
13
- body {
14
- margin: 0;
15
- min-width: 320px;
16
- min-height: 100vh;
17
- background-color: #fafafa;
18
- color: #171717;
19
- }
20
-
21
- #root {
22
- min-height: 100vh;
23
- }
24
-
25
- /* Custom scrollbar for Ollama aesthetic */
26
- ::-webkit-scrollbar {
27
- width: 8px;
28
- height: 8px;
29
- }
30
-
31
- ::-webkit-scrollbar-track {
32
- background: transparent;
33
- }
34
-
35
- ::-webkit-scrollbar-thumb {
36
- background: #d4d4d4;
37
- border-radius: 4px;
38
- }
39
-
40
- ::-webkit-scrollbar-thumb:hover {
41
- background: #a3a3a3;
42
- }
@@ -1,226 +0,0 @@
1
- import type {
2
- Server,
3
- Model,
4
- SystemStatus,
5
- CreateServerRequest,
6
- UpdateServerRequest,
7
- ApiError,
8
- HFModelResult,
9
- DownloadJob,
10
- RouterInfo,
11
- UpdateRouterRequest,
12
- } from '../types/api';
13
-
14
- const API_BASE = ''; // Proxy handles routing
15
-
16
- class ApiClient {
17
- private apiKey: string | null = null;
18
-
19
- setApiKey(key: string) {
20
- this.apiKey = key;
21
- localStorage.setItem('llamacpp_api_key', key);
22
- }
23
-
24
- getApiKey(): string | null {
25
- if (!this.apiKey) {
26
- this.apiKey = localStorage.getItem('llamacpp_api_key');
27
- }
28
- return this.apiKey;
29
- }
30
-
31
- clearApiKey() {
32
- this.apiKey = null;
33
- localStorage.removeItem('llamacpp_api_key');
34
- }
35
-
36
- private async request<T>(
37
- endpoint: string,
38
- options: RequestInit = {}
39
- ): Promise<T> {
40
- const apiKey = this.getApiKey();
41
-
42
- const headers: Record<string, string> = {
43
- 'Content-Type': 'application/json',
44
- ...(options.headers as Record<string, string>),
45
- };
46
-
47
- if (apiKey && endpoint !== '/health') {
48
- headers['Authorization'] = `Bearer ${apiKey}`;
49
- }
50
-
51
- const response = await fetch(`${API_BASE}${endpoint}`, {
52
- ...options,
53
- headers,
54
- });
55
-
56
- if (!response.ok) {
57
- const error: ApiError = await response.json();
58
- throw new Error(error.details || error.error);
59
- }
60
-
61
- return response.json();
62
- }
63
-
64
- // Health
65
- async getHealth() {
66
- return this.request<{ status: string; uptime: number; timestamp: string }>('/health');
67
- }
68
-
69
- // System
70
- async getSystemStatus() {
71
- return this.request<SystemStatus>('/api/status');
72
- }
73
-
74
- // Servers
75
- async listServers() {
76
- return this.request<{ servers: Server[] }>('/api/servers');
77
- }
78
-
79
- async getServer(id: string) {
80
- return this.request<{ server: Server }>(`/api/servers/${id}`);
81
- }
82
-
83
- async createServer(data: CreateServerRequest) {
84
- return this.request<{ server: Server }>('/api/servers', {
85
- method: 'POST',
86
- body: JSON.stringify(data),
87
- });
88
- }
89
-
90
- async updateServer(id: string, data: UpdateServerRequest) {
91
- return this.request<{ server: Server }>(`/api/servers/${id}`, {
92
- method: 'PATCH',
93
- body: JSON.stringify(data),
94
- });
95
- }
96
-
97
- async deleteServer(id: string) {
98
- return this.request<{ success: boolean }>(`/api/servers/${id}`, {
99
- method: 'DELETE',
100
- });
101
- }
102
-
103
- async startServer(id: string) {
104
- return this.request<{ server: Server }>(`/api/servers/${id}/start`, {
105
- method: 'POST',
106
- });
107
- }
108
-
109
- async stopServer(id: string) {
110
- return this.request<{ server: Server }>(`/api/servers/${id}/stop`, {
111
- method: 'POST',
112
- });
113
- }
114
-
115
- async restartServer(id: string) {
116
- return this.request<{ server: Server }>(`/api/servers/${id}/restart`, {
117
- method: 'POST',
118
- });
119
- }
120
-
121
- async getServerLogs(id: string, type: 'stdout' | 'stderr' | 'both' = 'both', lines = 100) {
122
- return this.request<{ stdout: string; stderr: string }>(
123
- `/api/servers/${id}/logs?type=${type}&lines=${lines}`
124
- );
125
- }
126
-
127
- // Models
128
- async listModels() {
129
- return this.request<{ models: Model[] }>('/api/models');
130
- }
131
-
132
- async getModel(name: string) {
133
- return this.request<{ model: Model }>(`/api/models/${encodeURIComponent(name)}`);
134
- }
135
-
136
- async searchModels(query: string, limit = 20) {
137
- return this.request<{ results: HFModelResult[] }>(
138
- `/api/models/search?q=${encodeURIComponent(query)}&limit=${limit}`
139
- );
140
- }
141
-
142
- async getModelFiles(repoId: string) {
143
- return this.request<{ repoId: string; files: string[] }>(
144
- `/api/models/${encodeURIComponent(repoId)}/files`
145
- );
146
- }
147
-
148
- async downloadModel(repo: string, filename: string) {
149
- return this.request<{ jobId: string; status: string }>(
150
- '/api/models/download',
151
- {
152
- method: 'POST',
153
- body: JSON.stringify({ repo, filename }),
154
- }
155
- );
156
- }
157
-
158
- async deleteModel(name: string, cascade = false) {
159
- return this.request<{ success: boolean; deletedServers?: string[] }>(
160
- `/api/models/${encodeURIComponent(name)}?cascade=${cascade}`,
161
- {
162
- method: 'DELETE',
163
- }
164
- );
165
- }
166
-
167
- // Download Jobs
168
- async listDownloadJobs() {
169
- return this.request<{ jobs: DownloadJob[] }>('/api/jobs');
170
- }
171
-
172
- async getDownloadJob(jobId: string) {
173
- return this.request<{ job: DownloadJob }>(`/api/jobs/${jobId}`);
174
- }
175
-
176
- async cancelDownloadJob(jobId: string) {
177
- return this.request<{ success: boolean; message: string }>(
178
- `/api/jobs/${jobId}`,
179
- { method: 'DELETE' }
180
- );
181
- }
182
-
183
- // Router
184
- async getRouter() {
185
- return this.request<RouterInfo>('/api/router');
186
- }
187
-
188
- async startRouter() {
189
- return this.request<{ success: boolean; status: string; pid: number | null }>(
190
- '/api/router/start',
191
- { method: 'POST' }
192
- );
193
- }
194
-
195
- async stopRouter() {
196
- return this.request<{ success: boolean; status: string }>(
197
- '/api/router/stop',
198
- { method: 'POST' }
199
- );
200
- }
201
-
202
- async restartRouter() {
203
- return this.request<{ success: boolean; status: string; pid: number | null }>(
204
- '/api/router/restart',
205
- { method: 'POST' }
206
- );
207
- }
208
-
209
- async getRouterLogs(type: 'stdout' | 'stderr' | 'both' = 'both', lines = 100) {
210
- return this.request<{ stdout: string; stderr: string }>(
211
- `/api/router/logs?type=${type}&lines=${lines}`
212
- );
213
- }
214
-
215
- async updateRouter(data: UpdateRouterRequest) {
216
- return this.request<{ success: boolean; needsRestart: boolean; config: any }>(
217
- '/api/router',
218
- {
219
- method: 'PATCH',
220
- body: JSON.stringify(data),
221
- }
222
- );
223
- }
224
- }
225
-
226
- export const api = new ApiClient();
package/web/src/main.tsx DELETED
@@ -1,10 +0,0 @@
1
- import { StrictMode } from 'react'
2
- import { createRoot } from 'react-dom/client'
3
- import './index.css'
4
- import App from './App.tsx'
5
-
6
- createRoot(document.getElementById('root')!).render(
7
- <StrictMode>
8
- <App />
9
- </StrictMode>,
10
- )
@@ -1,103 +0,0 @@
1
- import { useSystemStatus, useServers } from '../hooks/useApi';
2
- import { Server, Box, Activity, Clock } from 'lucide-react';
3
-
4
- export function Dashboard() {
5
- const { data: status } = useSystemStatus();
6
- const { data: serversData } = useServers();
7
-
8
- const runningServers = serversData?.servers.filter(s => s.status === 'running') || [];
9
-
10
- return (
11
- <div className="max-w-7xl mx-auto px-6 py-8">
12
- <div className="mb-8">
13
- <h1 className="text-3xl font-semibold text-gray-900 dark:text-white mb-2">
14
- Dashboard
15
- </h1>
16
- <p className="text-gray-600 dark:text-gray-400">
17
- Manage your local llama.cpp servers
18
- </p>
19
- </div>
20
-
21
- <div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
22
- <div className="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-lg p-6">
23
- <div className="flex items-center justify-between mb-2">
24
- <span className="text-sm text-gray-600 dark:text-gray-400">Total Servers</span>
25
- <Server className="w-4 h-4 text-gray-400" />
26
- </div>
27
- <div className="text-3xl font-semibold text-gray-900 dark:text-white">
28
- {status?.servers.total || 0}
29
- </div>
30
- </div>
31
-
32
- <div className="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-lg p-6">
33
- <div className="flex items-center justify-between mb-2">
34
- <span className="text-sm text-gray-600 dark:text-gray-400">Running</span>
35
- <Activity className="w-4 h-4 text-green-500" />
36
- </div>
37
- <div className="text-3xl font-semibold text-green-600 dark:text-green-500">
38
- {status?.servers.running || 0}
39
- </div>
40
- </div>
41
-
42
- <div className="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-lg p-6">
43
- <div className="flex items-center justify-between mb-2">
44
- <span className="text-sm text-gray-600 dark:text-gray-400">Stopped</span>
45
- <Clock className="w-4 h-4 text-gray-400" />
46
- </div>
47
- <div className="text-3xl font-semibold text-gray-900 dark:text-white">
48
- {status?.servers.stopped || 0}
49
- </div>
50
- </div>
51
-
52
- <div className="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-lg p-6">
53
- <div className="flex items-center justify-between mb-2">
54
- <span className="text-sm text-gray-600 dark:text-gray-400">Models</span>
55
- <Box className="w-4 h-4 text-gray-400" />
56
- </div>
57
- <div className="text-3xl font-semibold text-gray-900 dark:text-white">
58
- {status?.models.total || 0}
59
- </div>
60
- </div>
61
- </div>
62
-
63
- <div className="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-lg">
64
- <div className="p-6 border-b border-gray-200 dark:border-gray-800">
65
- <h2 className="text-lg font-semibold text-gray-900 dark:text-white">
66
- Running Servers
67
- </h2>
68
- </div>
69
- <div className="divide-y divide-gray-200 dark:divide-gray-800">
70
- {runningServers.length === 0 ? (
71
- <div className="p-8 text-center">
72
- <p className="text-gray-500 dark:text-gray-400">No servers running</p>
73
- </div>
74
- ) : (
75
- runningServers.map((server) => (
76
- <div key={server.id} className="p-6 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors">
77
- <div className="flex items-center justify-between">
78
- <div>
79
- <h3 className="text-sm font-medium text-gray-900 dark:text-white mb-1">
80
- {server.modelName}
81
- </h3>
82
- <div className="flex items-center space-x-4 text-sm text-gray-600 dark:text-gray-400">
83
- <span>Port {server.port}</span>
84
- <span>•</span>
85
- <span>{server.threads} threads</span>
86
- <span>•</span>
87
- <span>Context {server.ctxSize}</span>
88
- </div>
89
- </div>
90
- <div className="flex items-center space-x-2">
91
- <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400">
92
- Running
93
- </span>
94
- </div>
95
- </div>
96
- </div>
97
- ))
98
- )}
99
- </div>
100
- </div>
101
- </div>
102
- );
103
- }