aiservermanager 0.1.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 (96) hide show
  1. package/README.md +31 -0
  2. package/backend/dist/di/container.js +80 -0
  3. package/backend/dist/di/container.js.map +1 -0
  4. package/backend/dist/di/types.js +18 -0
  5. package/backend/dist/di/types.js.map +1 -0
  6. package/backend/dist/files/validatePassword.ps1 +10 -0
  7. package/backend/dist/files/wmiGpuQuery.ps1 +15 -0
  8. package/backend/dist/helpers/ExecTools.js +101 -0
  9. package/backend/dist/helpers/ExecTools.js.map +1 -0
  10. package/backend/dist/index.js +51 -0
  11. package/backend/dist/index.js.map +1 -0
  12. package/backend/dist/middleware/authMiddleware.js +32 -0
  13. package/backend/dist/middleware/authMiddleware.js.map +1 -0
  14. package/backend/dist/models/GpuInfo.js +3 -0
  15. package/backend/dist/models/GpuInfo.js.map +1 -0
  16. package/backend/dist/models/ServiceConfig.js +46 -0
  17. package/backend/dist/models/ServiceConfig.js.map +1 -0
  18. package/backend/dist/models/ServiceStatus.js +3 -0
  19. package/backend/dist/models/ServiceStatus.js.map +1 -0
  20. package/backend/dist/routes/authRoutes.js +46 -0
  21. package/backend/dist/routes/authRoutes.js.map +1 -0
  22. package/backend/dist/routes/gpuRoutes.js +35 -0
  23. package/backend/dist/routes/gpuRoutes.js.map +1 -0
  24. package/backend/dist/routes/serviceRoutes.js +206 -0
  25. package/backend/dist/routes/serviceRoutes.js.map +1 -0
  26. package/backend/dist/routes/systemRoutes.js +64 -0
  27. package/backend/dist/routes/systemRoutes.js.map +1 -0
  28. package/backend/dist/services/auth/linuxPasswordVerifier.js +74 -0
  29. package/backend/dist/services/auth/linuxPasswordVerifier.js.map +1 -0
  30. package/backend/dist/services/auth/passwordVerifier.js +3 -0
  31. package/backend/dist/services/auth/passwordVerifier.js.map +1 -0
  32. package/backend/dist/services/auth/windowsPasswordVerifier.js +73 -0
  33. package/backend/dist/services/auth/windowsPasswordVerifier.js.map +1 -0
  34. package/backend/dist/services/authService.js +240 -0
  35. package/backend/dist/services/authService.js.map +1 -0
  36. package/backend/dist/services/configManager.js +96 -0
  37. package/backend/dist/services/configManager.js.map +1 -0
  38. package/backend/dist/services/controllers/systemctlController.js +287 -0
  39. package/backend/dist/services/controllers/systemctlController.js.map +1 -0
  40. package/backend/dist/services/controllers/windowsServiceController.js +201 -0
  41. package/backend/dist/services/controllers/windowsServiceController.js.map +1 -0
  42. package/backend/dist/services/detectors/amdLinuxDetector.js +127 -0
  43. package/backend/dist/services/detectors/amdLinuxDetector.js.map +1 -0
  44. package/backend/dist/services/detectors/gpuDetector.js +3 -0
  45. package/backend/dist/services/detectors/gpuDetector.js.map +1 -0
  46. package/backend/dist/services/detectors/nvidiaSmiDetector.js +67 -0
  47. package/backend/dist/services/detectors/nvidiaSmiDetector.js.map +1 -0
  48. package/backend/dist/services/detectors/wmiDetector.js +125 -0
  49. package/backend/dist/services/detectors/wmiDetector.js.map +1 -0
  50. package/backend/dist/services/enrichers/gpuEnricher.js +3 -0
  51. package/backend/dist/services/enrichers/gpuEnricher.js.map +1 -0
  52. package/backend/dist/services/enrichers/lspciEnricher.js +92 -0
  53. package/backend/dist/services/enrichers/lspciEnricher.js.map +1 -0
  54. package/backend/dist/services/enrichers/vulkanEnricher.js +57 -0
  55. package/backend/dist/services/enrichers/vulkanEnricher.js.map +1 -0
  56. package/backend/dist/services/gpuService.js +108 -0
  57. package/backend/dist/services/gpuService.js.map +1 -0
  58. package/backend/dist/services/helpers/gpuDedup.js +46 -0
  59. package/backend/dist/services/helpers/gpuDedup.js.map +1 -0
  60. package/backend/dist/services/helpers/gpuEngineNames.js +22 -0
  61. package/backend/dist/services/helpers/gpuEngineNames.js.map +1 -0
  62. package/backend/dist/services/llamaAutocompleteService.js +277 -0
  63. package/backend/dist/services/llamaAutocompleteService.js.map +1 -0
  64. package/backend/dist/services/managedServicesController.js +72 -0
  65. package/backend/dist/services/managedServicesController.js.map +1 -0
  66. package/backend/dist/services/managedServicesManager.js +91 -0
  67. package/backend/dist/services/managedServicesManager.js.map +1 -0
  68. package/backend/dist/services/probes/amdLinuxUsageProbe.js +109 -0
  69. package/backend/dist/services/probes/amdLinuxUsageProbe.js.map +1 -0
  70. package/backend/dist/services/probes/gpuUsageProbe.js +3 -0
  71. package/backend/dist/services/probes/gpuUsageProbe.js.map +1 -0
  72. package/backend/dist/services/probes/nvidiaSmiUsageProbe.js +67 -0
  73. package/backend/dist/services/probes/nvidiaSmiUsageProbe.js.map +1 -0
  74. package/backend/dist/services/providers/systemInfoLinuxProvider.js +188 -0
  75. package/backend/dist/services/providers/systemInfoLinuxProvider.js.map +1 -0
  76. package/backend/dist/services/providers/systemInfoWindowsProvider.js +101 -0
  77. package/backend/dist/services/providers/systemInfoWindowsProvider.js.map +1 -0
  78. package/backend/dist/services/serviceConfigController.js +116 -0
  79. package/backend/dist/services/serviceConfigController.js.map +1 -0
  80. package/backend/dist/services/serviceController.js +3 -0
  81. package/backend/dist/services/serviceController.js.map +1 -0
  82. package/backend/dist/services/serviceManager.js +157 -0
  83. package/backend/dist/services/serviceManager.js.map +1 -0
  84. package/backend/dist/services/systemInfoProvider.js +3 -0
  85. package/backend/dist/services/systemInfoProvider.js.map +1 -0
  86. package/backend/dist/services/systemService.js +42 -0
  87. package/backend/dist/services/systemService.js.map +1 -0
  88. package/backend/public/browser/chunk-34LU6STA.js +1 -0
  89. package/backend/public/browser/chunk-JGNPKRLU.js +4 -0
  90. package/backend/public/browser/favicon.svg +11 -0
  91. package/backend/public/browser/index.html +15 -0
  92. package/backend/public/browser/main-MUMQYBNS.js +5579 -0
  93. package/backend/public/browser/polyfills-5CFQRCPP.js +2 -0
  94. package/backend/public/browser/styles-QBUDNRKH.css +1 -0
  95. package/bin/aiservermanager.js +2 -0
  96. package/package.json +45 -0
package/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # aiservermanager
2
+
3
+ GPU server monitoring and AI service manager web dashboard.
4
+ Monitors GPU utilization and manages llama.cpp / ComfyUI services through a web UI.
5
+
6
+ ## Run
7
+
8
+ Requires Node.js >= 24.
9
+
10
+ ```bash
11
+ npx --yes aiservermanager@latest
12
+ ```
13
+
14
+ Open http://127.0.0.1:4243 and log in with your OS account password.
15
+
16
+ ## Linux: PAM auth
17
+
18
+ Password login on Linux uses PAM via the `authenticate-pam` module (built on install).
19
+ Install the PAM development headers first:
20
+
21
+ ```bash
22
+ sudo apt install libpam0g-dev
23
+ ```
24
+
25
+ Without them the module is skipped and password login is unavailable.
26
+
27
+ ## Configuration
28
+
29
+ - Port and host: `PORT` and `HOST` environment variables (defaults: `4243`, `127.0.0.1`)
30
+ - Persistent state (auth secret, managed services, custom service configs):
31
+ `~/.config/aiservermanager/`
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createContainer = createContainer;
4
+ require("reflect-metadata");
5
+ const inversify_1 = require("inversify");
6
+ const types_1 = require("./types");
7
+ const authService_1 = require("../services/authService");
8
+ const linuxPasswordVerifier_1 = require("../services/auth/linuxPasswordVerifier");
9
+ const windowsPasswordVerifier_1 = require("../services/auth/windowsPasswordVerifier");
10
+ const gpuService_1 = require("../services/gpuService");
11
+ const systemService_1 = require("../services/systemService");
12
+ const nvidiaSmiDetector_1 = require("../services/detectors/nvidiaSmiDetector");
13
+ const amdLinuxDetector_1 = require("../services/detectors/amdLinuxDetector");
14
+ const wmiDetector_1 = require("../services/detectors/wmiDetector");
15
+ const lspciEnricher_1 = require("../services/enrichers/lspciEnricher");
16
+ const vulkanEnricher_1 = require("../services/enrichers/vulkanEnricher");
17
+ const nvidiaSmiUsageProbe_1 = require("../services/probes/nvidiaSmiUsageProbe");
18
+ const amdLinuxUsageProbe_1 = require("../services/probes/amdLinuxUsageProbe");
19
+ const serviceManager_1 = require("../services/serviceManager");
20
+ const systemctlController_1 = require("../services/controllers/systemctlController");
21
+ const windowsServiceController_1 = require("../services/controllers/windowsServiceController");
22
+ const serviceConfigController_1 = require("../services/serviceConfigController");
23
+ const managedServicesController_1 = require("../services/managedServicesController");
24
+ const llamaAutocompleteService_1 = require("../services/llamaAutocompleteService");
25
+ const systemInfoLinuxProvider_1 = require("../services/providers/systemInfoLinuxProvider");
26
+ const systemInfoWindowsProvider_1 = require("../services/providers/systemInfoWindowsProvider");
27
+ const isWindows = process.platform === "win32";
28
+ function createContainer() {
29
+ const container = new inversify_1.Container();
30
+ // Password verifier — platform-specific strategy
31
+ if (isWindows) {
32
+ container.bind(types_1.PASSWORD_VERIFIER).to(windowsPasswordVerifier_1.WindowsPasswordVerifier).inSingletonScope();
33
+ }
34
+ else {
35
+ container.bind(types_1.PASSWORD_VERIFIER).to(linuxPasswordVerifier_1.LinuxPasswordVerifier).inSingletonScope();
36
+ }
37
+ // Auth service — JWT generation + brute-force protection
38
+ container.bind(types_1.AUTH_SERVICE).to(authService_1.AuthService).inSingletonScope();
39
+ // GpuService — singleton for the lifetime of the app
40
+ container.bind(types_1.GPU_SERVICE).to(gpuService_1.GpuService).inSingletonScope();
41
+ // SystemService — CPU + memory info
42
+ container.bind(types_1.SYSTEM_SERVICE).to(systemService_1.SystemService).inSingletonScope();
43
+ // Detectors — static GPU info (run once at bootstrap)
44
+ container.bind(types_1.GPU_DETECTOR).to(nvidiaSmiDetector_1.NvidiaSmiDetector);
45
+ if (isWindows) {
46
+ container.bind(types_1.GPU_DETECTOR).to(wmiDetector_1.WmiDetector);
47
+ }
48
+ else {
49
+ container.bind(types_1.GPU_DETECTOR).to(amdLinuxDetector_1.AmdLinuxDetector);
50
+ }
51
+ // Enrichers — Linux only, enrich static info
52
+ if (!isWindows) {
53
+ container.bind(types_1.GPU_ENRICHER).to(lspciEnricher_1.LspciEnricher);
54
+ container.bind(types_1.GPU_ENRICHER).to(vulkanEnricher_1.VulkanEnricher);
55
+ }
56
+ // Usage probes — dynamic metrics (usage, temperature, vramUsed)
57
+ container.bind(types_1.GPU_USAGE_PROBE).to(nvidiaSmiUsageProbe_1.NvidiaSmiUsageProbe);
58
+ if (!isWindows) {
59
+ container.bind(types_1.GPU_USAGE_PROBE).to(amdLinuxUsageProbe_1.AmdLinuxUsageProbe);
60
+ }
61
+ // Service controllers — platform-aware strategies
62
+ container.bind(types_1.SERVICE_CONTROLLER).to(systemctlController_1.SystemctlController);
63
+ container.bind(types_1.SERVICE_CONTROLLER).to(windowsServiceController_1.WindowsServiceController);
64
+ // Service manager — orchestrates llama.cpp / ComfyUI (multi-injects controllers)
65
+ container.bind(types_1.SERVICE_MANAGER).to(serviceManager_1.ServiceManager).inSingletonScope();
66
+ // Service config controller — CRUD for user-created llama service configs
67
+ container.bind(types_1.SERVICE_CONFIG_CONTROLLER).to(serviceConfigController_1.ServiceConfigController).inSingletonScope();
68
+ // Managed services controller — discover available services + manage user selection
69
+ container
70
+ .bind(types_1.MANAGED_SERVICES_CONTROLLER)
71
+ .to(managedServicesController_1.ManagedServicesController)
72
+ .inSingletonScope();
73
+ // Llama autocomplete service — path suggestions, hosts, devices
74
+ container.bind(types_1.LLAMA_AUTOCOMPLETE_SERVICE).to(llamaAutocompleteService_1.LlamaAutocompleteService).inSingletonScope();
75
+ // System info providers — platform-specific strategies
76
+ container.bind(types_1.SYSTEM_INFO_PROVIDER).to(systemInfoLinuxProvider_1.SystemInfoLinuxProvider);
77
+ container.bind(types_1.SYSTEM_INFO_PROVIDER).to(systemInfoWindowsProvider_1.SystemInfoWindowsProvider);
78
+ return container;
79
+ }
80
+ //# sourceMappingURL=container.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container.js","sourceRoot":"","sources":["../../src/di/container.ts"],"names":[],"mappings":";;AA8CA,0CA+DC;AA7GD,4BAA0B;AAC1B,yCAAsC;AACtC,mCAciB;AACjB,yDAAsD;AAEtD,kFAA+E;AAC/E,sFAAmF;AACnF,uDAAoD;AACpD,6DAA0D;AAE1D,+EAA4E;AAC5E,6EAA0E;AAC1E,mEAAgE;AAEhE,uEAAoE;AACpE,yEAAsE;AAEtE,gFAA6E;AAC7E,8EAA2E;AAC3E,+DAA4D;AAE5D,qFAAkF;AAClF,+FAA4F;AAC5F,iFAA8E;AAC9E,qFAAkF;AAClF,mFAAgF;AAEhF,2FAAwF;AACxF,+FAA4F;AAE5F,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;AAE/C,SAAgB,eAAe;IAC7B,MAAM,SAAS,GAAG,IAAI,qBAAS,EAAE,CAAC;IAElC,iDAAiD;IACjD,IAAI,SAAS,EAAE,CAAC;QACd,SAAS,CAAC,IAAI,CAAmB,yBAAiB,CAAC,CAAC,EAAE,CAAC,iDAAuB,CAAC,CAAC,gBAAgB,EAAE,CAAC;IACrG,CAAC;SAAM,CAAC;QACN,SAAS,CAAC,IAAI,CAAmB,yBAAiB,CAAC,CAAC,EAAE,CAAC,6CAAqB,CAAC,CAAC,gBAAgB,EAAE,CAAC;IACnG,CAAC;IAED,yDAAyD;IACzD,SAAS,CAAC,IAAI,CAAc,oBAAY,CAAC,CAAC,EAAE,CAAC,yBAAW,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAE7E,qDAAqD;IACrD,SAAS,CAAC,IAAI,CAAa,mBAAW,CAAC,CAAC,EAAE,CAAC,uBAAU,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAE1E,oCAAoC;IACpC,SAAS,CAAC,IAAI,CAAgB,sBAAc,CAAC,CAAC,EAAE,CAAC,6BAAa,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAEnF,sDAAsD;IACtD,SAAS,CAAC,IAAI,CAAc,oBAAY,CAAC,CAAC,EAAE,CAAC,qCAAiB,CAAC,CAAC;IAChE,IAAI,SAAS,EAAE,CAAC;QACd,SAAS,CAAC,IAAI,CAAc,oBAAY,CAAC,CAAC,EAAE,CAAC,yBAAW,CAAC,CAAC;IAC5D,CAAC;SAAM,CAAC;QACN,SAAS,CAAC,IAAI,CAAc,oBAAY,CAAC,CAAC,EAAE,CAAC,mCAAgB,CAAC,CAAC;IACjE,CAAC;IAED,6CAA6C;IAC7C,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,SAAS,CAAC,IAAI,CAAc,oBAAY,CAAC,CAAC,EAAE,CAAC,6BAAa,CAAC,CAAC;QAC5D,SAAS,CAAC,IAAI,CAAc,oBAAY,CAAC,CAAC,EAAE,CAAC,+BAAc,CAAC,CAAC;IAC/D,CAAC;IAED,gEAAgE;IAChE,SAAS,CAAC,IAAI,CAAgB,uBAAe,CAAC,CAAC,EAAE,CAAC,yCAAmB,CAAC,CAAC;IACvE,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,SAAS,CAAC,IAAI,CAAgB,uBAAe,CAAC,CAAC,EAAE,CAAC,uCAAkB,CAAC,CAAC;IACxE,CAAC;IAED,kDAAkD;IAClD,SAAS,CAAC,IAAI,CAAoB,0BAAkB,CAAC,CAAC,EAAE,CAAC,yCAAmB,CAAC,CAAC;IAC9E,SAAS,CAAC,IAAI,CAAoB,0BAAkB,CAAC,CAAC,EAAE,CAAC,mDAAwB,CAAC,CAAC;IAEnF,iFAAiF;IACjF,SAAS,CAAC,IAAI,CAAiB,uBAAe,CAAC,CAAC,EAAE,CAAC,+BAAc,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAEtF,0EAA0E;IAC1E,SAAS,CAAC,IAAI,CAA0B,iCAAyB,CAAC,CAAC,EAAE,CAAC,iDAAuB,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAElH,oFAAoF;IACpF,SAAS;SACN,IAAI,CAA4B,mCAA2B,CAAC;SAC5D,EAAE,CAAC,qDAAyB,CAAC;SAC7B,gBAAgB,EAAE,CAAC;IAEtB,gEAAgE;IAChE,SAAS,CAAC,IAAI,CAA2B,kCAA0B,CAAC,CAAC,EAAE,CAAC,mDAAwB,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAErH,uDAAuD;IACvD,SAAS,CAAC,IAAI,CAAqB,4BAAoB,CAAC,CAAC,EAAE,CAAC,iDAAuB,CAAC,CAAC;IACrF,SAAS,CAAC,IAAI,CAAqB,4BAAoB,CAAC,CAAC,EAAE,CAAC,qDAAyB,CAAC,CAAC;IAEvF,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LLAMA_AUTOCOMPLETE_SERVICE = exports.MANAGED_SERVICES_CONTROLLER = exports.SERVICE_CONFIG_CONTROLLER = exports.SERVICE_CONTROLLER = exports.SERVICE_MANAGER = exports.GPU_USAGE_PROBE = exports.GPU_ENRICHER = exports.GPU_DETECTOR = exports.SYSTEM_INFO_PROVIDER = exports.SYSTEM_SERVICE = exports.GPU_SERVICE = exports.PASSWORD_VERIFIER = exports.AUTH_SERVICE = void 0;
4
+ /** Injection token constants for InversifyJS. */
5
+ exports.AUTH_SERVICE = "AUTH_SERVICE";
6
+ exports.PASSWORD_VERIFIER = "PASSWORD_VERIFIER";
7
+ exports.GPU_SERVICE = "GPU_SERVICE";
8
+ exports.SYSTEM_SERVICE = "SYSTEM_SERVICE";
9
+ exports.SYSTEM_INFO_PROVIDER = "SYSTEM_INFO_PROVIDER";
10
+ exports.GPU_DETECTOR = "GPU_DETECTOR";
11
+ exports.GPU_ENRICHER = "GPU_ENRICHER";
12
+ exports.GPU_USAGE_PROBE = "GPU_USAGE_PROBE";
13
+ exports.SERVICE_MANAGER = "SERVICE_MANAGER";
14
+ exports.SERVICE_CONTROLLER = "SERVICE_CONTROLLER";
15
+ exports.SERVICE_CONFIG_CONTROLLER = "SERVICE_CONFIG_CONTROLLER";
16
+ exports.MANAGED_SERVICES_CONTROLLER = "MANAGED_SERVICES_CONTROLLER";
17
+ exports.LLAMA_AUTOCOMPLETE_SERVICE = "LLAMA_AUTOCOMPLETE_SERVICE";
18
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/di/types.ts"],"names":[],"mappings":";;;AAAA,iDAAiD;AACpC,QAAA,YAAY,GAAG,cAAc,CAAC;AAC9B,QAAA,iBAAiB,GAAG,mBAAmB,CAAC;AACxC,QAAA,WAAW,GAAG,aAAa,CAAC;AAC5B,QAAA,cAAc,GAAG,gBAAgB,CAAC;AAClC,QAAA,oBAAoB,GAAG,sBAAsB,CAAC;AAC9C,QAAA,YAAY,GAAG,cAAc,CAAC;AAC9B,QAAA,YAAY,GAAG,cAAc,CAAC;AAC9B,QAAA,eAAe,GAAG,iBAAiB,CAAC;AACpC,QAAA,eAAe,GAAG,iBAAiB,CAAC;AACpC,QAAA,kBAAkB,GAAG,oBAAoB,CAAC;AAC1C,QAAA,yBAAyB,GAAG,2BAA2B,CAAC;AACxD,QAAA,2BAA2B,GAAG,6BAA6B,CAAC;AAC5D,QAAA,0BAA0B,GAAG,4BAA4B,CAAC"}
@@ -0,0 +1,10 @@
1
+ Add-Type -AssemblyName System.DirectoryServices.AccountManagement
2
+ $ctx = [System.DirectoryServices.AccountManagement.PrincipalContext]::new(
3
+ [System.DirectoryServices.AccountManagement.ContextType]::Machine,
4
+ $env:COMPUTERNAME
5
+ )
6
+ if ($ctx.ValidateCredentials($env:USERNAME, $env:SM_PASSWORD)) {
7
+ Write-Output "OK"
8
+ } else {
9
+ Write-Output "FAIL"
10
+ }
@@ -0,0 +1,15 @@
1
+ Get-CimInstance -ClassName Win32_VideoController | ForEach-Object {
2
+ $pciBusId = "";
3
+ try {
4
+ $regKey = "HKLM:\SYSTEM\CurrentControlSet\Enum\" + $_.PNPDeviceID;
5
+ $props = Get-ItemProperty $regKey -ErrorAction Stop;
6
+ if ($props.LocationInformation -match '\((\d+),(\d+),(\d+)\)') {
7
+ $pciBusId = "{0:X2}:{1:X2}.{2}" -f [int]$Matches[1], [int]$Matches[2], $Matches[3];
8
+ }
9
+ } catch {}
10
+ [PSCustomObject]@{
11
+ name = $_.Name;
12
+ vram = $_.AdapterRAM;
13
+ pci = $pciBusId;
14
+ }
15
+ } | ConvertTo-Json
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExecTools = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const util_1 = require("util");
6
+ const exec = (0, util_1.promisify)(child_process_1.exec);
7
+ class ExecTools {
8
+ /**
9
+ * Run a shell command safely — never throws, returns stderr on failure.
10
+ * On Windows the shell is PowerShell; on Linux it is /bin/sh.
11
+ */
12
+ static async safeExec(command, opts) {
13
+ const baseEnv = opts?.env ? { ...process.env, ...opts.env } : undefined;
14
+ return exec(command, {
15
+ timeout: opts?.timeout ?? 10_000,
16
+ maxBuffer: 1024 * 1024,
17
+ shell: process.platform === "win32" ? "powershell.exe" : "/bin/sh",
18
+ env: baseEnv,
19
+ }).catch((err) => ({
20
+ stdout: err.stdout ?? "",
21
+ stderr: err.stderr ?? err.message,
22
+ }));
23
+ }
24
+ /**
25
+ * Run a shell command and return the exit code along with stdout/stderr.
26
+ * Never throws — on failure returns whatever exitCode the process returned.
27
+ * On Windows the shell is PowerShell; on Linux it is /bin/sh.
28
+ */
29
+ static async safeExecWithCode(command, opts) {
30
+ const timeout = opts?.timeout ?? 10_000;
31
+ const shell = process.platform === "win32" ? "powershell.exe" : "/bin/sh";
32
+ return new Promise((resolve) => {
33
+ const child = (0, child_process_1.spawn)(command, { shell, timeout: undefined });
34
+ let stdout = "";
35
+ let stderr = "";
36
+ let resolved = false;
37
+ const done = (out) => {
38
+ if (!resolved) {
39
+ resolved = true;
40
+ resolve(out);
41
+ }
42
+ };
43
+ child.stdout.on("data", (chunk) => {
44
+ stdout += chunk.toString();
45
+ });
46
+ child.stderr.on("data", (chunk) => {
47
+ stderr += chunk.toString();
48
+ });
49
+ child.on("close", (code) => {
50
+ done({ stdout, stderr, exitCode: code ?? 1 });
51
+ });
52
+ child.on("error", (err) => {
53
+ done({ stdout: "", stderr: err.message, exitCode: 1 });
54
+ });
55
+ setTimeout(() => {
56
+ child.kill();
57
+ done({ stdout, stderr: `Timeout after ${timeout}ms`, exitCode: 1 });
58
+ }, timeout);
59
+ });
60
+ }
61
+ /**
62
+ * Run a PowerShell script file safely — never throws, returns stderr on failure.
63
+ * Uses spawn to avoid shell escaping issues with complex scripts.
64
+ */
65
+ static async safeExecPs1(scriptPath, opts) {
66
+ const timeout = opts?.timeout ?? 10_000;
67
+ const baseEnv = opts?.env ? { ...process.env, ...opts.env } : undefined;
68
+ return new Promise((resolve) => {
69
+ const child = (0, child_process_1.spawn)("powershell.exe", ["-ExecutionPolicy", "Bypass", "-File", scriptPath], {
70
+ env: baseEnv,
71
+ });
72
+ let stdout = "";
73
+ let stderr = "";
74
+ let resolved = false;
75
+ const done = (out) => {
76
+ if (!resolved) {
77
+ resolved = true;
78
+ resolve(out);
79
+ }
80
+ };
81
+ child.stdout.on("data", (chunk) => {
82
+ stdout += chunk.toString();
83
+ });
84
+ child.stderr.on("data", (chunk) => {
85
+ stderr += chunk.toString();
86
+ });
87
+ child.on("close", () => {
88
+ done({ stdout, stderr });
89
+ });
90
+ child.on("error", (err) => {
91
+ done({ stdout: "", stderr: err.message });
92
+ });
93
+ setTimeout(() => {
94
+ child.kill();
95
+ done({ stdout: "", stderr: `Timeout after ${timeout}ms` });
96
+ }, timeout);
97
+ });
98
+ }
99
+ }
100
+ exports.ExecTools = ExecTools;
101
+ //# sourceMappingURL=ExecTools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExecTools.js","sourceRoot":"","sources":["../../src/helpers/ExecTools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ require("reflect-metadata");
7
+ const express_1 = __importDefault(require("express"));
8
+ const cors_1 = __importDefault(require("cors"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const container_1 = require("./di/container");
11
+ const gpuRoutes_1 = __importDefault(require("./routes/gpuRoutes"));
12
+ const serviceRoutes_1 = __importDefault(require("./routes/serviceRoutes"));
13
+ const systemRoutes_1 = __importDefault(require("./routes/systemRoutes"));
14
+ const authRoutes_1 = __importDefault(require("./routes/authRoutes"));
15
+ const authMiddleware_1 = require("./middleware/authMiddleware");
16
+ const types_1 = require("./di/types");
17
+ const container = (0, container_1.createContainer)();
18
+ const app = (0, express_1.default)();
19
+ const PORT = parseInt(process.env.PORT ?? "4243");
20
+ const HOST = process.env.HOST ?? "127.0.0.1";
21
+ app.use((0, cors_1.default)());
22
+ app.use(express_1.default.json());
23
+ // ── Public: frontend (must be before auth middleware) ──
24
+ const publicPath = path_1.default.join(__dirname, "../public/browser");
25
+ app.use(express_1.default.static(publicPath));
26
+ app.get("/health", (_req, res) => {
27
+ res.json({ status: "ok", uptime: process.uptime() });
28
+ });
29
+ // SPA fallback — serve index.html for all non-API routes
30
+ app.use((_req, res, next) => {
31
+ // Let API routes pass through; only SPA fallback for non-/api paths
32
+ if (_req.path.startsWith("/api"))
33
+ return next();
34
+ res.sendFile(path_1.default.join(publicPath, "index.html"));
35
+ });
36
+ // ── Public API routes ──
37
+ app.use("/api/auth", (0, authRoutes_1.default)(container));
38
+ // ── Protected API routes — require JWT ──
39
+ app.use((0, authMiddleware_1.authMiddleware)(container));
40
+ app.use("/api/gpus", (0, gpuRoutes_1.default)(container));
41
+ app.use("/api/services", (0, serviceRoutes_1.default)(container));
42
+ app.use("/api/system", (0, systemRoutes_1.default)(container));
43
+ // Bootstrap: auto-install custom services, cache install errors
44
+ const sm = container.get(types_1.SERVICE_MANAGER);
45
+ sm.bootstrap().catch((err) => {
46
+ console.error("[ServiceManager] bootstrap failed:", err);
47
+ });
48
+ app.listen(PORT, HOST, () => {
49
+ console.log(`aiservermanager backend listening on http://${HOST}:${PORT}`);
50
+ });
51
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAAA,4BAA0B;AAC1B,sDAA8B;AAC9B,gDAAwB;AACxB,gDAAwB;AACxB,8CAAiD;AACjD,mEAA2C;AAC3C,2EAAmD;AACnD,yEAAiD;AACjD,qEAA6C;AAC7C,gEAA6D;AAC7D,sCAA6C;AAG7C,MAAM,SAAS,GAAG,IAAA,2BAAe,GAAE,CAAC;AACpC,MAAM,GAAG,GAAG,IAAA,iBAAO,GAAE,CAAC;AACtB,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC;AAClD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,WAAW,CAAC;AAE7C,GAAG,CAAC,GAAG,CAAC,IAAA,cAAI,GAAE,CAAC,CAAC;AAChB,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AAExB,0DAA0D;AAE1D,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;AAC7D,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;AAEpC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAC/B,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACvD,CAAC,CAAC,CAAC;AAEH,yDAAyD;AACzD,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IAC1B,oEAAoE;IACpE,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,EAAE,CAAC;IAChD,GAAG,CAAC,QAAQ,CAAC,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC;AACpD,CAAC,CAAC,CAAC;AAEH,0BAA0B;AAE1B,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,IAAA,oBAAU,EAAC,SAAS,CAAC,CAAC,CAAC;AAE5C,2CAA2C;AAE3C,GAAG,CAAC,GAAG,CAAC,IAAA,+BAAc,EAAC,SAAS,CAAC,CAAC,CAAC;AACnC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,IAAA,mBAAS,EAAC,SAAS,CAAC,CAAC,CAAC;AAC3C,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,IAAA,uBAAa,EAAC,SAAS,CAAC,CAAC,CAAC;AACnD,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,IAAA,sBAAY,EAAC,SAAS,CAAC,CAAC,CAAC;AAEhD,gEAAgE;AAChE,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,CAAiB,uBAAe,CAAC,CAAC;AAC1D,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC3B,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE,GAAG,CAAC,CAAC;AAC3D,CAAC,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;IAC1B,OAAO,CAAC,GAAG,CAAC,+CAA+C,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC;AAC7E,CAAC,CAAC,CAAC"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.authMiddleware = authMiddleware;
4
+ const types_1 = require("../di/types");
5
+ /**
6
+ * Express middleware that validates JWT from Authorization: Bearer <token> header.
7
+ * Skips /health endpoint.
8
+ */
9
+ function authMiddleware(container) {
10
+ return (req, _res, next) => {
11
+ // Allow health check without auth
12
+ if (req.path === "/health")
13
+ return next();
14
+ const authHeader = req.headers.authorization;
15
+ if (!authHeader) {
16
+ return _res.status(401).json({ error: "Authorization header required" });
17
+ }
18
+ const parts = authHeader.split(" ");
19
+ if (parts.length !== 2 || parts[0] !== "Bearer") {
20
+ return _res.status(401).json({ error: "Invalid authorization format. Use: Bearer <token>" });
21
+ }
22
+ const authService = container.get(types_1.AUTH_SERVICE);
23
+ const payload = authService.verifyToken(parts[1]);
24
+ if (!payload) {
25
+ return _res.status(401).json({ error: "Invalid or expired token" });
26
+ }
27
+ // Attach user info to request
28
+ req.user = payload;
29
+ next();
30
+ };
31
+ }
32
+ //# sourceMappingURL=authMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authMiddleware.js","sourceRoot":"","sources":["../../src/middleware/authMiddleware.ts"],"names":[],"mappings":";;AASA,wCA0BC;AAjCD,uCAA2C;AAG3C;;;GAGG;AACH,SAAgB,cAAc,CAAC,SAAoB;IACjD,OAAO,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB,EAAE,EAAE;QAC1D,kCAAkC;QAClC,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,IAAI,EAAE,CAAC;QAE1C,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;QAC7C,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAC,CAAC;QAC3E,CAAC;QAED,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;YAChD,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mDAAmD,EAAE,CAAC,CAAC;QAC/F,CAAC;QAED,MAAM,WAAW,GAAG,SAAS,CAAC,GAAG,CAAc,oBAAY,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,WAAW,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAElD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC,CAAC;QACtE,CAAC;QAED,8BAA8B;QAC7B,GAAW,CAAC,IAAI,GAAG,OAAO,CAAC;QAC5B,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=GpuInfo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GpuInfo.js","sourceRoot":"","sources":["../../src/models/GpuInfo.ts"],"names":[],"mappings":""}
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseConfigFile = parseConfigFile;
4
+ exports.serializeConfig = serializeConfig;
5
+ exports.buildExecStart = buildExecStart;
6
+ /** Parse a config file into a ServiceConfig. Lines with "type=" or "command=" are metadata; every other non-empty line is a raw CLI argument. */
7
+ function parseConfigFile(name, raw) {
8
+ const flags = [];
9
+ let command = "";
10
+ let type;
11
+ for (const line of raw.split("\n")) {
12
+ const trimmed = line.trim();
13
+ if (!trimmed || trimmed.startsWith("#"))
14
+ continue;
15
+ if (trimmed.startsWith("type=")) {
16
+ const val = trimmed.slice("type=".length).trim();
17
+ if (val === "generic" || val === "llama-server") {
18
+ type = val;
19
+ }
20
+ }
21
+ else if (trimmed.startsWith("command=")) {
22
+ command = trimmed.slice("command=".length).trim();
23
+ }
24
+ else {
25
+ flags.push(trimmed);
26
+ }
27
+ }
28
+ return { name, type, command, flags };
29
+ }
30
+ /** Serialize a ServiceConfig back to a config file string. */
31
+ function serializeConfig(cfg) {
32
+ const lines = [];
33
+ if (cfg.type) {
34
+ lines.push(`type=${cfg.type}`);
35
+ }
36
+ lines.push(`command=${cfg.command}`);
37
+ for (const flag of cfg.flags) {
38
+ lines.push(flag);
39
+ }
40
+ return lines.join("\n") + "\n";
41
+ }
42
+ /** Build the full ExecStart command from a service config. */
43
+ function buildExecStart(cfg) {
44
+ return cfg.flags.length ? `${cfg.command} ${cfg.flags.join(" ")}` : cfg.command;
45
+ }
46
+ //# sourceMappingURL=ServiceConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ServiceConfig.js","sourceRoot":"","sources":["../../src/models/ServiceConfig.ts"],"names":[],"mappings":";;AAgBA,0CAsBC;AAGD,0CAUC;AAGD,wCAEC;AAzCD,iJAAiJ;AACjJ,SAAgB,eAAe,CAAC,IAAY,EAAE,GAAW;IACvD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,IAA6B,CAAC;IAElC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAElD,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAChC,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;YACjD,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,cAAc,EAAE,CAAC;gBAChD,IAAI,GAAG,GAAG,CAAC;YACb,CAAC;QACH,CAAC;aAAM,IAAI,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC1C,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AACxC,CAAC;AAED,8DAA8D;AAC9D,SAAgB,eAAe,CAAC,GAAkB;IAChD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QACb,KAAK,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IACjC,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AACjC,CAAC;AAED,8DAA8D;AAC9D,SAAgB,cAAc,CAAC,GAAkB;IAC/C,OAAO,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC;AAClF,CAAC"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=ServiceStatus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ServiceStatus.js","sourceRoot":"","sources":["../../src/models/ServiceStatus.ts"],"names":[],"mappings":""}
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = authRoutes;
4
+ const express_1 = require("express");
5
+ const types_1 = require("../di/types");
6
+ /** Extract real client IP, respecting reverse proxy headers. */
7
+ function getClientIp(req) {
8
+ const forwarded = req.headers["x-forwarded-for"];
9
+ if (forwarded) {
10
+ // X-Forwarded-For: "client, proxy1, proxy2" — first is the original client
11
+ return forwarded.split(",")[0].trim();
12
+ }
13
+ return req.ip ?? "127.0.0.1";
14
+ }
15
+ function authRoutes(container) {
16
+ const router = (0, express_1.Router)();
17
+ router.post("/login", async (req, res) => {
18
+ try {
19
+ const { password } = req.body;
20
+ if (!password) {
21
+ return res.status(400).json({ error: "Password is required" });
22
+ }
23
+ const authService = container.get(types_1.AUTH_SERVICE);
24
+ const ip = getClientIp(req);
25
+ // Check rate limit BEFORE touching PAM — timing-safe
26
+ const limited = authService.checkRateLimit(ip);
27
+ if (limited) {
28
+ return res.status(429).json({
29
+ error: "Too many attempts. Try again later.",
30
+ retryAfter: limited.retryAfter,
31
+ });
32
+ }
33
+ const token = await authService.login(ip, password);
34
+ if (!token) {
35
+ return res.status(401).json({ error: "Invalid password" });
36
+ }
37
+ res.json({ token });
38
+ }
39
+ catch (error) {
40
+ console.error("[Auth] login error:", error);
41
+ res.status(500).json({ error: "Internal server error" });
42
+ }
43
+ });
44
+ return router;
45
+ }
46
+ //# sourceMappingURL=authRoutes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authRoutes.js","sourceRoot":"","sources":["../../src/routes/authRoutes.ts"],"names":[],"mappings":";;AAeA,6BAqCC;AApDD,qCAAiC;AAEjC,uCAA2C;AAG3C,gEAAgE;AAChE,SAAS,WAAW,CAAC,GAA8B;IACjD,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAuB,CAAC;IACvE,IAAI,SAAS,EAAE,CAAC;QACd,2EAA2E;QAC3E,OAAO,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,CAAC;IACD,OAAO,GAAG,CAAC,EAAE,IAAI,WAAW,CAAC;AAC/B,CAAC;AAED,SAAwB,UAAU,CAAC,SAAoB;IACrD,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;IAExB,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACvC,IAAI,CAAC;YACH,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,IAA6B,CAAC;YAEvD,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC,CAAC;YACjE,CAAC;YAED,MAAM,WAAW,GAAG,SAAS,CAAC,GAAG,CAAc,oBAAY,CAAC,CAAC;YAC7D,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;YAE5B,qDAAqD;YACrD,MAAM,OAAO,GAAG,WAAW,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;YAC/C,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;oBAC1B,KAAK,EAAE,qCAAqC;oBAC5C,UAAU,EAAE,OAAO,CAAC,UAAU;iBAC/B,CAAC,CAAC;YACL,CAAC;YAED,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;YAEpD,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC;YAC7D,CAAC;YAED,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;YAC5C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = gpuRoutes;
4
+ const express_1 = require("express");
5
+ const types_1 = require("../di/types");
6
+ function gpuRoutes(container) {
7
+ const router = (0, express_1.Router)();
8
+ /** Static GPU info — call once on frontend init. */
9
+ router.get("/", async (_req, res) => {
10
+ try {
11
+ const gpuService = container.get(types_1.GPU_SERVICE);
12
+ const gpus = await gpuService.getStaticGpus();
13
+ res.json(gpus);
14
+ }
15
+ catch (err) {
16
+ res.status(500).json({ error: err instanceof Error ? err.message : "Unknown error" });
17
+ }
18
+ });
19
+ /** Dynamic usage metrics + system info — polled every few seconds. */
20
+ router.get("/usage", async (_req, res) => {
21
+ try {
22
+ const [gpuService, systemService] = [
23
+ container.get(types_1.GPU_SERVICE),
24
+ container.get(types_1.SYSTEM_SERVICE),
25
+ ];
26
+ const [gpus, system] = await Promise.all([gpuService.getUsage(), systemService.getSystemInfo()]);
27
+ res.json({ gpus, system });
28
+ }
29
+ catch (err) {
30
+ res.status(500).json({ error: err instanceof Error ? err.message : "Unknown error" });
31
+ }
32
+ });
33
+ return router;
34
+ }
35
+ //# sourceMappingURL=gpuRoutes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gpuRoutes.js","sourceRoot":"","sources":["../../src/routes/gpuRoutes.ts"],"names":[],"mappings":";;AAMA,4BA6BC;AAnCD,qCAAiC;AAEjC,uCAA0D;AAI1D,SAAwB,SAAS,CAAC,SAAoB;IACpD,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;IAExB,oDAAoD;IACpD,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QAClC,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,SAAS,CAAC,GAAG,CAAa,mBAAW,CAAC,CAAC;YAC1D,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,aAAa,EAAE,CAAC;YAC9C,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;QACxF,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,sEAAsE;IACtE,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QACvC,IAAI,CAAC;YACH,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG;gBAClC,SAAS,CAAC,GAAG,CAAa,mBAAW,CAAC;gBACtC,SAAS,CAAC,GAAG,CAAgB,sBAAc,CAAC;aAC7C,CAAC;YACF,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,aAAa,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;YACjG,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAC7B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;QACxF,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}