@camstack/system 1.2.416 → 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.
- package/dist/addon-runner.js +9969 -27
- package/dist/addon-runner.mjs +9930 -9
- package/dist/addon-utils.js +19 -20
- package/dist/addon-utils.mjs +1 -2
- package/dist/auth/auth-audit-log.d.ts +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/autotrack/index.js +1 -1
- package/dist/builtins/autotrack/index.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
- package/dist/builtins/calendars/calendars.addon.js +1 -1
- package/dist/builtins/calendars/calendars.addon.mjs +1 -1
- package/dist/builtins/camera-grid/index.js +1 -1
- package/dist/builtins/camera-grid/index.mjs +1 -1
- package/dist/builtins/composer/composer.addon.js +2 -2
- package/dist/builtins/composer/composer.addon.mjs +2 -2
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
- package/dist/builtins/device-manager/device-manager.addon.js +2 -2
- package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/keychain/index.js +7 -9
- package/dist/builtins/keychain/index.mjs +7 -9
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +5 -3
- package/dist/builtins/local-auth/local-auth.addon.mjs +5 -3
- package/dist/builtins/local-network/local-network.addon.js +2 -2
- package/dist/builtins/local-network/local-network.addon.mjs +2 -2
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +102 -10
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +92 -4
- package/dist/builtins/platform-probe/amd-npu.d.ts +6 -0
- package/dist/builtins/platform-probe/index.js +317 -19
- package/dist/builtins/platform-probe/index.mjs +318 -20
- package/dist/builtins/platform-probe/platform-scorer.d.ts +4 -0
- package/dist/builtins/platform-probe/windows-hardware.d.ts +77 -0
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
- package/dist/builtins/storage-orchestrator/index.mjs +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +83 -3
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +79 -3
- package/dist/builtins/system-config/system-config.addon.js +2 -2
- package/dist/builtins/system-config/system-config.addon.mjs +2 -2
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{composition-sources-DEZqZLPT.mjs → composition-sources-0cPw9E0M.mjs} +1 -1
- package/dist/{composition-sources-DBcOVpGn.js → composition-sources-BVjuMvpb.js} +1 -1
- package/dist/{constants-C4jZO_qO.js → constants-Aj0OBqlt.js} +1 -1
- package/dist/{constants-BSBtmzm3.mjs → constants-C8T7u1_Z.mjs} +1 -1
- package/dist/{event-loop-stall-monitor-LBZskC1S.mjs → custom-action-registry-BQnbN0DP.mjs} +57 -2
- package/dist/{event-loop-stall-monitor-E2hFvhDg.js → custom-action-registry-Bf_YnagS.js} +64 -3
- package/dist/{dist-DxiDI8hZ.js → dist-DVUg8e1T.js} +56 -182
- package/dist/{dist-DwE9yHdk.mjs → dist-DlmdESNT.mjs} +57 -153
- package/dist/index.js +22901 -10154
- package/dist/index.mjs +22616 -9869
- package/dist/{jwt-signing-key-DLbjTIuA.mjs → jwt-signing-key-CGg61A5v.mjs} +1 -1
- package/dist/{jwt-signing-key-CsC-FxSm.js → jwt-signing-key-Jyu81jZR.js} +1 -1
- package/dist/kernel/moleculer/broker-factory.d.ts +2 -4
- package/dist/kernel/transport/local-child-registry.d.ts +1 -1
- package/dist/logging/log-redactor.d.ts +1 -1
- package/dist/{resource-monitor-B2WZ8_q9.mjs → resource-monitor-Di9daRwI.mjs} +1 -1
- package/dist/{resource-monitor-BgWGRxN5.js → resource-monitor-mio9dSCT.js} +5 -5
- package/dist/{restore-archive-BtKjaVua.js → restore-archive-Bq5PpQ-H.js} +1 -1
- package/dist/{restore-archive-CImzbuRo.mjs → restore-archive-DVo5ooBt.mjs} +1 -1
- package/dist/{retired-settings-keys-Cl1tnkmq.js → retired-settings-keys-CihMX6h9.js} +1 -1
- package/dist/{retired-settings-keys-D1WsCdUY.mjs → retired-settings-keys-k_XuC-Mt.mjs} +1 -1
- package/dist/{tls-C0TRPfqc.js → tls-BgImdxCO.js} +4 -11
- package/dist/{tls-D-2dPY-5.mjs → tls-CaOLZ09W.mjs} +5 -12
- package/package.json +2 -2
- package/dist/amd-npu-B-QyyMyF.js +0 -53
- package/dist/amd-npu-Db_SoSuL.mjs +0 -52
- package/dist/child-cap-dispatch-B87B0FrC.js +0 -13986
- package/dist/child-cap-dispatch-CmfT-OR2.mjs +0 -12916
- package/dist/custom-action-registry-B4Cf4O_X.mjs +0 -57
- package/dist/custom-action-registry-acBY3mHA.js +0 -62
- package/dist/hub-cap-forward-CDiN1p98.mjs +0 -78
- package/dist/hub-cap-forward-W8oEm4ME.js +0 -101
- package/dist/proc-stat-CbC55Kqi.mjs +0 -92
- package/dist/proc-stat-DL-WUOhl.js +0 -127
|
@@ -3,8 +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-
|
|
7
|
-
const require_proc_stat = require("../../proc-stat-DL-WUOhl.js");
|
|
6
|
+
const require_dist = require("../../dist-DVUg8e1T.js");
|
|
8
7
|
let node_fs_promises = require("node:fs/promises");
|
|
9
8
|
let node_os = require("node:os");
|
|
10
9
|
node_os = require_chunk.__toESM(node_os);
|
|
@@ -2126,7 +2125,7 @@ var NativeMetricsProvider = class {
|
|
|
2126
2125
|
return getCpuTemperatureInternal();
|
|
2127
2126
|
}
|
|
2128
2127
|
async getProcessStats(input) {
|
|
2129
|
-
const { getPidStats } = await Promise.resolve().then(() => require("../../resource-monitor-
|
|
2128
|
+
const { getPidStats } = await Promise.resolve().then(() => require("../../resource-monitor-mio9dSCT.js")).then((n) => n.resource_monitor_exports);
|
|
2130
2129
|
const raw = await getPidStats(input.pids);
|
|
2131
2130
|
const result = [];
|
|
2132
2131
|
for (const [pid, s] of raw) result.push({
|
|
@@ -2462,6 +2461,95 @@ function execAsync(cmd, args, timeoutMs = 5e3) {
|
|
|
2462
2461
|
});
|
|
2463
2462
|
});
|
|
2464
2463
|
}
|
|
2464
|
+
/**
|
|
2465
|
+
* Parse one `/proc/<pid>/stat` line. `null` for a line that does not parse —
|
|
2466
|
+
* a process that exited between the readdir and the read leaves a truncated
|
|
2467
|
+
* or empty file, and that is normal, not an error.
|
|
2468
|
+
*/
|
|
2469
|
+
function parseProcStat(line) {
|
|
2470
|
+
const close = line.lastIndexOf(")");
|
|
2471
|
+
const open = line.indexOf("(");
|
|
2472
|
+
if (close < 0 || open < 0 || close < open) return null;
|
|
2473
|
+
const pid = Number(line.slice(0, open).trim());
|
|
2474
|
+
const comm = line.slice(open + 1, close);
|
|
2475
|
+
const rest = line.slice(close + 1).trim().split(/\s+/);
|
|
2476
|
+
const state = rest[0];
|
|
2477
|
+
const ppid = Number(rest[1]);
|
|
2478
|
+
const pgid = Number(rest[2]);
|
|
2479
|
+
const utime = Number(rest[11]);
|
|
2480
|
+
const stime = Number(rest[12]);
|
|
2481
|
+
const startTicks = Number(rest[19]);
|
|
2482
|
+
const rssPages = Number(rest[21]);
|
|
2483
|
+
if (state === void 0 || state === "") return null;
|
|
2484
|
+
if (![
|
|
2485
|
+
pid,
|
|
2486
|
+
ppid,
|
|
2487
|
+
pgid,
|
|
2488
|
+
utime,
|
|
2489
|
+
stime,
|
|
2490
|
+
startTicks,
|
|
2491
|
+
rssPages
|
|
2492
|
+
].every(Number.isFinite)) return null;
|
|
2493
|
+
return {
|
|
2494
|
+
pid,
|
|
2495
|
+
comm,
|
|
2496
|
+
state,
|
|
2497
|
+
ppid,
|
|
2498
|
+
pgid,
|
|
2499
|
+
ticks: utime + stime,
|
|
2500
|
+
startTicks,
|
|
2501
|
+
rssPages
|
|
2502
|
+
};
|
|
2503
|
+
}
|
|
2504
|
+
function statusKb(status, key) {
|
|
2505
|
+
const match = status.match(new RegExp(`^${key}:\\s+(\\d+)`, "m"));
|
|
2506
|
+
return match ? parseInt(match[1], 10) * 1024 : void 0;
|
|
2507
|
+
}
|
|
2508
|
+
/** Read `VmRSS` / `RssAnon` / `RssFile` (kB in the file) out of `/proc/<pid>/status`. */
|
|
2509
|
+
function parseProcStatusRss(status) {
|
|
2510
|
+
return {
|
|
2511
|
+
rssBytes: statusKb(status, "VmRSS"),
|
|
2512
|
+
privateBytes: statusKb(status, "RssAnon"),
|
|
2513
|
+
sharedBytes: statusKb(status, "RssFile")
|
|
2514
|
+
};
|
|
2515
|
+
}
|
|
2516
|
+
/** Seconds the process has been alive — `ps etime`, whole seconds. */
|
|
2517
|
+
function elapsedSecSince(input) {
|
|
2518
|
+
return Math.max(0, Math.floor(input.uptimeSec - input.startTicks / 100));
|
|
2519
|
+
}
|
|
2520
|
+
/**
|
|
2521
|
+
* `ps pcpu`: CPU time over elapsed time, as a percentage with one decimal.
|
|
2522
|
+
*
|
|
2523
|
+
* This is deliberately the LIFETIME average `ps` prints, not an instantaneous
|
|
2524
|
+
* rate: the columns fed from it have always shown that quantity, and the
|
|
2525
|
+
* instantaneous split is a different number with a different owner
|
|
2526
|
+
* (`cpuMainPercent` / `cpuGcPercent` from `thread-cpu-sampler.ts`). A process
|
|
2527
|
+
* with no elapsed window yet reports 0, never a division by zero.
|
|
2528
|
+
*/
|
|
2529
|
+
function lifetimeCpuPercent(input) {
|
|
2530
|
+
const elapsedSec = input.uptimeSec - input.startTicks / 100;
|
|
2531
|
+
if (!(elapsedSec > 0)) return 0;
|
|
2532
|
+
const cpuSec = input.ticks / 100;
|
|
2533
|
+
return Math.round(cpuSec / elapsedSec * 1e3) / 10;
|
|
2534
|
+
}
|
|
2535
|
+
var PID_RE = /^\d+$/;
|
|
2536
|
+
var nodeProcReader = {
|
|
2537
|
+
listPids: async () => (await (0, node_fs_promises.readdir)("/proc")).filter((entry) => PID_RE.test(entry)).map((entry) => Number(entry)),
|
|
2538
|
+
readStat: (pid) => (0, node_fs_promises.readFile)(`/proc/${pid}/stat`, "latin1"),
|
|
2539
|
+
readStatus: (pid) => (0, node_fs_promises.readFile)(`/proc/${pid}/status`, "latin1"),
|
|
2540
|
+
readCmdline: (pid) => (0, node_fs_promises.readFile)(`/proc/${pid}/cmdline`, "latin1"),
|
|
2541
|
+
readUptimeSec: async () => {
|
|
2542
|
+
const [first] = (await (0, node_fs_promises.readFile)("/proc/uptime", "latin1")).trim().split(/\s+/);
|
|
2543
|
+
const seconds = Number(first);
|
|
2544
|
+
if (!Number.isFinite(seconds)) throw new Error(`/proc/uptime unreadable: ${String(first)}`);
|
|
2545
|
+
return seconds;
|
|
2546
|
+
}
|
|
2547
|
+
};
|
|
2548
|
+
/** `ps command=`: argv joined by spaces, or `[comm]` when there is no argv. */
|
|
2549
|
+
function commandFromCmdline(cmdline, comm) {
|
|
2550
|
+
const joined = cmdline.split("\0").filter((part) => part.length > 0).join(" ");
|
|
2551
|
+
return joined.length > 0 ? joined : `[${comm}]`;
|
|
2552
|
+
}
|
|
2465
2553
|
//#endregion
|
|
2466
2554
|
//#region src/builtins/native-metrics/proc-process-table.ts
|
|
2467
2555
|
/**
|
|
@@ -2493,28 +2581,28 @@ async function readProcRow(pid, uptimeSec, reader) {
|
|
|
2493
2581
|
} catch {
|
|
2494
2582
|
return null;
|
|
2495
2583
|
}
|
|
2496
|
-
const parsed =
|
|
2584
|
+
const parsed = parseProcStat(stat);
|
|
2497
2585
|
if (parsed === null) return null;
|
|
2498
|
-
const rss =
|
|
2586
|
+
const rss = parseProcStatusRss(status);
|
|
2499
2587
|
return {
|
|
2500
2588
|
pid: parsed.pid,
|
|
2501
2589
|
ppid: parsed.ppid,
|
|
2502
2590
|
pgid: parsed.pgid,
|
|
2503
|
-
cpuPercent:
|
|
2591
|
+
cpuPercent: lifetimeCpuPercent({
|
|
2504
2592
|
ticks: parsed.ticks,
|
|
2505
2593
|
startTicks: parsed.startTicks,
|
|
2506
2594
|
uptimeSec
|
|
2507
2595
|
}),
|
|
2508
2596
|
memoryRssBytes: rss.rssBytes ?? 0,
|
|
2509
|
-
uptimeSec:
|
|
2597
|
+
uptimeSec: elapsedSecSince({
|
|
2510
2598
|
startTicks: parsed.startTicks,
|
|
2511
2599
|
uptimeSec
|
|
2512
2600
|
}),
|
|
2513
|
-
command:
|
|
2601
|
+
command: commandFromCmdline(cmdline, parsed.comm)
|
|
2514
2602
|
};
|
|
2515
2603
|
}
|
|
2516
2604
|
/** Every process in `/proc`, as the rows `ps -eo …` would have printed. */
|
|
2517
|
-
async function readProcProcessTable(reader =
|
|
2605
|
+
async function readProcProcessTable(reader = nodeProcReader) {
|
|
2518
2606
|
const [pids, uptimeSec] = await Promise.all([reader.listPids(), reader.readUptimeSec()]);
|
|
2519
2607
|
return (await Promise.all(pids.map((pid) => readProcRow(pid, uptimeSec, reader)))).filter((row) => row !== null);
|
|
2520
2608
|
}
|
|
@@ -2569,7 +2657,7 @@ async function spawnPsTable() {
|
|
|
2569
2657
|
}
|
|
2570
2658
|
var defaultDeps = {
|
|
2571
2659
|
platform: (0, node_os.platform)(),
|
|
2572
|
-
reader:
|
|
2660
|
+
reader: nodeProcReader,
|
|
2573
2661
|
spawnPs: spawnPsTable
|
|
2574
2662
|
};
|
|
2575
2663
|
/**
|
|
@@ -3481,3 +3569,7 @@ var NativeMetricsAddon = class NativeMetricsAddon extends require_dist.BaseAddon
|
|
|
3481
3569
|
//#endregion
|
|
3482
3570
|
exports.NativeMetricsProvider = NativeMetricsProvider;
|
|
3483
3571
|
exports.default = NativeMetricsAddon;
|
|
3572
|
+
exports.lifetimeCpuPercent = lifetimeCpuPercent;
|
|
3573
|
+
exports.nodeProcReader = nodeProcReader;
|
|
3574
|
+
exports.parseProcStat = parseProcStat;
|
|
3575
|
+
exports.parseProcStatusRss = parseProcStatusRss;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as parseProcStat, i as nodeProcReader, n as elapsedSecSince, o as parseProcStatusRss, r as lifetimeCpuPercent, t as commandFromCmdline } from "../../proc-stat-CbC55Kqi.mjs";
|
|
1
|
+
import { Mt as foldSnapshotByFunction, cr as BaseAddon, ei as EventCategory, hn as reducePoints, nn as metricsProviderCapability, sr as errMsg, vn as resolveBucketMs, wr as createEvent } from "../../dist-DlmdESNT.mjs";
|
|
3
2
|
import { access, readFile, readdir } from "node:fs/promises";
|
|
4
3
|
import * as os from "node:os";
|
|
5
4
|
import { platform } from "node:os";
|
|
@@ -2121,7 +2120,7 @@ var NativeMetricsProvider = class {
|
|
|
2121
2120
|
return getCpuTemperatureInternal();
|
|
2122
2121
|
}
|
|
2123
2122
|
async getProcessStats(input) {
|
|
2124
|
-
const { getPidStats } = await import("../../resource-monitor-
|
|
2123
|
+
const { getPidStats } = await import("../../resource-monitor-Di9daRwI.mjs").then((n) => n.r);
|
|
2125
2124
|
const raw = await getPidStats(input.pids);
|
|
2126
2125
|
const result = [];
|
|
2127
2126
|
for (const [pid, s] of raw) result.push({
|
|
@@ -2457,6 +2456,95 @@ function execAsync(cmd, args, timeoutMs = 5e3) {
|
|
|
2457
2456
|
});
|
|
2458
2457
|
});
|
|
2459
2458
|
}
|
|
2459
|
+
/**
|
|
2460
|
+
* Parse one `/proc/<pid>/stat` line. `null` for a line that does not parse —
|
|
2461
|
+
* a process that exited between the readdir and the read leaves a truncated
|
|
2462
|
+
* or empty file, and that is normal, not an error.
|
|
2463
|
+
*/
|
|
2464
|
+
function parseProcStat(line) {
|
|
2465
|
+
const close = line.lastIndexOf(")");
|
|
2466
|
+
const open = line.indexOf("(");
|
|
2467
|
+
if (close < 0 || open < 0 || close < open) return null;
|
|
2468
|
+
const pid = Number(line.slice(0, open).trim());
|
|
2469
|
+
const comm = line.slice(open + 1, close);
|
|
2470
|
+
const rest = line.slice(close + 1).trim().split(/\s+/);
|
|
2471
|
+
const state = rest[0];
|
|
2472
|
+
const ppid = Number(rest[1]);
|
|
2473
|
+
const pgid = Number(rest[2]);
|
|
2474
|
+
const utime = Number(rest[11]);
|
|
2475
|
+
const stime = Number(rest[12]);
|
|
2476
|
+
const startTicks = Number(rest[19]);
|
|
2477
|
+
const rssPages = Number(rest[21]);
|
|
2478
|
+
if (state === void 0 || state === "") return null;
|
|
2479
|
+
if (![
|
|
2480
|
+
pid,
|
|
2481
|
+
ppid,
|
|
2482
|
+
pgid,
|
|
2483
|
+
utime,
|
|
2484
|
+
stime,
|
|
2485
|
+
startTicks,
|
|
2486
|
+
rssPages
|
|
2487
|
+
].every(Number.isFinite)) return null;
|
|
2488
|
+
return {
|
|
2489
|
+
pid,
|
|
2490
|
+
comm,
|
|
2491
|
+
state,
|
|
2492
|
+
ppid,
|
|
2493
|
+
pgid,
|
|
2494
|
+
ticks: utime + stime,
|
|
2495
|
+
startTicks,
|
|
2496
|
+
rssPages
|
|
2497
|
+
};
|
|
2498
|
+
}
|
|
2499
|
+
function statusKb(status, key) {
|
|
2500
|
+
const match = status.match(new RegExp(`^${key}:\\s+(\\d+)`, "m"));
|
|
2501
|
+
return match ? parseInt(match[1], 10) * 1024 : void 0;
|
|
2502
|
+
}
|
|
2503
|
+
/** Read `VmRSS` / `RssAnon` / `RssFile` (kB in the file) out of `/proc/<pid>/status`. */
|
|
2504
|
+
function parseProcStatusRss(status) {
|
|
2505
|
+
return {
|
|
2506
|
+
rssBytes: statusKb(status, "VmRSS"),
|
|
2507
|
+
privateBytes: statusKb(status, "RssAnon"),
|
|
2508
|
+
sharedBytes: statusKb(status, "RssFile")
|
|
2509
|
+
};
|
|
2510
|
+
}
|
|
2511
|
+
/** Seconds the process has been alive — `ps etime`, whole seconds. */
|
|
2512
|
+
function elapsedSecSince(input) {
|
|
2513
|
+
return Math.max(0, Math.floor(input.uptimeSec - input.startTicks / 100));
|
|
2514
|
+
}
|
|
2515
|
+
/**
|
|
2516
|
+
* `ps pcpu`: CPU time over elapsed time, as a percentage with one decimal.
|
|
2517
|
+
*
|
|
2518
|
+
* This is deliberately the LIFETIME average `ps` prints, not an instantaneous
|
|
2519
|
+
* rate: the columns fed from it have always shown that quantity, and the
|
|
2520
|
+
* instantaneous split is a different number with a different owner
|
|
2521
|
+
* (`cpuMainPercent` / `cpuGcPercent` from `thread-cpu-sampler.ts`). A process
|
|
2522
|
+
* with no elapsed window yet reports 0, never a division by zero.
|
|
2523
|
+
*/
|
|
2524
|
+
function lifetimeCpuPercent(input) {
|
|
2525
|
+
const elapsedSec = input.uptimeSec - input.startTicks / 100;
|
|
2526
|
+
if (!(elapsedSec > 0)) return 0;
|
|
2527
|
+
const cpuSec = input.ticks / 100;
|
|
2528
|
+
return Math.round(cpuSec / elapsedSec * 1e3) / 10;
|
|
2529
|
+
}
|
|
2530
|
+
var PID_RE = /^\d+$/;
|
|
2531
|
+
var nodeProcReader = {
|
|
2532
|
+
listPids: async () => (await readdir("/proc")).filter((entry) => PID_RE.test(entry)).map((entry) => Number(entry)),
|
|
2533
|
+
readStat: (pid) => readFile(`/proc/${pid}/stat`, "latin1"),
|
|
2534
|
+
readStatus: (pid) => readFile(`/proc/${pid}/status`, "latin1"),
|
|
2535
|
+
readCmdline: (pid) => readFile(`/proc/${pid}/cmdline`, "latin1"),
|
|
2536
|
+
readUptimeSec: async () => {
|
|
2537
|
+
const [first] = (await readFile("/proc/uptime", "latin1")).trim().split(/\s+/);
|
|
2538
|
+
const seconds = Number(first);
|
|
2539
|
+
if (!Number.isFinite(seconds)) throw new Error(`/proc/uptime unreadable: ${String(first)}`);
|
|
2540
|
+
return seconds;
|
|
2541
|
+
}
|
|
2542
|
+
};
|
|
2543
|
+
/** `ps command=`: argv joined by spaces, or `[comm]` when there is no argv. */
|
|
2544
|
+
function commandFromCmdline(cmdline, comm) {
|
|
2545
|
+
const joined = cmdline.split("\0").filter((part) => part.length > 0).join(" ");
|
|
2546
|
+
return joined.length > 0 ? joined : `[${comm}]`;
|
|
2547
|
+
}
|
|
2460
2548
|
//#endregion
|
|
2461
2549
|
//#region src/builtins/native-metrics/proc-process-table.ts
|
|
2462
2550
|
/**
|
|
@@ -3474,4 +3562,4 @@ var NativeMetricsAddon = class NativeMetricsAddon extends BaseAddon {
|
|
|
3474
3562
|
}
|
|
3475
3563
|
};
|
|
3476
3564
|
//#endregion
|
|
3477
|
-
export { NativeMetricsAddon as default,
|
|
3565
|
+
export { NativeMetricsProvider as a, NativeMetricsAddon as default, parseProcStatusRss as i, nodeProcReader as n, parseProcStat as r, lifetimeCpuPercent as t };
|
|
@@ -27,3 +27,9 @@ export interface AmdNpuContext {
|
|
|
27
27
|
* generation so the UI and fleet census can show the hardware.
|
|
28
28
|
*/
|
|
29
29
|
export declare function classifyAmdNpu(ctx: AmdNpuContext, fsx: AmdNpuFs): NpuInfo | null;
|
|
30
|
+
/**
|
|
31
|
+
* The NPU descriptor for an AMD PCI device id (`0x1502` or `1502`). Shared by
|
|
32
|
+
* the Linux sysfs probe and the Windows PnP probe so both name the same
|
|
33
|
+
* silicon the same way.
|
|
34
|
+
*/
|
|
35
|
+
export declare function xdnaInfoForDevice(rawDeviceId: string): NpuInfo;
|
|
@@ -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-
|
|
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
|
-
|
|
242
|
-
|
|
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(() =>
|
|
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
|
*
|