@camstack/system 1.2.417 → 1.2.418

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 (78) hide show
  1. package/dist/addon-utils.js +1 -1
  2. package/dist/addon-utils.mjs +1 -1
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.js +1 -1
  8. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  9. package/dist/builtins/autotrack/index.js +1 -1
  10. package/dist/builtins/autotrack/index.mjs +1 -1
  11. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
  12. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
  13. package/dist/builtins/calendars/calendars.addon.js +1 -1
  14. package/dist/builtins/calendars/calendars.addon.mjs +1 -1
  15. package/dist/builtins/camera-grid/index.js +1 -1
  16. package/dist/builtins/camera-grid/index.mjs +1 -1
  17. package/dist/builtins/composer/composer.addon.js +2 -2
  18. package/dist/builtins/composer/composer.addon.mjs +2 -2
  19. package/dist/builtins/console-logging/index.js +1 -1
  20. package/dist/builtins/console-logging/index.mjs +1 -1
  21. package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
  22. package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
  23. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  24. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  25. package/dist/builtins/hub-forwarder/index.js +1 -1
  26. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  27. package/dist/builtins/keychain/index.js +7 -9
  28. package/dist/builtins/keychain/index.mjs +7 -9
  29. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  30. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  31. package/dist/builtins/local-auth/local-auth.addon.js +2 -2
  32. package/dist/builtins/local-auth/local-auth.addon.mjs +2 -2
  33. package/dist/builtins/local-network/local-network.addon.js +2 -2
  34. package/dist/builtins/local-network/local-network.addon.mjs +2 -2
  35. package/dist/builtins/loki-logging/index.js +1 -1
  36. package/dist/builtins/loki-logging/index.mjs +1 -1
  37. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  38. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  39. package/dist/builtins/platform-probe/amd-npu.d.ts +6 -0
  40. package/dist/builtins/platform-probe/index.js +317 -19
  41. package/dist/builtins/platform-probe/index.mjs +318 -20
  42. package/dist/builtins/platform-probe/platform-scorer.d.ts +4 -0
  43. package/dist/builtins/platform-probe/windows-hardware.d.ts +77 -0
  44. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  45. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  46. package/dist/builtins/snapshot/index.js +1 -1
  47. package/dist/builtins/snapshot/index.mjs +1 -1
  48. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  49. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  50. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  51. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  52. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  53. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  54. package/dist/builtins/system-config/system-config.addon.js +2 -2
  55. package/dist/builtins/system-config/system-config.addon.mjs +2 -2
  56. package/dist/builtins/winston-logging/index.js +1 -1
  57. package/dist/builtins/winston-logging/index.mjs +1 -1
  58. package/dist/{composition-sources-CndJVssc.mjs → composition-sources-0cPw9E0M.mjs} +1 -1
  59. package/dist/{composition-sources-ChAX2Kdo.js → composition-sources-BVjuMvpb.js} +1 -1
  60. package/dist/{constants-VXvZkGgw.js → constants-Aj0OBqlt.js} +1 -1
  61. package/dist/{constants-BzxwnfDc.mjs → constants-C8T7u1_Z.mjs} +1 -1
  62. package/dist/{custom-action-registry-DHv0kYcT.mjs → custom-action-registry-BQnbN0DP.mjs} +1 -2
  63. package/dist/{custom-action-registry-Bb28ibZV.js → custom-action-registry-Bf_YnagS.js} +3 -4
  64. package/dist/{dist-BMOD9OyH.js → dist-DVUg8e1T.js} +56 -72
  65. package/dist/{dist-H_RapIeM.mjs → dist-DlmdESNT.mjs} +57 -55
  66. package/dist/index.js +38 -10
  67. package/dist/index.mjs +38 -10
  68. package/dist/{jwt-signing-key-xQxqpwMC.mjs → jwt-signing-key-CGg61A5v.mjs} +1 -1
  69. package/dist/{jwt-signing-key-BiKHENST.js → jwt-signing-key-Jyu81jZR.js} +1 -1
  70. package/dist/{restore-archive-BobsETEp.js → restore-archive-Bq5PpQ-H.js} +1 -1
  71. package/dist/{restore-archive-CCAQicV-.mjs → restore-archive-DVo5ooBt.mjs} +1 -1
  72. package/dist/{retired-settings-keys-BMq2u4Wv.js → retired-settings-keys-CihMX6h9.js} +1 -1
  73. package/dist/{retired-settings-keys-CwL4zHLg.mjs → retired-settings-keys-k_XuC-Mt.mjs} +1 -1
  74. package/dist/{tls-C0TRPfqc.js → tls-BgImdxCO.js} +4 -11
  75. package/dist/{tls-D-2dPY-5.mjs → tls-CaOLZ09W.mjs} +5 -12
  76. package/package.json +1 -1
  77. package/dist/amd-npu-B-QyyMyF.js +0 -53
  78. package/dist/amd-npu-Db_SoSuL.mjs +0 -52
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BMOD9OyH.js");
6
+ const require_dist = require("../../dist-DVUg8e1T.js");
7
7
  let node_os = require("node:os");
8
8
  node_os = require_chunk.__toESM(node_os);
9
9
  let node_fs = require("node:fs");
@@ -59,7 +59,257 @@ function classifyCoralAccelerator(fsx) {
59
59
  return null;
60
60
  }
61
61
  //#endregion
62
+ //#region src/builtins/platform-probe/amd-npu.ts
63
+ var amd_npu_exports = /* @__PURE__ */ require_chunk.__exportAll({
64
+ classifyAmdNpu: () => classifyAmdNpu,
65
+ xdnaInfoForDevice: () => xdnaInfoForDevice
66
+ });
67
+ var ACCEL_CLASS = "/sys/class/accel";
68
+ var ACCEL_DEV = "/dev/accel";
69
+ var AMD_PCI_VENDOR = "0x1022";
70
+ /** Phoenix / Hawk Point XDNA1. */
71
+ var XDNA1_DEVICES = new Set(["0x1502"]);
72
+ /** Strix / Krackan XDNA2. */
73
+ var XDNA2_DEVICES = new Set(["0x17f0"]);
74
+ function normalizePciId(raw) {
75
+ const t = raw.trim().toLowerCase();
76
+ if (t.startsWith("0x")) return t;
77
+ if (t.length > 0 && [...t].every((c) => "0123456789abcdef".includes(c))) return "0x" + t;
78
+ return t;
79
+ }
80
+ function hasAccelCharDevice(fsx) {
81
+ if (fsx.listDir(ACCEL_DEV).some((n) => n.startsWith("accel"))) return true;
82
+ return fsx.exists("/dev/accel/accel0");
83
+ }
84
+ /**
85
+ * Detect an AMD XDNA NPU and report it as **present only**.
86
+ *
87
+ * Inference is NOT offered until P2 (Ryzen AI on Linux is Strix-first;
88
+ * Phoenix is not a production path today). The probe still names the
89
+ * generation so the UI and fleet census can show the hardware.
90
+ */
91
+ function classifyAmdNpu(ctx, fsx) {
92
+ if (ctx.platform !== "linux" || ctx.arch !== "x64" || !ctx.amdPresent) return null;
93
+ if (!hasAccelCharDevice(fsx)) return null;
94
+ for (const name of fsx.listDir(ACCEL_CLASS)) {
95
+ if (!name.startsWith("accel")) continue;
96
+ const vendor = normalizePciId(fsx.readFile("/sys/class/accel/" + name + "/device/vendor"));
97
+ const device = normalizePciId(fsx.readFile("/sys/class/accel/" + name + "/device/device"));
98
+ if (vendor !== AMD_PCI_VENDOR) continue;
99
+ return xdnaInfoForDevice(device);
100
+ }
101
+ return null;
102
+ }
103
+ /**
104
+ * The NPU descriptor for an AMD PCI device id (`0x1502` or `1502`). Shared by
105
+ * the Linux sysfs probe and the Windows PnP probe so both name the same
106
+ * silicon the same way.
107
+ */
108
+ function xdnaInfoForDevice(rawDeviceId) {
109
+ const device = normalizePciId(rawDeviceId);
110
+ if (XDNA1_DEVICES.has(device)) return {
111
+ type: "amd-xdna",
112
+ name: "AMD XDNA1 NPU",
113
+ generation: "xdna1"
114
+ };
115
+ if (XDNA2_DEVICES.has(device)) return {
116
+ type: "amd-xdna",
117
+ name: "AMD XDNA2 NPU",
118
+ generation: "xdna2"
119
+ };
120
+ return {
121
+ type: "amd-xdna",
122
+ name: "AMD XDNA NPU"
123
+ };
124
+ }
125
+ //#endregion
126
+ //#region src/builtins/platform-probe/windows-hardware.ts
127
+ var PCI_VENDOR_NVIDIA = "10DE";
128
+ var PCI_VENDOR_INTEL = "8086";
129
+ /** Radeon graphics are 1002; the XDNA NPU is a 1022 device. */
130
+ var PCI_VENDORS_AMD = new Set(["1002", "1022"]);
131
+ /** Adapters that are not hardware: RDP, hypervisor, capture and spoofing drivers. */
132
+ var VIRTUAL_ADAPTER = /basic display|basic render|remote display|hyper-v|vmware|virtualbox|parallels|citrix|parsec|indirect display|displaylink|mirror driver|virtual/i;
133
+ /** One PowerShell process answers both: video controllers and AMD PCI devices. */
134
+ var POWERSHELL_SCRIPT = [
135
+ "$ErrorActionPreference = 'SilentlyContinue'",
136
+ "$gpu = @(Get-CimInstance Win32_VideoController | Select-Object Name, PNPDeviceID)",
137
+ "$npu = @(Get-CimInstance Win32_PnPEntity | Where-Object { $_.PNPDeviceID -match 'VEN_1022&DEV_(1502|17F0)' } | Select-Object Name, PNPDeviceID)",
138
+ "@{ gpu = $gpu; npu = $npu } | ConvertTo-Json -Compress -Depth 3"
139
+ ].join("; ");
140
+ function powershellInvocation(systemRoot) {
141
+ return [`${systemRoot ?? "C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`, [
142
+ "-NoProfile",
143
+ "-NonInteractive",
144
+ "-ExecutionPolicy",
145
+ "Bypass",
146
+ "-Command",
147
+ POWERSHELL_SCRIPT
148
+ ]];
149
+ }
150
+ var WMIC_VIDEO_ARGS = [
151
+ "path",
152
+ "Win32_VideoController",
153
+ "get",
154
+ "Name,PNPDeviceID",
155
+ "/format:csv"
156
+ ];
157
+ var WMIC_AMD_PNP_ARGS = [
158
+ "path",
159
+ "Win32_PnPEntity",
160
+ "where",
161
+ "PNPDeviceID like 'PCI\\\\VEN_1022&DEV_%'",
162
+ "get",
163
+ "Name,PNPDeviceID",
164
+ "/format:csv"
165
+ ];
166
+ function asRecordList(value) {
167
+ if (Array.isArray(value)) return value.filter((v) => v !== null && typeof v === "object" && !Array.isArray(v));
168
+ return value !== null && typeof value === "object" ? [Object.fromEntries(Object.entries(value))] : [];
169
+ }
170
+ function str(value) {
171
+ return typeof value === "string" ? value : "";
172
+ }
173
+ /** PowerShell prepends a BOM on some hosts, which `JSON.parse` rejects. */
174
+ function parseJson(text) {
175
+ return JSON.parse(text.replace(/^\uFEFF/, "").trim());
176
+ }
177
+ /**
178
+ * `{"gpu":[...],"npu":[...]}` as the script emits it. A single result comes
179
+ * back as an object rather than a one-element array, and an empty one as
180
+ * `null` — all three are handled.
181
+ */
182
+ function parsePowershellOutput(text) {
183
+ const root = parseJson(text);
184
+ const gpu = root !== null && typeof root === "object" ? Reflect.get(root, "gpu") : void 0;
185
+ const npu = root !== null && typeof root === "object" ? Reflect.get(root, "npu") : void 0;
186
+ const toDevice = (r) => ({
187
+ name: str(r["Name"]),
188
+ pnpDeviceId: str(r["PNPDeviceID"])
189
+ });
190
+ return {
191
+ controllers: asRecordList(gpu).map(toDevice),
192
+ pnp: asRecordList(npu).map(toDevice)
193
+ };
194
+ }
195
+ /**
196
+ * `wmic ... /format:csv` — a blank line, then `Node,<cols alphabetical>`, then
197
+ * one row per object. The device name can contain commas, so only the FIRST
198
+ * column boundaries are trusted: columns after `Node` are `Name,PNPDeviceID`
199
+ * and the PNP id (no commas) is taken from the END of the row.
200
+ */
201
+ function parseWmicCsv(text) {
202
+ const lines = text.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
203
+ const headerIndex = lines.findIndex((l) => /^node,/i.test(l));
204
+ if (headerIndex < 0) return [];
205
+ const out = [];
206
+ for (const line of lines.slice(headerIndex + 1)) {
207
+ const parts = line.split(",");
208
+ if (parts.length < 3) continue;
209
+ const pnpDeviceId = (parts[parts.length - 1] ?? "").trim();
210
+ const name = parts.slice(1, -1).join(",").trim();
211
+ if (pnpDeviceId.length > 0) out.push({
212
+ name,
213
+ pnpDeviceId
214
+ });
215
+ }
216
+ return out;
217
+ }
218
+ /** The PCI vendor id (`10DE`) out of a PnP id like `PCI\VEN_10DE&DEV_2504&...`. */
219
+ function pciVendorOf(pnpDeviceId) {
220
+ const m = /VEN_([0-9A-F]{4})/i.exec(pnpDeviceId);
221
+ return m?.[1] ? m[1].toUpperCase() : null;
222
+ }
223
+ function pciDeviceOf(pnpDeviceId) {
224
+ const m = /DEV_([0-9A-F]{4})/i.exec(pnpDeviceId);
225
+ return m?.[1] ? m[1].toLowerCase() : null;
226
+ }
227
+ function gpuVendor(c) {
228
+ const vendor = pciVendorOf(c.pnpDeviceId);
229
+ if (vendor === PCI_VENDOR_NVIDIA) return "nvidia";
230
+ if (vendor === PCI_VENDOR_INTEL) return "intel";
231
+ if (vendor !== null && PCI_VENDORS_AMD.has(vendor)) return "amd";
232
+ if (/nvidia|geforce|quadro|rtx/i.test(c.name)) return "nvidia";
233
+ if (/intel/i.test(c.name)) return "intel";
234
+ if (/amd|radeon/i.test(c.name)) return "amd";
235
+ return null;
236
+ }
237
+ /**
238
+ * The accelerator the node should treat as its GPU. Same preference as the
239
+ * Linux probe — NVIDIA, then Intel, then AMD — so a laptop with an Intel iGPU
240
+ * and a discrete card is described the way the rest of the system already
241
+ * expects. Virtual adapters never count.
242
+ */
243
+ function classifyWindowsGpu(controllers) {
244
+ const real = controllers.filter((c) => c.name.length > 0 && !VIRTUAL_ADAPTER.test(c.name));
245
+ for (const wanted of [
246
+ "nvidia",
247
+ "intel",
248
+ "amd"
249
+ ]) {
250
+ const hit = real.find((c) => gpuVendor(c) === wanted);
251
+ if (hit) return {
252
+ type: wanted,
253
+ name: hit.name
254
+ };
255
+ }
256
+ return null;
257
+ }
258
+ /**
259
+ * An AMD XDNA NPU from PnP ids: `PCI\VEN_1022&DEV_1502` (Phoenix / Hawk Point)
260
+ * or `DEV_17F0` (Strix / Krackan). Present-only, exactly like the Linux probe.
261
+ */
262
+ function classifyWindowsAmdNpu(devices) {
263
+ for (const d of devices) {
264
+ if (pciVendorOf(d.pnpDeviceId) !== "1022") continue;
265
+ const device = pciDeviceOf(d.pnpDeviceId);
266
+ if (device === "1502" || device === "17f0") return xdnaInfoForDevice(device);
267
+ }
268
+ return null;
269
+ }
270
+ async function viaPowershell(run, systemRoot) {
271
+ const [file, args] = powershellInvocation(systemRoot);
272
+ const parsed = parsePowershellOutput(await run(file, args));
273
+ return {
274
+ gpu: classifyWindowsGpu(parsed.controllers),
275
+ npu: classifyWindowsAmdNpu(parsed.pnp),
276
+ source: "powershell"
277
+ };
278
+ }
279
+ async function viaWmic(run) {
280
+ const [video, amd] = await Promise.all([run("wmic", WMIC_VIDEO_ARGS), run("wmic", WMIC_AMD_PNP_ARGS).catch(() => "")]);
281
+ return {
282
+ gpu: classifyWindowsGpu(parseWmicCsv(video)),
283
+ npu: classifyWindowsAmdNpu(parseWmicCsv(amd)),
284
+ source: "wmic"
285
+ };
286
+ }
287
+ /**
288
+ * GPU + AMD NPU on a Windows host. NEVER throws: a probe that cannot run is
289
+ * `{ gpu: null, npu: null, source: 'none' }` and boot goes on — the node then
290
+ * scores as a CPU node, which is correct for hardware we could not see.
291
+ */
292
+ async function probeWindowsHardware(run, options = {}) {
293
+ try {
294
+ return await viaPowershell(run, options.systemRoot);
295
+ } catch (powershellError) {
296
+ options.onFallback?.(`PowerShell probe failed (${powershellError instanceof Error ? powershellError.message : String(powershellError)}); trying wmic`);
297
+ }
298
+ try {
299
+ return await viaWmic(run);
300
+ } catch (wmicError) {
301
+ options.onFallback?.(`wmic probe failed (${wmicError instanceof Error ? wmicError.message : String(wmicError)}); hardware unknown`);
302
+ return {
303
+ gpu: null,
304
+ npu: null,
305
+ source: "none"
306
+ };
307
+ }
308
+ }
309
+ //#endregion
62
310
  //#region src/builtins/platform-probe/platform-scorer.ts
311
+ /** WMI can be slow on a cold machine, but never worth stalling boot for. */
312
+ var WINDOWS_PROBE_TIMEOUT_MS = 15e3;
63
313
  /**
64
314
  * Promisified `execFile`. Used across the scorer so every subprocess
65
315
  * spawn yields the event loop — critical at boot because a single sync
@@ -237,24 +487,25 @@ var PlatformScorer = class {
237
487
  };
238
488
  npu = { type: "apple-ane" };
239
489
  }
490
+ if (platform === "win32") {
491
+ const nvidia = await this.probeNvidiaSmi();
492
+ const win = await this.probeWindowsHardwareSafely();
493
+ gpu = nvidia ?? win.gpu;
494
+ npu = win.npu;
495
+ if (gpu) this.logger.info("Windows GPU detected", { meta: {
496
+ type: gpu.type,
497
+ name: gpu.name
498
+ } });
499
+ if (npu && win.source !== "none") this.logger.info("AMD XDNA NPU detected (present only)", { meta: {
500
+ type: npu.type,
501
+ name: npu.name,
502
+ generation: npu.generation,
503
+ source: win.source
504
+ } });
505
+ }
240
506
  if (platform === "linux") {
241
- try {
242
- const { stdout } = await execFileAsync("nvidia-smi", ["--query-gpu=name,memory.total", "--format=csv,noheader"], {
243
- encoding: "utf8",
244
- timeout: 5e3
245
- });
246
- const output = stdout.trim();
247
- if (output) {
248
- const [name, mem] = output.split(",").map((s) => s.trim());
249
- gpu = {
250
- type: "nvidia",
251
- name: name ?? "NVIDIA GPU",
252
- memoryMB: parseInt(mem ?? "0")
253
- };
254
- }
255
- } catch (err) {
256
- this.logger.debug("NVIDIA GPU detection failed", { meta: { error: require_dist.errMsg(err) } });
257
- }
507
+ const nvidia = await this.probeNvidiaSmi();
508
+ if (nvidia) gpu = nvidia;
258
509
  const listRenderNodes = () => {
259
510
  try {
260
511
  return node_fs.readdirSync("/dev/dri").filter((n) => n.startsWith("renderD")).map((n) => `/dev/dri/${n}`);
@@ -313,7 +564,7 @@ var PlatformScorer = class {
313
564
  } });
314
565
  }
315
566
  if (!npu) {
316
- const { classifyAmdNpu } = await Promise.resolve().then(() => require("../../amd-npu-B-QyyMyF.js"));
567
+ const { classifyAmdNpu } = await Promise.resolve().then(() => amd_npu_exports);
317
568
  npu = classifyAmdNpu({
318
569
  platform,
319
570
  arch,
@@ -348,6 +599,53 @@ var PlatformScorer = class {
348
599
  coral
349
600
  };
350
601
  }
602
+ /** NVIDIA's own tool: exact model and VRAM. Null when absent or failing. */
603
+ async probeNvidiaSmi() {
604
+ try {
605
+ const { stdout } = await execFileAsync("nvidia-smi", ["--query-gpu=name,memory.total", "--format=csv,noheader"], {
606
+ encoding: "utf8",
607
+ timeout: 5e3,
608
+ windowsHide: true
609
+ });
610
+ const output = stdout.trim();
611
+ if (!output) return null;
612
+ const [name, mem] = output.split(",").map((s) => s.trim());
613
+ return {
614
+ type: "nvidia",
615
+ name: name ?? "NVIDIA GPU",
616
+ memoryMB: parseInt(mem ?? "0")
617
+ };
618
+ } catch (err) {
619
+ this.logger.debug("NVIDIA GPU detection failed", { meta: { error: require_dist.errMsg(err) } });
620
+ return null;
621
+ }
622
+ }
623
+ /** Windows WMI probe; bounded, and a failure is a log line, never a boot failure. */
624
+ async probeWindowsHardwareSafely() {
625
+ const run = async (file, args) => {
626
+ const { stdout } = await execFileAsync(file, [...args], {
627
+ encoding: "utf8",
628
+ timeout: WINDOWS_PROBE_TIMEOUT_MS,
629
+ windowsHide: true,
630
+ maxBuffer: 4 * 1024 * 1024
631
+ });
632
+ return stdout;
633
+ };
634
+ const systemRoot = process.env["SystemRoot"];
635
+ try {
636
+ return await probeWindowsHardware(run, {
637
+ ...systemRoot === void 0 ? {} : { systemRoot },
638
+ onFallback: (reason) => this.logger.warn("Windows hardware probe degraded", { meta: { reason } })
639
+ });
640
+ } catch (err) {
641
+ this.logger.warn("Windows hardware probe failed — scoring as a CPU node", { meta: { error: require_dist.errMsg(err) } });
642
+ return {
643
+ gpu: null,
644
+ npu: null,
645
+ source: "none"
646
+ };
647
+ }
648
+ }
351
649
  /**
352
650
  * Locates a Python interpreter without importing any module.
353
651
  *