@edgible-team/cli 1.0.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 (102) hide show
  1. package/LICENSE +136 -0
  2. package/README.md +450 -0
  3. package/dist/client/api-client.js +1057 -0
  4. package/dist/client/index.js +21 -0
  5. package/dist/commands/agent.js +1280 -0
  6. package/dist/commands/ai.js +608 -0
  7. package/dist/commands/application.js +885 -0
  8. package/dist/commands/auth.js +570 -0
  9. package/dist/commands/base/BaseCommand.js +93 -0
  10. package/dist/commands/base/CommandHandler.js +7 -0
  11. package/dist/commands/base/command-wrapper.js +58 -0
  12. package/dist/commands/base/middleware.js +77 -0
  13. package/dist/commands/config.js +116 -0
  14. package/dist/commands/connectivity.js +59 -0
  15. package/dist/commands/debug.js +98 -0
  16. package/dist/commands/discover.js +144 -0
  17. package/dist/commands/examples/migrated-command-example.js +180 -0
  18. package/dist/commands/gateway.js +494 -0
  19. package/dist/commands/managedGateway.js +787 -0
  20. package/dist/commands/utils/config-validator.js +76 -0
  21. package/dist/commands/utils/gateway-prompt.js +79 -0
  22. package/dist/commands/utils/input-parser.js +120 -0
  23. package/dist/commands/utils/output-formatter.js +109 -0
  24. package/dist/config/app-config.js +99 -0
  25. package/dist/detection/SystemCapabilityDetector.js +1244 -0
  26. package/dist/detection/ToolDetector.js +305 -0
  27. package/dist/detection/WorkloadDetector.js +314 -0
  28. package/dist/di/bindings.js +99 -0
  29. package/dist/di/container.js +88 -0
  30. package/dist/di/types.js +32 -0
  31. package/dist/index.js +52 -0
  32. package/dist/interfaces/IDaemonManager.js +3 -0
  33. package/dist/repositories/config-repository.js +62 -0
  34. package/dist/repositories/gateway-repository.js +35 -0
  35. package/dist/scripts/postinstall.js +101 -0
  36. package/dist/services/AgentStatusManager.js +299 -0
  37. package/dist/services/ConnectivityTester.js +271 -0
  38. package/dist/services/DependencyInstaller.js +475 -0
  39. package/dist/services/LocalAgentManager.js +2216 -0
  40. package/dist/services/application/ApplicationService.js +299 -0
  41. package/dist/services/auth/AuthService.js +214 -0
  42. package/dist/services/aws.js +644 -0
  43. package/dist/services/daemon/DaemonManagerFactory.js +65 -0
  44. package/dist/services/daemon/DockerDaemonManager.js +395 -0
  45. package/dist/services/daemon/LaunchdDaemonManager.js +257 -0
  46. package/dist/services/daemon/PodmanDaemonManager.js +369 -0
  47. package/dist/services/daemon/SystemdDaemonManager.js +221 -0
  48. package/dist/services/daemon/WindowsServiceDaemonManager.js +210 -0
  49. package/dist/services/daemon/index.js +16 -0
  50. package/dist/services/edgible.js +3060 -0
  51. package/dist/services/gateway/GatewayService.js +334 -0
  52. package/dist/state/config.js +146 -0
  53. package/dist/types/AgentConfig.js +5 -0
  54. package/dist/types/AgentStatus.js +5 -0
  55. package/dist/types/ApiClient.js +5 -0
  56. package/dist/types/ApiRequests.js +5 -0
  57. package/dist/types/ApiResponses.js +5 -0
  58. package/dist/types/Application.js +5 -0
  59. package/dist/types/CaddyJson.js +5 -0
  60. package/dist/types/UnifiedAgentStatus.js +56 -0
  61. package/dist/types/WireGuard.js +5 -0
  62. package/dist/types/Workload.js +5 -0
  63. package/dist/types/agent.js +5 -0
  64. package/dist/types/command-options.js +5 -0
  65. package/dist/types/connectivity.js +5 -0
  66. package/dist/types/errors.js +250 -0
  67. package/dist/types/gateway-types.js +5 -0
  68. package/dist/types/index.js +48 -0
  69. package/dist/types/models/ApplicationData.js +5 -0
  70. package/dist/types/models/CertificateData.js +5 -0
  71. package/dist/types/models/DeviceData.js +5 -0
  72. package/dist/types/models/DevicePoolData.js +5 -0
  73. package/dist/types/models/OrganizationData.js +5 -0
  74. package/dist/types/models/OrganizationInviteData.js +5 -0
  75. package/dist/types/models/ProviderConfiguration.js +5 -0
  76. package/dist/types/models/ResourceData.js +5 -0
  77. package/dist/types/models/ServiceResourceData.js +5 -0
  78. package/dist/types/models/UserData.js +5 -0
  79. package/dist/types/route.js +5 -0
  80. package/dist/types/validation/schemas.js +218 -0
  81. package/dist/types/validation.js +5 -0
  82. package/dist/utils/FileIntegrityManager.js +256 -0
  83. package/dist/utils/PathMigration.js +219 -0
  84. package/dist/utils/PathResolver.js +235 -0
  85. package/dist/utils/PlatformDetector.js +277 -0
  86. package/dist/utils/console-logger.js +130 -0
  87. package/dist/utils/docker-compose-parser.js +179 -0
  88. package/dist/utils/errors.js +130 -0
  89. package/dist/utils/health-checker.js +155 -0
  90. package/dist/utils/json-logger.js +72 -0
  91. package/dist/utils/log-formatter.js +293 -0
  92. package/dist/utils/logger.js +59 -0
  93. package/dist/utils/network-utils.js +217 -0
  94. package/dist/utils/output.js +182 -0
  95. package/dist/utils/passwordValidation.js +91 -0
  96. package/dist/utils/progress.js +167 -0
  97. package/dist/utils/sudo-checker.js +22 -0
  98. package/dist/utils/urls.js +32 -0
  99. package/dist/utils/validation.js +31 -0
  100. package/dist/validation/schemas.js +175 -0
  101. package/dist/validation/validator.js +67 -0
  102. package/package.json +83 -0
@@ -0,0 +1,1244 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.SystemCapabilityDetector = void 0;
40
+ const child_process_1 = require("child_process");
41
+ const os = __importStar(require("os"));
42
+ const chalk_1 = __importDefault(require("chalk"));
43
+ class SystemCapabilityDetector {
44
+ /**
45
+ * Detect all system capabilities
46
+ */
47
+ static async detectCapabilities() {
48
+ const cpu = this.detectCpu();
49
+ const memory = this.detectMemory();
50
+ const gpu = await this.detectGpu();
51
+ const platform = os.platform();
52
+ const gpuDriverSupport = await this.detectGpuDriverSupport(gpu);
53
+ const recommendedModels = this.recommendLLMModels(cpu, memory, gpu);
54
+ const hasEnoughResources = recommendedModels.some((m) => m.suitability === 'excellent' || m.suitability === 'good');
55
+ return {
56
+ cpu,
57
+ memory,
58
+ gpu,
59
+ platform,
60
+ hasEnoughResources,
61
+ recommendedModels,
62
+ gpuDriverSupport,
63
+ };
64
+ }
65
+ /**
66
+ * Detect CPU information
67
+ */
68
+ static detectCpu() {
69
+ const cpus = os.cpus();
70
+ const firstCpu = cpus[0];
71
+ const model = firstCpu?.model || 'Unknown';
72
+ const cores = cpus.length;
73
+ const threads = cores; // os.cpus() returns logical cores
74
+ const architecture = os.arch();
75
+ const speed = firstCpu?.speed ? firstCpu.speed / 1000 : 0; // Convert MHz to GHz
76
+ return {
77
+ model,
78
+ cores,
79
+ threads,
80
+ architecture,
81
+ speed,
82
+ };
83
+ }
84
+ /**
85
+ * Detect memory information
86
+ */
87
+ static detectMemory() {
88
+ const total = os.totalmem();
89
+ const free = os.freemem();
90
+ const used = total - free;
91
+ return {
92
+ total,
93
+ free,
94
+ used,
95
+ totalGB: this.bytesToGB(total),
96
+ freeGB: this.bytesToGB(free),
97
+ usedGB: this.bytesToGB(used),
98
+ };
99
+ }
100
+ /**
101
+ * Detect GPU information
102
+ */
103
+ static async detectGpu() {
104
+ try {
105
+ // Try NVIDIA GPU first (most common for ML)
106
+ if (this.hasCommand('nvidia-smi')) {
107
+ return await this.detectNvidiaGpu();
108
+ }
109
+ // Try macOS GPU detection (Apple Silicon or Intel)
110
+ if (process.platform === 'darwin') {
111
+ return await this.detectMacOSGpu();
112
+ }
113
+ // Try AMD GPU detection (multiple methods)
114
+ if (process.platform === 'linux') {
115
+ // Try ROCm first (for data center GPUs)
116
+ if (this.hasCommand('rocm-smi')) {
117
+ const rocmGpu = await this.detectAmdGpu();
118
+ if (rocmGpu)
119
+ return rocmGpu;
120
+ }
121
+ // Try Linux AMD GPU detection (consumer GPUs)
122
+ const linuxAmdGpu = await this.detectAmdGpuLinux();
123
+ if (linuxAmdGpu)
124
+ return linuxAmdGpu;
125
+ // Fall back to generic PCI detection
126
+ return await this.detectGpuViaLspci();
127
+ }
128
+ // Try Windows GPU detection (includes AMD VRAM)
129
+ if (process.platform === 'win32') {
130
+ return await this.detectGpuViaWmic();
131
+ }
132
+ return null;
133
+ }
134
+ catch (error) {
135
+ return null;
136
+ }
137
+ }
138
+ /**
139
+ * Detect NVIDIA GPU using nvidia-smi
140
+ */
141
+ static async detectNvidiaGpu() {
142
+ try {
143
+ const output = (0, child_process_1.execSync)('nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv,noheader,nounits', {
144
+ encoding: 'utf8',
145
+ timeout: 5000,
146
+ });
147
+ const lines = output.trim().split('\n');
148
+ if (lines.length === 0) {
149
+ return null;
150
+ }
151
+ // Get first GPU
152
+ const line = lines[0].trim();
153
+ const parts = line.split(',');
154
+ if (parts.length < 2) {
155
+ return null;
156
+ }
157
+ const name = parts[0].trim();
158
+ const vramMB = parseInt(parts[1].trim(), 10);
159
+ const driverVersion = parts[2]?.trim();
160
+ return {
161
+ name,
162
+ vendor: 'NVIDIA',
163
+ vramGB: Math.round((vramMB / 1024) * 10) / 10, // Round to 1 decimal
164
+ driverVersion,
165
+ available: true,
166
+ };
167
+ }
168
+ catch (error) {
169
+ return null;
170
+ }
171
+ }
172
+ /**
173
+ * Detect AMD GPU using rocm-smi (ROCm drivers, typically data center GPUs)
174
+ */
175
+ static async detectAmdGpu() {
176
+ try {
177
+ const output = (0, child_process_1.execSync)('rocm-smi --showproductname --showmeminfo vram --showdriverversion', {
178
+ encoding: 'utf8',
179
+ timeout: 5000,
180
+ });
181
+ // Parse output (format varies)
182
+ const nameMatch = output.match(/Card series:\s*(.+)/i);
183
+ const vramMatch = output.match(/vram.*?(\d+)\s*MB/i);
184
+ const driverMatch = output.match(/Driver version:\s*(.+)/i);
185
+ return {
186
+ name: nameMatch?.[1] || 'AMD GPU',
187
+ vendor: 'AMD',
188
+ vramGB: vramMatch ? Math.round((parseInt(vramMatch[1], 10) / 1024) * 10) / 10 : undefined,
189
+ driverVersion: driverMatch?.[1],
190
+ available: true,
191
+ };
192
+ }
193
+ catch (error) {
194
+ return null;
195
+ }
196
+ }
197
+ /**
198
+ * Detect AMD GPU on Linux using amdgpu driver (consumer GPUs)
199
+ */
200
+ static async detectAmdGpuLinux() {
201
+ try {
202
+ // First check if AMD GPU exists via lspci
203
+ const lspciOutput = (0, child_process_1.execSync)('lspci | grep -i "amd\\|ati\\|radeon"', {
204
+ encoding: 'utf8',
205
+ timeout: 5000,
206
+ });
207
+ if (!lspciOutput.trim()) {
208
+ return null;
209
+ }
210
+ // Extract GPU name from lspci
211
+ const lines = lspciOutput.trim().split('\n');
212
+ const firstLine = lines[0];
213
+ const gpuName = firstLine.split(':')[2]?.trim() || 'AMD GPU';
214
+ // Try to get VRAM from /sys/class/drm/
215
+ // Look for card* devices
216
+ const drmCards = (0, child_process_1.execSync)('ls -d /sys/class/drm/card* 2>/dev/null | head -1', {
217
+ encoding: 'utf8',
218
+ timeout: 2000,
219
+ }).trim();
220
+ if (drmCards) {
221
+ // Try to read VRAM size from device
222
+ try {
223
+ // Check if it's an AMD GPU by looking at device path
224
+ const devicePath = (0, child_process_1.execSync)(`readlink -f ${drmCards}`, {
225
+ encoding: 'utf8',
226
+ timeout: 2000,
227
+ }).trim();
228
+ if (devicePath.includes('amdgpu')) {
229
+ // Try to get VRAM from mem_info_vram_total (in bytes)
230
+ try {
231
+ const vramBytes = (0, child_process_1.execSync)(`cat ${drmCards}/device/mem_info_vram_total 2>/dev/null`, {
232
+ encoding: 'utf8',
233
+ timeout: 2000,
234
+ }).trim();
235
+ if (vramBytes && !isNaN(parseInt(vramBytes, 10))) {
236
+ const vramGB = Math.round((parseInt(vramBytes, 10) / (1024 * 1024 * 1024)) * 10) / 10;
237
+ return {
238
+ name: gpuName,
239
+ vendor: 'AMD',
240
+ vramGB,
241
+ available: true,
242
+ };
243
+ }
244
+ }
245
+ catch {
246
+ // Try alternative method
247
+ }
248
+ // Alternative: try to get from radeontop or other tools
249
+ if (this.hasCommand('radeontop')) {
250
+ try {
251
+ // radeontop can show VRAM but requires running, so skip for now
252
+ }
253
+ catch {
254
+ // Ignore
255
+ }
256
+ }
257
+ // Try to get from lspci -v (may show memory size)
258
+ try {
259
+ const lspciVOutput = (0, child_process_1.execSync)(`lspci -v -s ${firstLine.split(' ')[0]}`, {
260
+ encoding: 'utf8',
261
+ timeout: 3000,
262
+ });
263
+ // Look for memory size in output (format: Memory at ... [size=8G])
264
+ const memoryMatch = lspciVOutput.match(/Memory.*?\[size=(\d+)([KMGT]?)\]/i);
265
+ if (memoryMatch) {
266
+ let size = parseInt(memoryMatch[1], 10);
267
+ const unit = memoryMatch[2].toUpperCase();
268
+ if (unit === 'G') {
269
+ // Already in GB
270
+ }
271
+ else if (unit === 'M') {
272
+ size = size / 1024;
273
+ }
274
+ else if (unit === 'T') {
275
+ size = size * 1024;
276
+ }
277
+ // K is too small, ignore
278
+ if (size >= 1) {
279
+ // Likely VRAM if it's a reasonable size
280
+ return {
281
+ name: gpuName,
282
+ vendor: 'AMD',
283
+ vramGB: Math.round(size * 10) / 10,
284
+ available: true,
285
+ };
286
+ }
287
+ }
288
+ }
289
+ catch {
290
+ // Ignore
291
+ }
292
+ }
293
+ }
294
+ catch {
295
+ // Ignore errors
296
+ }
297
+ }
298
+ // If we found an AMD GPU but couldn't get VRAM, return basic info
299
+ return {
300
+ name: gpuName,
301
+ vendor: 'AMD',
302
+ available: true,
303
+ };
304
+ }
305
+ catch (error) {
306
+ return null;
307
+ }
308
+ }
309
+ /**
310
+ * Detect GPU via lspci (Linux)
311
+ */
312
+ static async detectGpuViaLspci() {
313
+ try {
314
+ const output = (0, child_process_1.execSync)('lspci | grep -i "vga\\|3d\\|display"', {
315
+ encoding: 'utf8',
316
+ timeout: 5000,
317
+ });
318
+ const lines = output.trim().split('\n');
319
+ if (lines.length === 0) {
320
+ return null;
321
+ }
322
+ const line = lines[0];
323
+ let vendor = 'Unknown';
324
+ let name = 'Unknown';
325
+ if (line.includes('NVIDIA')) {
326
+ vendor = 'NVIDIA';
327
+ name = line.split(':')[2]?.trim() || 'NVIDIA GPU';
328
+ }
329
+ else if (line.includes('AMD') || line.includes('ATI')) {
330
+ vendor = 'AMD';
331
+ name = line.split(':')[2]?.trim() || 'AMD GPU';
332
+ }
333
+ else if (line.includes('Intel')) {
334
+ vendor = 'Intel';
335
+ name = line.split(':')[2]?.trim() || 'Intel GPU';
336
+ }
337
+ else {
338
+ name = line.split(':')[2]?.trim() || 'Unknown GPU';
339
+ }
340
+ return {
341
+ name,
342
+ vendor,
343
+ available: true,
344
+ };
345
+ }
346
+ catch (error) {
347
+ return null;
348
+ }
349
+ }
350
+ /**
351
+ * Detect GPU on macOS (Apple Silicon or Intel)
352
+ */
353
+ static async detectMacOSGpu() {
354
+ try {
355
+ // Use system_profiler to get GPU information
356
+ const output = (0, child_process_1.execSync)('system_profiler SPDisplaysDataType', {
357
+ encoding: 'utf8',
358
+ timeout: 5000,
359
+ });
360
+ // Check if it's Apple Silicon
361
+ const isAppleSilicon = output.includes('Apple') && (output.includes('M1') ||
362
+ output.includes('M2') ||
363
+ output.includes('M3') ||
364
+ output.includes('M4') ||
365
+ output.includes('Apple GPU'));
366
+ // Extract GPU name - try multiple patterns
367
+ let name = 'Apple GPU';
368
+ const chipsetMatch = output.match(/Chipset Model:\s*(.+)/i);
369
+ const modelMatch = output.match(/Model:\s*(.+)/i);
370
+ const deviceMatch = output.match(/Device ID:\s*(.+)/i);
371
+ if (chipsetMatch) {
372
+ name = chipsetMatch[1].trim();
373
+ }
374
+ else if (modelMatch) {
375
+ name = modelMatch[1].trim();
376
+ }
377
+ // Extract VRAM - try multiple patterns for different GPU types
378
+ let vramGB;
379
+ // Pattern 1: "VRAM (Total): 4096 MB" or "VRAM: 4096 MB"
380
+ let vramMatch = output.match(/VRAM\s*\(?Total\)?:\s*(\d+)\s*MB/i) ||
381
+ output.match(/VRAM:\s*(\d+)\s*MB/i);
382
+ if (vramMatch) {
383
+ vramGB = Math.round((parseInt(vramMatch[1], 10) / 1024) * 10) / 10;
384
+ }
385
+ else {
386
+ // Pattern 2: "VRAM (Dynamic, Max): 4096 MB" - for AMD GPUs
387
+ vramMatch = output.match(/VRAM\s*\(Dynamic[^)]*\):\s*(\d+)\s*MB/i);
388
+ if (vramMatch) {
389
+ vramGB = Math.round((parseInt(vramMatch[1], 10) / 1024) * 10) / 10;
390
+ }
391
+ else {
392
+ // Pattern 3: Look for memory size in different formats
393
+ // Some AMD GPUs show: "Memory: 4096 MB" or "Total Memory: 4096 MB"
394
+ vramMatch = output.match(/(?:Total\s+)?Memory:\s*(\d+)\s*MB/i);
395
+ if (vramMatch) {
396
+ const memMB = parseInt(vramMatch[1], 10);
397
+ // Only consider it VRAM if it's a reasonable size (>= 512MB, <= 64GB)
398
+ if (memMB >= 512 && memMB <= 65536) {
399
+ vramGB = Math.round((memMB / 1024) * 10) / 10;
400
+ }
401
+ }
402
+ }
403
+ }
404
+ // For Apple Silicon, VRAM is shared with system RAM
405
+ if (isAppleSilicon && !vramGB) {
406
+ // Apple Silicon uses unified memory - we'll note this in the reasoning
407
+ // but don't set a specific VRAM value as it's shared and dynamically allocated
408
+ }
409
+ // Determine vendor
410
+ let vendor = 'Apple';
411
+ if (isAppleSilicon) {
412
+ // Extract chip model (M1, M2, M3, etc.)
413
+ const chipMatch = output.match(/(M[1-4]\s*(?:Pro|Max|Ultra)?)/i) ||
414
+ name.match(/(M[1-4]\s*(?:Pro|Max|Ultra)?)/i);
415
+ if (chipMatch) {
416
+ vendor = `Apple Silicon (${chipMatch[1]})`;
417
+ }
418
+ else {
419
+ vendor = 'Apple Silicon';
420
+ }
421
+ }
422
+ else if (output.includes('AMD') || name.includes('AMD') || name.includes('Radeon')) {
423
+ vendor = 'AMD';
424
+ // For AMD GPUs, try to extract more specific model info
425
+ const radeonMatch = output.match(/Radeon\s+(Pro\s+)?([A-Z0-9\s]+)/i) ||
426
+ name.match(/Radeon\s+(Pro\s+)?([A-Z0-9\s]+)/i);
427
+ if (radeonMatch && !name.includes('Radeon')) {
428
+ name = `AMD Radeon ${radeonMatch[2]?.trim() || name}`;
429
+ }
430
+ }
431
+ else if (output.includes('NVIDIA')) {
432
+ vendor = 'NVIDIA';
433
+ }
434
+ else if (output.includes('Intel')) {
435
+ vendor = 'Intel';
436
+ }
437
+ return {
438
+ name,
439
+ vendor,
440
+ vramGB,
441
+ available: true,
442
+ };
443
+ }
444
+ catch (error) {
445
+ // Fallback: try to detect Apple Silicon via uname
446
+ try {
447
+ const arch = (0, child_process_1.execSync)('uname -m', { encoding: 'utf8', timeout: 2000 }).trim();
448
+ if (arch === 'arm64') {
449
+ // Likely Apple Silicon
450
+ const chipInfo = (0, child_process_1.execSync)('sysctl -n machdep.cpu.brand_string', {
451
+ encoding: 'utf8',
452
+ timeout: 2000,
453
+ }).trim();
454
+ return {
455
+ name: 'Apple GPU',
456
+ vendor: chipInfo.includes('Apple') ? 'Apple Silicon' : 'Apple',
457
+ available: true,
458
+ };
459
+ }
460
+ }
461
+ catch {
462
+ // Ignore fallback errors
463
+ }
464
+ return null;
465
+ }
466
+ }
467
+ /**
468
+ * Detect GPU via WMIC (Windows) - includes VRAM for AMD and NVIDIA
469
+ */
470
+ static async detectGpuViaWmic() {
471
+ try {
472
+ // Get GPU name and adapter RAM (VRAM)
473
+ const output = (0, child_process_1.execSync)('wmic path win32_VideoController get name,AdapterRAM,DriverVersion', {
474
+ encoding: 'utf8',
475
+ timeout: 5000,
476
+ });
477
+ const lines = output.trim().split('\n').filter((line) => line.trim() && !line.includes('Name'));
478
+ if (lines.length === 0) {
479
+ return null;
480
+ }
481
+ // Parse the first GPU
482
+ const firstLine = lines[0].trim();
483
+ // WMIC output format can vary, try to parse it
484
+ // Format is typically: Name AdapterRAM DriverVersion
485
+ // Try to extract name (usually first field)
486
+ const parts = firstLine.split(/\s{2,}/); // Split on multiple spaces
487
+ let name = parts[0]?.trim() || firstLine;
488
+ let vramBytes;
489
+ let driverVersion;
490
+ // Try to get VRAM from AdapterRAM field
491
+ try {
492
+ const vramOutput = (0, child_process_1.execSync)(`wmic path win32_VideoController where "Name='${name.replace(/'/g, "''")}'" get AdapterRAM /value`, {
493
+ encoding: 'utf8',
494
+ timeout: 3000,
495
+ });
496
+ const vramMatch = vramOutput.match(/AdapterRAM=(\d+)/);
497
+ if (vramMatch) {
498
+ vramBytes = parseInt(vramMatch[1], 10);
499
+ }
500
+ }
501
+ catch {
502
+ // Try alternative: get all info in one query
503
+ try {
504
+ const fullOutput = (0, child_process_1.execSync)(`wmic path win32_VideoController where "Name='${name.replace(/'/g, "''")}'" get AdapterRAM,DriverVersion /format:list`, {
505
+ encoding: 'utf8',
506
+ timeout: 3000,
507
+ });
508
+ const vramMatch = fullOutput.match(/AdapterRAM=(\d+)/);
509
+ const driverMatch = fullOutput.match(/DriverVersion=(.+)/);
510
+ if (vramMatch) {
511
+ vramBytes = parseInt(vramMatch[1], 10);
512
+ }
513
+ if (driverMatch) {
514
+ driverVersion = driverMatch[1].trim();
515
+ }
516
+ }
517
+ catch {
518
+ // Ignore
519
+ }
520
+ }
521
+ let vendor = 'Unknown';
522
+ if (name.includes('NVIDIA')) {
523
+ vendor = 'NVIDIA';
524
+ }
525
+ else if (name.includes('AMD') || name.includes('Radeon')) {
526
+ vendor = 'AMD';
527
+ }
528
+ else if (name.includes('Intel')) {
529
+ vendor = 'Intel';
530
+ }
531
+ const vramGB = vramBytes ? Math.round((vramBytes / (1024 * 1024 * 1024)) * 10) / 10 : undefined;
532
+ return {
533
+ name,
534
+ vendor,
535
+ vramGB,
536
+ driverVersion,
537
+ available: true,
538
+ };
539
+ }
540
+ catch (error) {
541
+ return null;
542
+ }
543
+ }
544
+ /**
545
+ * Detect GPU driver support and Docker GPU runtime availability
546
+ */
547
+ static async detectGpuDriverSupport(gpu) {
548
+ const support = {
549
+ cuda: {
550
+ available: false,
551
+ toolkitAvailable: false,
552
+ },
553
+ rocm: {
554
+ available: false,
555
+ },
556
+ metal: {
557
+ available: false,
558
+ },
559
+ dockerGpuSupport: {
560
+ available: false,
561
+ },
562
+ ollamaGpuReady: false,
563
+ };
564
+ // Detect CUDA (NVIDIA)
565
+ if (this.hasCommand('nvidia-smi')) {
566
+ try {
567
+ const nvidiaOutput = (0, child_process_1.execSync)('nvidia-smi --query-gpu=driver_version,cuda_version --format=csv,noheader', {
568
+ encoding: 'utf8',
569
+ timeout: 3000,
570
+ });
571
+ const lines = nvidiaOutput.trim().split('\n');
572
+ if (lines.length > 0) {
573
+ const parts = lines[0].split(',');
574
+ support.cuda.available = true;
575
+ if (parts[1]) {
576
+ support.cuda.version = parts[1].trim();
577
+ }
578
+ }
579
+ }
580
+ catch {
581
+ // nvidia-smi available but query failed
582
+ }
583
+ // Check for CUDA toolkit
584
+ if (this.hasCommand('nvcc')) {
585
+ try {
586
+ const nvccOutput = (0, child_process_1.execSync)('nvcc --version', {
587
+ encoding: 'utf8',
588
+ timeout: 2000,
589
+ });
590
+ const versionMatch = nvccOutput.match(/release\s+(\d+\.\d+)/i);
591
+ if (versionMatch) {
592
+ support.cuda.toolkitAvailable = true;
593
+ if (!support.cuda.version) {
594
+ support.cuda.version = versionMatch[1];
595
+ }
596
+ }
597
+ }
598
+ catch {
599
+ // nvcc available but version check failed
600
+ }
601
+ }
602
+ }
603
+ // Detect ROCm (AMD)
604
+ if (this.hasCommand('rocm-smi')) {
605
+ try {
606
+ const rocmOutput = (0, child_process_1.execSync)('rocm-smi --version', {
607
+ encoding: 'utf8',
608
+ timeout: 3000,
609
+ });
610
+ support.rocm.available = true;
611
+ const versionMatch = rocmOutput.match(/version\s+(\d+\.\d+\.\d+)/i);
612
+ if (versionMatch) {
613
+ support.rocm.version = versionMatch[1];
614
+ }
615
+ }
616
+ catch {
617
+ // rocm-smi available but version check failed
618
+ }
619
+ }
620
+ // Detect Metal (macOS)
621
+ if (process.platform === 'darwin') {
622
+ try {
623
+ // Check if Metal is available (it's built into macOS)
624
+ const metalCheck = (0, child_process_1.execSync)('system_profiler SPDisplaysDataType | grep -i "metal"', {
625
+ encoding: 'utf8',
626
+ timeout: 2000,
627
+ });
628
+ support.metal.available = true;
629
+ }
630
+ catch {
631
+ // Try alternative check
632
+ try {
633
+ // Metal is available on all modern macOS systems
634
+ const osVersion = (0, child_process_1.execSync)('sw_vers -productVersion', {
635
+ encoding: 'utf8',
636
+ timeout: 2000,
637
+ });
638
+ const majorVersion = parseInt(osVersion.split('.')[0], 10);
639
+ // Metal has been available since macOS 10.11
640
+ support.metal.available = majorVersion >= 10;
641
+ }
642
+ catch {
643
+ support.metal.available = false;
644
+ }
645
+ }
646
+ }
647
+ // Detect Docker GPU support
648
+ if (this.hasCommand('docker')) {
649
+ try {
650
+ // Check for NVIDIA Container Runtime
651
+ const dockerInfo = (0, child_process_1.execSync)('docker info 2>/dev/null', {
652
+ encoding: 'utf8',
653
+ timeout: 3000,
654
+ });
655
+ if (dockerInfo.includes('nvidia') || dockerInfo.includes('gpu')) {
656
+ support.dockerGpuSupport.available = true;
657
+ }
658
+ // Check for nvidia-container-runtime
659
+ if (this.hasCommand('nvidia-container-runtime')) {
660
+ support.dockerGpuSupport.nvidiaContainerRuntime = true;
661
+ support.dockerGpuSupport.runtime = 'nvidia';
662
+ support.dockerGpuSupport.available = true;
663
+ }
664
+ // Check for nvidia-docker (legacy)
665
+ if (this.hasCommand('nvidia-docker')) {
666
+ support.dockerGpuSupport.nvidiaDocker = true;
667
+ support.dockerGpuSupport.runtime = 'nvidia';
668
+ support.dockerGpuSupport.available = true;
669
+ }
670
+ // Check Docker daemon configuration for GPU runtimes
671
+ try {
672
+ const dockerVersion = (0, child_process_1.execSync)('docker version --format "{{.Server.Version}}"', {
673
+ encoding: 'utf8',
674
+ timeout: 2000,
675
+ });
676
+ // Try to test GPU access in Docker
677
+ if (support.cuda.available) {
678
+ try {
679
+ // Check if nvidia runtime is available
680
+ const runtimes = (0, child_process_1.execSync)('docker info --format "{{.Runtimes}}"', {
681
+ encoding: 'utf8',
682
+ timeout: 2000,
683
+ });
684
+ if (runtimes.includes('nvidia') || runtimes.includes('gpu')) {
685
+ support.dockerGpuSupport.available = true;
686
+ support.dockerGpuSupport.runtime = 'nvidia';
687
+ }
688
+ }
689
+ catch {
690
+ // Ignore
691
+ }
692
+ }
693
+ if (support.rocm.available) {
694
+ try {
695
+ const runtimes = (0, child_process_1.execSync)('docker info --format "{{.Runtimes}}"', {
696
+ encoding: 'utf8',
697
+ timeout: 2000,
698
+ });
699
+ if (runtimes.includes('rocm')) {
700
+ support.dockerGpuSupport.available = true;
701
+ support.dockerGpuSupport.runtime = 'rocm';
702
+ }
703
+ }
704
+ catch {
705
+ // Ignore
706
+ }
707
+ }
708
+ }
709
+ catch {
710
+ // Docker not running or can't query
711
+ }
712
+ }
713
+ catch {
714
+ // Docker not available or not running
715
+ }
716
+ }
717
+ // Determine Ollama GPU readiness
718
+ if (gpu) {
719
+ if (gpu.vendor === 'NVIDIA' || gpu.vendor.includes('NVIDIA')) {
720
+ if (support.cuda.available && support.dockerGpuSupport.available) {
721
+ support.ollamaGpuReady = true;
722
+ support.ollamaGpuReason = 'NVIDIA GPU with CUDA and Docker GPU support available';
723
+ }
724
+ else if (support.cuda.available) {
725
+ support.ollamaGpuReady = true;
726
+ support.ollamaGpuReason = 'NVIDIA GPU with CUDA available (Docker GPU support not detected)';
727
+ }
728
+ else {
729
+ support.ollamaGpuReady = false;
730
+ support.ollamaGpuReason = 'NVIDIA GPU detected but CUDA drivers not available';
731
+ }
732
+ }
733
+ else if (gpu.vendor === 'AMD' || gpu.vendor.includes('AMD')) {
734
+ if (support.rocm.available && support.dockerGpuSupport.available) {
735
+ support.ollamaGpuReady = true;
736
+ support.ollamaGpuReason = 'AMD GPU with ROCm and Docker GPU support available';
737
+ }
738
+ else if (support.rocm.available) {
739
+ support.ollamaGpuReady = true;
740
+ support.ollamaGpuReason = 'AMD GPU with ROCm available (Docker GPU support not detected)';
741
+ }
742
+ else {
743
+ support.ollamaGpuReady = false;
744
+ support.ollamaGpuReason = 'AMD GPU detected but ROCm drivers not available';
745
+ }
746
+ }
747
+ else if (gpu.vendor.includes('Apple Silicon') || gpu.vendor.includes('Apple')) {
748
+ if (support.metal.available) {
749
+ support.ollamaGpuReady = true;
750
+ support.ollamaGpuReason = 'Apple Silicon GPU with Metal support available';
751
+ }
752
+ else {
753
+ support.ollamaGpuReady = false;
754
+ support.ollamaGpuReason = 'Apple Silicon GPU detected but Metal support not available';
755
+ }
756
+ }
757
+ else {
758
+ support.ollamaGpuReady = false;
759
+ support.ollamaGpuReason = `GPU detected (${gpu.vendor}) but no compatible drivers found`;
760
+ }
761
+ }
762
+ else {
763
+ support.ollamaGpuReady = false;
764
+ support.ollamaGpuReason = 'No GPU detected - Ollama will run in CPU-only mode';
765
+ }
766
+ return support;
767
+ }
768
+ /**
769
+ * Check if a command is available
770
+ */
771
+ static hasCommand(command) {
772
+ try {
773
+ (0, child_process_1.execSync)(`which ${command}`, { encoding: 'utf8', timeout: 1000, stdio: 'ignore' });
774
+ return true;
775
+ }
776
+ catch {
777
+ try {
778
+ // Windows fallback
779
+ (0, child_process_1.execSync)(`where ${command}`, { encoding: 'utf8', timeout: 1000, stdio: 'ignore' });
780
+ return true;
781
+ }
782
+ catch {
783
+ return false;
784
+ }
785
+ }
786
+ }
787
+ /**
788
+ * Recommend LLM models based on system capabilities
789
+ */
790
+ static recommendLLMModels(cpu, memory, gpu) {
791
+ const recommendations = [];
792
+ // Model definitions with requirements
793
+ const models = [
794
+ {
795
+ modelName: 'Llama 3.2 1B',
796
+ size: '1B',
797
+ parameters: '1 billion',
798
+ minRamGB: 2,
799
+ recommendedRamGB: 4,
800
+ minVramGB: undefined,
801
+ recommendedVramGB: undefined,
802
+ },
803
+ {
804
+ modelName: 'Llama 3.2 3B',
805
+ size: '3B',
806
+ parameters: '3 billion',
807
+ minRamGB: 4,
808
+ recommendedRamGB: 8,
809
+ minVramGB: 4,
810
+ recommendedVramGB: 6,
811
+ },
812
+ {
813
+ modelName: 'Llama 3.1 8B',
814
+ size: '8B',
815
+ parameters: '8 billion',
816
+ minRamGB: 8,
817
+ recommendedRamGB: 16,
818
+ minVramGB: 6,
819
+ recommendedVramGB: 8,
820
+ },
821
+ {
822
+ modelName: 'Llama 3.1 70B',
823
+ size: '70B',
824
+ parameters: '70 billion',
825
+ minRamGB: 40,
826
+ recommendedRamGB: 64,
827
+ minVramGB: 40,
828
+ recommendedVramGB: 48,
829
+ },
830
+ {
831
+ modelName: 'Mistral 7B',
832
+ size: '7B',
833
+ parameters: '7 billion',
834
+ minRamGB: 8,
835
+ recommendedRamGB: 16,
836
+ minVramGB: 6,
837
+ recommendedVramGB: 8,
838
+ },
839
+ {
840
+ modelName: 'Phi-3 Mini (3.8B)',
841
+ size: '3.8B',
842
+ parameters: '3.8 billion',
843
+ minRamGB: 4,
844
+ recommendedRamGB: 8,
845
+ minVramGB: 4,
846
+ recommendedVramGB: 6,
847
+ },
848
+ {
849
+ modelName: 'Qwen2.5 0.5B',
850
+ size: '0.5B',
851
+ parameters: '0.5 billion',
852
+ minRamGB: 2,
853
+ recommendedRamGB: 4,
854
+ minVramGB: undefined,
855
+ recommendedVramGB: undefined,
856
+ },
857
+ {
858
+ modelName: 'Qwen2.5 7B',
859
+ size: '7B',
860
+ parameters: '7 billion',
861
+ minRamGB: 8,
862
+ recommendedRamGB: 16,
863
+ minVramGB: 6,
864
+ recommendedVramGB: 8,
865
+ },
866
+ // DeepSeek-R1 models (https://ollama.com/library/deepseek-r1)
867
+ {
868
+ modelName: 'DeepSeek-R1 1.5B',
869
+ size: '1.5B',
870
+ parameters: '1.5 billion',
871
+ minRamGB: 2,
872
+ recommendedRamGB: 4,
873
+ minVramGB: undefined,
874
+ recommendedVramGB: undefined,
875
+ },
876
+ {
877
+ modelName: 'DeepSeek-R1 7B',
878
+ size: '7B',
879
+ parameters: '7 billion',
880
+ minRamGB: 8,
881
+ recommendedRamGB: 16,
882
+ minVramGB: 6,
883
+ recommendedVramGB: 8,
884
+ },
885
+ {
886
+ modelName: 'DeepSeek-R1 8B',
887
+ size: '8B',
888
+ parameters: '8 billion',
889
+ minRamGB: 8,
890
+ recommendedRamGB: 16,
891
+ minVramGB: 6,
892
+ recommendedVramGB: 8,
893
+ },
894
+ {
895
+ modelName: 'DeepSeek-R1 14B',
896
+ size: '14B',
897
+ parameters: '14 billion',
898
+ minRamGB: 16,
899
+ recommendedRamGB: 32,
900
+ minVramGB: 10,
901
+ recommendedVramGB: 14,
902
+ },
903
+ {
904
+ modelName: 'DeepSeek-R1 32B',
905
+ size: '32B',
906
+ parameters: '32 billion',
907
+ minRamGB: 32,
908
+ recommendedRamGB: 48,
909
+ minVramGB: 20,
910
+ recommendedVramGB: 32,
911
+ },
912
+ {
913
+ modelName: 'DeepSeek-R1 70B',
914
+ size: '70B',
915
+ parameters: '70 billion',
916
+ minRamGB: 40,
917
+ recommendedRamGB: 64,
918
+ minVramGB: 40,
919
+ recommendedVramGB: 48,
920
+ },
921
+ {
922
+ modelName: 'DeepSeek-R1 671B',
923
+ size: '671B',
924
+ parameters: '671 billion',
925
+ minRamGB: 400,
926
+ recommendedRamGB: 512,
927
+ minVramGB: 400,
928
+ recommendedVramGB: 512,
929
+ },
930
+ ];
931
+ for (const model of models) {
932
+ const suitability = this.calculateSuitability(model, cpu, memory, gpu);
933
+ const reasoning = this.generateReasoning(model, cpu, memory, gpu, suitability);
934
+ recommendations.push({
935
+ modelName: model.modelName,
936
+ size: model.size,
937
+ parameters: model.parameters,
938
+ minRamGB: model.minRamGB,
939
+ minVramGB: model.minVramGB,
940
+ recommendedRamGB: model.recommendedRamGB,
941
+ recommendedVramGB: model.recommendedVramGB,
942
+ suitability,
943
+ reasoning,
944
+ });
945
+ }
946
+ // Sort by suitability (excellent first, then good, etc.)
947
+ const suitabilityOrder = { excellent: 0, good: 1, marginal: 2, insufficient: 3 };
948
+ return recommendations.sort((a, b) => suitabilityOrder[a.suitability] - suitabilityOrder[b.suitability]);
949
+ }
950
+ /**
951
+ * Calculate suitability for a model
952
+ */
953
+ static calculateSuitability(model, cpu, memory, gpu) {
954
+ // Check RAM
955
+ const hasMinRam = memory.totalGB >= model.minRamGB;
956
+ const hasRecommendedRam = memory.totalGB >= model.recommendedRamGB;
957
+ if (!hasMinRam) {
958
+ return 'insufficient';
959
+ }
960
+ // Check if it's Apple Silicon (unified memory architecture)
961
+ const isAppleSilicon = gpu?.vendor?.includes('Apple Silicon') || false;
962
+ // Check VRAM if model requires it and GPU is available
963
+ if (model.minVramGB && gpu) {
964
+ if (isAppleSilicon) {
965
+ // Apple Silicon uses unified memory - total RAM is available for GPU
966
+ // We need RAM for both system and model, so check if total RAM covers both
967
+ const totalRamNeeded = model.minRamGB + model.minVramGB;
968
+ const recommendedRamNeeded = model.recommendedRamGB + (model.recommendedVramGB || 0);
969
+ const hasMinTotalRam = memory.totalGB >= totalRamNeeded;
970
+ const hasRecommendedTotalRam = memory.totalGB >= recommendedRamNeeded;
971
+ if (!hasMinTotalRam) {
972
+ return 'insufficient';
973
+ }
974
+ if (hasRecommendedTotalRam && cpu.cores >= 4) {
975
+ return 'excellent';
976
+ }
977
+ if (hasMinTotalRam && cpu.cores >= 2) {
978
+ return 'good';
979
+ }
980
+ return 'marginal';
981
+ }
982
+ else if (gpu.vramGB) {
983
+ // Traditional GPU with dedicated VRAM
984
+ const hasMinVram = gpu.vramGB >= model.minVramGB;
985
+ const hasRecommendedVram = model.recommendedVramGB
986
+ ? gpu.vramGB >= model.recommendedVramGB
987
+ : true;
988
+ if (!hasMinVram) {
989
+ return 'insufficient';
990
+ }
991
+ if (hasRecommendedRam && hasRecommendedVram) {
992
+ return 'excellent';
993
+ }
994
+ if (hasMinRam && hasMinVram) {
995
+ return 'good';
996
+ }
997
+ return 'marginal';
998
+ }
999
+ }
1000
+ // If no GPU requirement or no GPU available, check RAM and CPU
1001
+ if (hasRecommendedRam && cpu.cores >= 4) {
1002
+ return 'excellent';
1003
+ }
1004
+ if (hasMinRam && cpu.cores >= 2) {
1005
+ return 'good';
1006
+ }
1007
+ return 'marginal';
1008
+ }
1009
+ /**
1010
+ * Generate reasoning for recommendation
1011
+ */
1012
+ static generateReasoning(model, cpu, memory, gpu, suitability) {
1013
+ const reasons = [];
1014
+ if (suitability === 'insufficient') {
1015
+ const isAppleSilicon = gpu?.vendor?.includes('Apple Silicon') || false;
1016
+ if (memory.totalGB < model.minRamGB) {
1017
+ reasons.push(`Insufficient RAM: ${memory.totalGB.toFixed(1)}GB available, ${model.minRamGB}GB minimum required`);
1018
+ }
1019
+ if (model.minVramGB) {
1020
+ if (isAppleSilicon) {
1021
+ // Apple Silicon uses unified memory
1022
+ const totalRamNeeded = model.minRamGB + model.minVramGB;
1023
+ if (memory.totalGB < totalRamNeeded) {
1024
+ reasons.push(`Insufficient unified memory: ${memory.totalGB.toFixed(1)}GB available, ${totalRamNeeded}GB minimum required (RAM + VRAM)`);
1025
+ }
1026
+ }
1027
+ else if (!gpu || !gpu.vramGB || gpu.vramGB < model.minVramGB) {
1028
+ reasons.push(`Insufficient VRAM: ${gpu?.vramGB?.toFixed(1) || 'No GPU'}GB available, ${model.minVramGB}GB minimum required`);
1029
+ }
1030
+ }
1031
+ return reasons.join('. ');
1032
+ }
1033
+ const isAppleSilicon = gpu?.vendor?.includes('Apple Silicon') || false;
1034
+ if (isAppleSilicon && model.minVramGB) {
1035
+ // Apple Silicon uses unified memory
1036
+ const totalRamNeeded = model.minRamGB + model.minVramGB;
1037
+ const recommendedRamNeeded = model.recommendedRamGB + (model.recommendedVramGB || 0);
1038
+ if (memory.totalGB >= recommendedRamNeeded) {
1039
+ reasons.push(`Unified Memory: ${memory.totalGB.toFixed(1)}GB (recommended: ${recommendedRamNeeded}GB for RAM + VRAM)`);
1040
+ }
1041
+ else if (memory.totalGB >= totalRamNeeded) {
1042
+ reasons.push(`Unified Memory: ${memory.totalGB.toFixed(1)}GB (minimum: ${totalRamNeeded}GB for RAM + VRAM)`);
1043
+ }
1044
+ }
1045
+ else {
1046
+ if (memory.totalGB >= model.recommendedRamGB) {
1047
+ reasons.push(`RAM: ${memory.totalGB.toFixed(1)}GB (recommended: ${model.recommendedRamGB}GB)`);
1048
+ }
1049
+ else {
1050
+ reasons.push(`RAM: ${memory.totalGB.toFixed(1)}GB (minimum: ${model.minRamGB}GB)`);
1051
+ }
1052
+ if (model.recommendedVramGB && gpu?.vramGB) {
1053
+ if (gpu.vramGB >= model.recommendedVramGB) {
1054
+ reasons.push(`VRAM: ${gpu.vramGB.toFixed(1)}GB (recommended: ${model.recommendedVramGB}GB)`);
1055
+ }
1056
+ else if (gpu.vramGB >= (model.minVramGB || 0)) {
1057
+ reasons.push(`VRAM: ${gpu.vramGB.toFixed(1)}GB (minimum: ${model.minVramGB}GB)`);
1058
+ }
1059
+ }
1060
+ else if (model.minVramGB && !gpu) {
1061
+ reasons.push('No GPU detected (CPU-only mode possible but slower)');
1062
+ }
1063
+ }
1064
+ if (cpu.cores >= 4) {
1065
+ reasons.push(`CPU: ${cpu.cores} cores`);
1066
+ }
1067
+ return reasons.join(', ');
1068
+ }
1069
+ /**
1070
+ * Convert bytes to GB
1071
+ */
1072
+ static bytesToGB(bytes) {
1073
+ return Math.round((bytes / (1024 * 1024 * 1024)) * 10) / 10;
1074
+ }
1075
+ /**
1076
+ * Display system capabilities
1077
+ */
1078
+ static displayCapabilities(capabilities) {
1079
+ console.log(chalk_1.default.blue.bold('\n🖥️ System Capabilities:\n'));
1080
+ // CPU
1081
+ console.log(chalk_1.default.cyan.bold('CPU:'));
1082
+ console.log(chalk_1.default.white(` Model: ${capabilities.cpu.model}`));
1083
+ console.log(chalk_1.default.white(` Cores: ${capabilities.cpu.cores}`));
1084
+ console.log(chalk_1.default.white(` Architecture: ${capabilities.cpu.architecture}`));
1085
+ console.log(chalk_1.default.white(` Speed: ${capabilities.cpu.speed.toFixed(2)} GHz\n`));
1086
+ // Memory
1087
+ console.log(chalk_1.default.cyan.bold('Memory:'));
1088
+ console.log(chalk_1.default.white(` Total: ${capabilities.memory.totalGB.toFixed(1)} GB`));
1089
+ console.log(chalk_1.default.white(` Used: ${capabilities.memory.usedGB.toFixed(1)} GB`));
1090
+ console.log(chalk_1.default.white(` Free: ${capabilities.memory.freeGB.toFixed(1)} GB\n`));
1091
+ // GPU
1092
+ if (capabilities.gpu) {
1093
+ console.log(chalk_1.default.cyan.bold('GPU:'));
1094
+ console.log(chalk_1.default.white(` Name: ${capabilities.gpu.name}`));
1095
+ console.log(chalk_1.default.white(` Vendor: ${capabilities.gpu.vendor}`));
1096
+ if (capabilities.gpu.vramGB) {
1097
+ console.log(chalk_1.default.white(` VRAM: ${capabilities.gpu.vramGB.toFixed(1)} GB`));
1098
+ }
1099
+ if (capabilities.gpu.driverVersion) {
1100
+ console.log(chalk_1.default.white(` Driver: ${capabilities.gpu.driverVersion}`));
1101
+ }
1102
+ console.log('');
1103
+ }
1104
+ else {
1105
+ console.log(chalk_1.default.yellow('GPU: No dedicated GPU detected\n'));
1106
+ }
1107
+ // GPU Driver Support & Ollama Readiness
1108
+ console.log(chalk_1.default.cyan.bold('GPU Driver Support:\n'));
1109
+ const support = capabilities.gpuDriverSupport;
1110
+ // CUDA (NVIDIA)
1111
+ if (support.cuda.available) {
1112
+ console.log(chalk_1.default.green(' ✓ CUDA: Available'));
1113
+ if (support.cuda.version) {
1114
+ console.log(chalk_1.default.gray(` Version: ${support.cuda.version}`));
1115
+ }
1116
+ if (support.cuda.toolkitAvailable) {
1117
+ console.log(chalk_1.default.gray(' Toolkit: Available'));
1118
+ }
1119
+ }
1120
+ else if (capabilities.gpu?.vendor?.includes('NVIDIA')) {
1121
+ console.log(chalk_1.default.red(' ✗ CUDA: Not available'));
1122
+ console.log(chalk_1.default.gray(' Install NVIDIA drivers and CUDA toolkit'));
1123
+ }
1124
+ // ROCm (AMD)
1125
+ if (support.rocm.available) {
1126
+ console.log(chalk_1.default.green(' ✓ ROCm: Available'));
1127
+ if (support.rocm.version) {
1128
+ console.log(chalk_1.default.gray(` Version: ${support.rocm.version}`));
1129
+ }
1130
+ }
1131
+ else if (capabilities.gpu?.vendor?.includes('AMD')) {
1132
+ console.log(chalk_1.default.yellow(' ⚠ ROCm: Not available'));
1133
+ console.log(chalk_1.default.gray(' Install ROCm drivers for AMD GPU acceleration'));
1134
+ }
1135
+ // Metal (macOS)
1136
+ if (support.metal.available) {
1137
+ console.log(chalk_1.default.green(' ✓ Metal: Available (macOS)'));
1138
+ }
1139
+ // Docker GPU Support
1140
+ console.log('');
1141
+ if (support.dockerGpuSupport.available) {
1142
+ console.log(chalk_1.default.green(' ✓ Docker GPU Support: Available'));
1143
+ if (support.dockerGpuSupport.runtime) {
1144
+ console.log(chalk_1.default.gray(` Runtime: ${support.dockerGpuSupport.runtime}`));
1145
+ }
1146
+ if (support.dockerGpuSupport.nvidiaContainerRuntime) {
1147
+ console.log(chalk_1.default.gray(' NVIDIA Container Runtime: Installed'));
1148
+ }
1149
+ if (support.dockerGpuSupport.nvidiaDocker) {
1150
+ console.log(chalk_1.default.gray(' nvidia-docker: Installed (legacy)'));
1151
+ }
1152
+ }
1153
+ else {
1154
+ console.log(chalk_1.default.yellow(' ⚠ Docker GPU Support: Not detected'));
1155
+ if (capabilities.gpu) {
1156
+ console.log(chalk_1.default.gray(' Install nvidia-container-runtime or configure Docker for GPU access'));
1157
+ }
1158
+ else {
1159
+ console.log(chalk_1.default.gray(' No GPU detected - Docker GPU support not needed'));
1160
+ }
1161
+ }
1162
+ // Ollama GPU Readiness
1163
+ console.log('');
1164
+ if (support.ollamaGpuReady) {
1165
+ console.log(chalk_1.default.green.bold(' ✓ Ollama GPU Ready: Yes'));
1166
+ if (support.ollamaGpuReason) {
1167
+ console.log(chalk_1.default.gray(` ${support.ollamaGpuReason}`));
1168
+ }
1169
+ }
1170
+ else {
1171
+ console.log(chalk_1.default.yellow.bold(' ⚠ Ollama GPU Ready: No'));
1172
+ if (support.ollamaGpuReason) {
1173
+ console.log(chalk_1.default.gray(` ${support.ollamaGpuReason}`));
1174
+ }
1175
+ }
1176
+ console.log('');
1177
+ // Overall assessment
1178
+ if (capabilities.hasEnoughResources) {
1179
+ console.log(chalk_1.default.green.bold('✓ System has sufficient resources for local LLM\n'));
1180
+ }
1181
+ else {
1182
+ console.log(chalk_1.default.yellow.bold('⚠ System may have limited resources for local LLM\n'));
1183
+ }
1184
+ // Recommendations
1185
+ console.log(chalk_1.default.blue.bold('📊 Recommended LLM Models:\n'));
1186
+ const excellent = capabilities.recommendedModels.filter((m) => m.suitability === 'excellent');
1187
+ const good = capabilities.recommendedModels.filter((m) => m.suitability === 'good');
1188
+ const marginal = capabilities.recommendedModels.filter((m) => m.suitability === 'marginal');
1189
+ const insufficient = capabilities.recommendedModels.filter((m) => m.suitability === 'insufficient');
1190
+ if (excellent.length > 0) {
1191
+ console.log(chalk_1.default.green.bold('Excellent Fit:'));
1192
+ excellent.forEach((model) => {
1193
+ console.log(chalk_1.default.green(` ✓ ${model.modelName} (${model.size})`));
1194
+ console.log(chalk_1.default.gray(` ${model.reasoning}`));
1195
+ });
1196
+ console.log('');
1197
+ }
1198
+ if (good.length > 0) {
1199
+ console.log(chalk_1.default.cyan.bold('Good Fit:'));
1200
+ good.forEach((model) => {
1201
+ console.log(chalk_1.default.cyan(` • ${model.modelName} (${model.size})`));
1202
+ console.log(chalk_1.default.gray(` ${model.reasoning}`));
1203
+ });
1204
+ console.log('');
1205
+ }
1206
+ if (marginal.length > 0) {
1207
+ console.log(chalk_1.default.yellow.bold('Marginal (may work but not recommended):'));
1208
+ marginal.forEach((model) => {
1209
+ console.log(chalk_1.default.yellow(` ⚠ ${model.modelName} (${model.size})`));
1210
+ console.log(chalk_1.default.gray(` ${model.reasoning}`));
1211
+ });
1212
+ console.log('');
1213
+ }
1214
+ if (insufficient.length > 0) {
1215
+ console.log(chalk_1.default.red.bold('Insufficient Resources:'));
1216
+ insufficient.forEach((model) => {
1217
+ console.log(chalk_1.default.red(` ✗ ${model.modelName} (${model.size})`));
1218
+ console.log(chalk_1.default.gray(` ${model.reasoning}`));
1219
+ });
1220
+ console.log('');
1221
+ }
1222
+ }
1223
+ /**
1224
+ * Get summary of capabilities
1225
+ */
1226
+ static getCapabilitySummary(capabilities) {
1227
+ const parts = [];
1228
+ parts.push(`${capabilities.cpu.cores} cores`);
1229
+ parts.push(`${capabilities.memory.totalGB.toFixed(1)}GB RAM`);
1230
+ if (capabilities.gpu) {
1231
+ parts.push(`${capabilities.gpu.vendor} GPU`);
1232
+ if (capabilities.gpu.vramGB) {
1233
+ parts.push(`${capabilities.gpu.vramGB.toFixed(1)}GB VRAM`);
1234
+ }
1235
+ }
1236
+ else {
1237
+ parts.push('No GPU');
1238
+ }
1239
+ const suitableModels = capabilities.recommendedModels.filter((m) => m.suitability === 'excellent' || m.suitability === 'good').length;
1240
+ return `${parts.join(', ')} - ${suitableModels} suitable LLM models`;
1241
+ }
1242
+ }
1243
+ exports.SystemCapabilityDetector = SystemCapabilityDetector;
1244
+ //# sourceMappingURL=SystemCapabilityDetector.js.map