@camstack/core 0.1.34 → 0.1.35
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/auth/auth-manager.d.ts +8 -0
- package/dist/auth/auth-manager.d.ts.map +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +5 -2
- package/dist/builtins/local-auth/local-auth.addon.js.map +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +5 -2
- package/dist/builtins/local-auth/local-auth.addon.mjs.map +1 -1
- package/dist/builtins/platform-probe/hardware-encoder-probe.d.ts +14 -0
- package/dist/builtins/platform-probe/hardware-encoder-probe.d.ts.map +1 -0
- package/dist/builtins/platform-probe/index.d.ts +2 -0
- package/dist/builtins/platform-probe/index.d.ts.map +1 -1
- package/dist/builtins/platform-probe/index.js +198 -5
- package/dist/builtins/platform-probe/index.js.map +1 -1
- package/dist/builtins/platform-probe/index.mjs +198 -6
- package/dist/builtins/platform-probe/index.mjs.map +1 -1
- package/dist/index.js +280 -175
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +280 -175
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HardwareInfo, HardwareEncoders, IScopedLogger } from '@camstack/types';
|
|
2
|
+
export declare class HardwareEncoderProber {
|
|
3
|
+
private cached;
|
|
4
|
+
private inflight;
|
|
5
|
+
private readonly logger;
|
|
6
|
+
private readonly ffmpegPath;
|
|
7
|
+
constructor(logger: IScopedLogger, ffmpegPath?: string);
|
|
8
|
+
getCached(): HardwareEncoders | null;
|
|
9
|
+
probe(hardware: HardwareInfo, options?: {
|
|
10
|
+
readonly force?: boolean;
|
|
11
|
+
}): Promise<HardwareEncoders>;
|
|
12
|
+
private runProbe;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=hardware-encoder-probe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hardware-encoder-probe.d.ts","sourceRoot":"","sources":["../../../src/builtins/platform-probe/hardware-encoder-probe.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,YAAY,EAGZ,gBAAgB,EAChB,aAAa,EACd,MAAM,iBAAiB,CAAA;AA4ExB,qBAAa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,QAAQ,CAAyC;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;gBAEvB,MAAM,EAAE,aAAa,EAAE,UAAU,SAAW;IAKxD,SAAS,IAAI,gBAAgB,GAAG,IAAI;IAI9B,KAAK,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,GAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC;YAiB5F,QAAQ;CAuCvB"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { ProviderRegistration, BaseAddon } from '@camstack/types';
|
|
2
2
|
export { PlatformScorer } from './platform-scorer.js';
|
|
3
3
|
export { InferenceConfigResolver } from './inference-config-resolver.js';
|
|
4
|
+
export { HardwareEncoderProber } from './hardware-encoder-probe.js';
|
|
4
5
|
export declare class PlatformProbeNativeAddon extends BaseAddon {
|
|
5
6
|
private scorer;
|
|
7
|
+
private encoderProber;
|
|
6
8
|
private cachedCaps;
|
|
7
9
|
constructor();
|
|
8
10
|
protected onInitialize(): Promise<ProviderRegistration[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/builtins/platform-probe/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/builtins/platform-probe/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EACV,oBAAoB,EAQrB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,SAAS,EAAkD,MAAM,iBAAiB,CAAA;AAK3F,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAA;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAA;AAEnE,qBAAa,wBAAyB,SAAQ,SAAS;IACrD,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,aAAa,CAAqC;IAC1D,OAAO,CAAC,UAAU,CAAoC;;cAItC,YAAY,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC;cA0F/C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAK5C;AAED,eAAe,wBAAwB,CAAA"}
|
|
@@ -16,7 +16,7 @@ node_os = require_chunk.__toESM(node_os);
|
|
|
16
16
|
* entire Node process (WS handshakes, tRPC subscriptions, metrics — all
|
|
17
17
|
* stalled). Keeping it async turns the probe into a true background task.
|
|
18
18
|
*/
|
|
19
|
-
var execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
|
|
19
|
+
var execFileAsync$1 = (0, node_util.promisify)(node_child_process.execFile);
|
|
20
20
|
/** Minimal no-op logger for default parameter */
|
|
21
21
|
var noopLogger = {
|
|
22
22
|
debug() {},
|
|
@@ -44,7 +44,7 @@ async function getAvailableRAM_MB() {
|
|
|
44
44
|
const platform = node_os.platform();
|
|
45
45
|
try {
|
|
46
46
|
if (platform === "darwin") {
|
|
47
|
-
const { stdout: output } = await execFileAsync("vm_stat", [], {
|
|
47
|
+
const { stdout: output } = await execFileAsync$1("vm_stat", [], {
|
|
48
48
|
encoding: "utf8",
|
|
49
49
|
timeout: 3e3
|
|
50
50
|
});
|
|
@@ -169,7 +169,7 @@ var PlatformScorer = class {
|
|
|
169
169
|
npu = { type: "apple-ane" };
|
|
170
170
|
}
|
|
171
171
|
if (platform === "linux") try {
|
|
172
|
-
const { stdout } = await execFileAsync("nvidia-smi", ["--query-gpu=name,memory.total", "--format=csv,noheader"], {
|
|
172
|
+
const { stdout } = await execFileAsync$1("nvidia-smi", ["--query-gpu=name,memory.total", "--format=csv,noheader"], {
|
|
173
173
|
encoding: "utf8",
|
|
174
174
|
timeout: 5e3
|
|
175
175
|
});
|
|
@@ -230,7 +230,7 @@ var PlatformScorer = class {
|
|
|
230
230
|
async probePythonBackends() {
|
|
231
231
|
let pythonPath = null;
|
|
232
232
|
for (const cmd of ["python3", "python"]) try {
|
|
233
|
-
await execFileAsync(cmd, ["--version"], { timeout: 5e3 });
|
|
233
|
+
await execFileAsync$1(cmd, ["--version"], { timeout: 5e3 });
|
|
234
234
|
pythonPath = cmd;
|
|
235
235
|
break;
|
|
236
236
|
} catch (err) {
|
|
@@ -265,7 +265,7 @@ var PlatformScorer = class {
|
|
|
265
265
|
const probeStart = Date.now();
|
|
266
266
|
let available = false;
|
|
267
267
|
try {
|
|
268
|
-
await execFileAsync(pythonPath, ["-c", `import ${mod}`], { timeout: 3e4 });
|
|
268
|
+
await execFileAsync$1(pythonPath, ["-c", `import ${mod}`], { timeout: 3e4 });
|
|
269
269
|
available = true;
|
|
270
270
|
} catch (err) {
|
|
271
271
|
console.debug(`Python module "${mod}" not installed: ${(0, _camstack_types.errMsg)(err)}`);
|
|
@@ -459,15 +459,192 @@ var InferenceConfigResolver = class {
|
|
|
459
459
|
}
|
|
460
460
|
};
|
|
461
461
|
//#endregion
|
|
462
|
+
//#region src/builtins/platform-probe/hardware-encoder-probe.ts
|
|
463
|
+
var execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
|
|
464
|
+
function buildCandidates(hardware) {
|
|
465
|
+
const out = [];
|
|
466
|
+
if (hardware.platform === "darwin") {
|
|
467
|
+
out.push({
|
|
468
|
+
encoder: "h264_videotoolbox",
|
|
469
|
+
codec: "H264",
|
|
470
|
+
family: "videotoolbox"
|
|
471
|
+
});
|
|
472
|
+
out.push({
|
|
473
|
+
encoder: "hevc_videotoolbox",
|
|
474
|
+
codec: "H265",
|
|
475
|
+
family: "videotoolbox"
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
if (hardware.platform === "linux") {
|
|
479
|
+
if (hardware.gpu?.type === "nvidia") {
|
|
480
|
+
out.push({
|
|
481
|
+
encoder: "h264_nvenc",
|
|
482
|
+
codec: "H264",
|
|
483
|
+
family: "nvenc"
|
|
484
|
+
});
|
|
485
|
+
out.push({
|
|
486
|
+
encoder: "hevc_nvenc",
|
|
487
|
+
codec: "H265",
|
|
488
|
+
family: "nvenc"
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
if (hardware.gpu?.type === "intel" || hardware.gpu?.type === "amd" || !hardware.gpu) {
|
|
492
|
+
out.push({
|
|
493
|
+
encoder: "h264_vaapi",
|
|
494
|
+
codec: "H264",
|
|
495
|
+
family: "vaapi"
|
|
496
|
+
});
|
|
497
|
+
out.push({
|
|
498
|
+
encoder: "hevc_vaapi",
|
|
499
|
+
codec: "H265",
|
|
500
|
+
family: "vaapi"
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return out;
|
|
505
|
+
}
|
|
506
|
+
async function runTestEncode(candidate, opts) {
|
|
507
|
+
const ffmpeg = opts.ffmpegPath ?? "ffmpeg";
|
|
508
|
+
const timeout = opts.timeoutMs ?? 8e3;
|
|
509
|
+
const nullSink = node_os.platform() === "win32" ? "NUL" : "/dev/null";
|
|
510
|
+
const baseArgs = [
|
|
511
|
+
"-hide_banner",
|
|
512
|
+
"-loglevel",
|
|
513
|
+
"error"
|
|
514
|
+
];
|
|
515
|
+
const inArgs = [
|
|
516
|
+
"-f",
|
|
517
|
+
"lavfi",
|
|
518
|
+
"-i",
|
|
519
|
+
"testsrc=duration=0.04:size=16x16:rate=25"
|
|
520
|
+
];
|
|
521
|
+
let outArgs;
|
|
522
|
+
if (candidate.family === "vaapi") outArgs = [
|
|
523
|
+
"-vaapi_device",
|
|
524
|
+
"/dev/dri/renderD128",
|
|
525
|
+
"-vf",
|
|
526
|
+
"format=nv12,hwupload",
|
|
527
|
+
"-c:v",
|
|
528
|
+
candidate.encoder,
|
|
529
|
+
"-frames:v",
|
|
530
|
+
"1",
|
|
531
|
+
"-f",
|
|
532
|
+
"null",
|
|
533
|
+
nullSink
|
|
534
|
+
];
|
|
535
|
+
else outArgs = [
|
|
536
|
+
"-c:v",
|
|
537
|
+
candidate.encoder,
|
|
538
|
+
"-frames:v",
|
|
539
|
+
"1",
|
|
540
|
+
"-f",
|
|
541
|
+
"null",
|
|
542
|
+
nullSink
|
|
543
|
+
];
|
|
544
|
+
try {
|
|
545
|
+
await execFileAsync(ffmpeg, [
|
|
546
|
+
...baseArgs,
|
|
547
|
+
...inArgs,
|
|
548
|
+
...outArgs
|
|
549
|
+
], {
|
|
550
|
+
timeout,
|
|
551
|
+
encoding: "utf8"
|
|
552
|
+
});
|
|
553
|
+
return {
|
|
554
|
+
encoder: candidate.encoder,
|
|
555
|
+
codec: candidate.codec,
|
|
556
|
+
family: candidate.family,
|
|
557
|
+
available: true
|
|
558
|
+
};
|
|
559
|
+
} catch (err) {
|
|
560
|
+
return {
|
|
561
|
+
encoder: candidate.encoder,
|
|
562
|
+
codec: candidate.codec,
|
|
563
|
+
family: candidate.family,
|
|
564
|
+
available: false,
|
|
565
|
+
reason: (0, _camstack_types.errMsg)(err)
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
var HardwareEncoderProber = class {
|
|
570
|
+
cached = null;
|
|
571
|
+
inflight = null;
|
|
572
|
+
logger;
|
|
573
|
+
ffmpegPath;
|
|
574
|
+
constructor(logger, ffmpegPath = "ffmpeg") {
|
|
575
|
+
this.logger = logger;
|
|
576
|
+
this.ffmpegPath = ffmpegPath;
|
|
577
|
+
}
|
|
578
|
+
getCached() {
|
|
579
|
+
return this.cached;
|
|
580
|
+
}
|
|
581
|
+
async probe(hardware, options = {}) {
|
|
582
|
+
if (!options.force && this.cached) return this.cached;
|
|
583
|
+
if (this.inflight) return this.inflight;
|
|
584
|
+
this.inflight = this.runProbe(hardware).then((res) => {
|
|
585
|
+
this.cached = res;
|
|
586
|
+
this.inflight = null;
|
|
587
|
+
return res;
|
|
588
|
+
}).catch((err) => {
|
|
589
|
+
this.inflight = null;
|
|
590
|
+
throw err;
|
|
591
|
+
});
|
|
592
|
+
return this.inflight;
|
|
593
|
+
}
|
|
594
|
+
async runProbe(hardware) {
|
|
595
|
+
const candidates = buildCandidates(hardware);
|
|
596
|
+
const start = Date.now();
|
|
597
|
+
this.logger.info("Probing hardware encoders", { meta: {
|
|
598
|
+
platform: hardware.platform,
|
|
599
|
+
arch: hardware.arch,
|
|
600
|
+
candidates: candidates.length
|
|
601
|
+
} });
|
|
602
|
+
const probes = await Promise.all(candidates.map((c) => runTestEncode(c, { ffmpegPath: this.ffmpegPath })));
|
|
603
|
+
probes.push({
|
|
604
|
+
encoder: "libx264",
|
|
605
|
+
codec: "H264",
|
|
606
|
+
family: "software",
|
|
607
|
+
available: true
|
|
608
|
+
});
|
|
609
|
+
probes.push({
|
|
610
|
+
encoder: "libx265",
|
|
611
|
+
codec: "H265",
|
|
612
|
+
family: "software",
|
|
613
|
+
available: true
|
|
614
|
+
});
|
|
615
|
+
const pickDefault = (codec) => {
|
|
616
|
+
const hw = probes.find((p) => p.codec === codec && p.available && p.family !== "software");
|
|
617
|
+
if (hw) return hw.encoder;
|
|
618
|
+
return codec === "H264" ? "libx264" : "libx265";
|
|
619
|
+
};
|
|
620
|
+
const result = {
|
|
621
|
+
encoders: probes,
|
|
622
|
+
defaultH264: pickDefault("H264"),
|
|
623
|
+
defaultH265: pickDefault("H265"),
|
|
624
|
+
probedAt: Date.now()
|
|
625
|
+
};
|
|
626
|
+
const elapsed = Date.now() - start;
|
|
627
|
+
this.logger.info("Hardware encoder probe complete", { meta: {
|
|
628
|
+
elapsedMs: elapsed,
|
|
629
|
+
defaultH264: result.defaultH264,
|
|
630
|
+
defaultH265: result.defaultH265,
|
|
631
|
+
availableHw: probes.filter((p) => p.available && p.family !== "software").map((p) => p.encoder)
|
|
632
|
+
} });
|
|
633
|
+
return result;
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
//#endregion
|
|
462
637
|
//#region src/builtins/platform-probe/index.ts
|
|
463
638
|
var PlatformProbeNativeAddon = class extends _camstack_types.BaseAddon {
|
|
464
639
|
scorer = null;
|
|
640
|
+
encoderProber = null;
|
|
465
641
|
cachedCaps = null;
|
|
466
642
|
constructor() {
|
|
467
643
|
super({});
|
|
468
644
|
}
|
|
469
645
|
async onInitialize() {
|
|
470
646
|
this.scorer = new PlatformScorer(this.ctx.logger);
|
|
647
|
+
this.encoderProber = new HardwareEncoderProber(this.ctx.logger);
|
|
471
648
|
const emitPhase = (phase, payload) => {
|
|
472
649
|
this.ctx.eventBus?.emit({
|
|
473
650
|
id: `platform-probe-${phase}-${Date.now()}`,
|
|
@@ -521,16 +698,32 @@ var PlatformProbeNativeAddon = class extends _camstack_types.BaseAddon {
|
|
|
521
698
|
const hwaccel = this.ctx.kernel.hwaccel;
|
|
522
699
|
if (!hwaccel) return { preferred: [] };
|
|
523
700
|
return { preferred: (await hwaccel.resolve(input.prefer ?? null)).preferred };
|
|
701
|
+
},
|
|
702
|
+
getHardwareEncoders: async () => {
|
|
703
|
+
const prober = this.encoderProber;
|
|
704
|
+
if (!prober) throw new Error("Hardware encoder prober not initialized");
|
|
705
|
+
const cached = prober.getCached();
|
|
706
|
+
if (cached) return cached;
|
|
707
|
+
const caps = await getCaps();
|
|
708
|
+
return prober.probe(caps.hardware);
|
|
709
|
+
},
|
|
710
|
+
refreshHardwareEncoders: async () => {
|
|
711
|
+
const prober = this.encoderProber;
|
|
712
|
+
if (!prober) throw new Error("Hardware encoder prober not initialized");
|
|
713
|
+
const caps = await getCaps();
|
|
714
|
+
return prober.probe(caps.hardware, { force: true });
|
|
524
715
|
}
|
|
525
716
|
}
|
|
526
717
|
}];
|
|
527
718
|
}
|
|
528
719
|
async onShutdown() {
|
|
529
720
|
this.scorer = null;
|
|
721
|
+
this.encoderProber = null;
|
|
530
722
|
this.cachedCaps = null;
|
|
531
723
|
}
|
|
532
724
|
};
|
|
533
725
|
//#endregion
|
|
726
|
+
exports.HardwareEncoderProber = HardwareEncoderProber;
|
|
534
727
|
exports.InferenceConfigResolver = InferenceConfigResolver;
|
|
535
728
|
exports.PlatformProbeNativeAddon = PlatformProbeNativeAddon;
|
|
536
729
|
exports.default = PlatformProbeNativeAddon;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/builtins/platform-probe/platform-scorer.ts","../../../src/builtins/platform-probe/inference-config-resolver.ts","../../../src/builtins/platform-probe/index.ts"],"sourcesContent":["import * as os from 'node:os'\nimport { execFile } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport { errMsg } from '@camstack/types'\nimport type { HardwareInfo, GpuInfo, NpuInfo, PlatformScore, PlatformCapabilities, IScopedLogger } from '@camstack/types'\n\n/**\n * Promisified `execFile`. Used across the scorer so every subprocess\n * spawn yields the event loop — critical at boot because a single sync\n * spawn of a missing Python module hits its 30s timeout and freezes the\n * entire Node process (WS handshakes, tRPC subscriptions, metrics — all\n * stalled). Keeping it async turns the probe into a true background task.\n */\nconst execFileAsync = promisify(execFile)\n\n/** Minimal no-op logger for default parameter */\nconst noopLogger: IScopedLogger = {\n debug() {},\n info() {},\n warn() {},\n error() {},\n child() { return noopLogger },\n withTags() { return noopLogger },\n}\n\n/**\n * Get reliable \"available\" RAM in MB, cross-platform.\n * os.freemem() is unreliable on macOS (reports ~200MB on a 36GB system).\n * Uses systeminformation when available, falls back to native commands.\n */\nasync function getAvailableRAM_MB(): Promise<number> {\n try {\n const si = await import('systeminformation')\n const mem = await si.mem()\n return Math.round(mem.available / 1024 / 1024)\n } catch (err) {\n console.debug(`systeminformation not available, using platform-specific RAM probe: ${errMsg(err)}`)\n const platform = os.platform()\n try {\n if (platform === 'darwin') {\n // Parse vm_stat: (free + inactive + purgeable) * pageSize\n const { stdout: output } = await execFileAsync('vm_stat', [], { encoding: 'utf8', timeout: 3000 })\n const pageSize = parseInt(output.match(/page size of (\\d+)/)?.[1] ?? '16384')\n const free = parseInt(output.match(/Pages free:\\s+(\\d+)/)?.[1] ?? '0')\n const inactive = parseInt(output.match(/Pages inactive:\\s+(\\d+)/)?.[1] ?? '0')\n const purgeable = parseInt(output.match(/Pages purgeable:\\s+(\\d+)/)?.[1] ?? '0')\n return Math.round((free + inactive + purgeable) * pageSize / 1024 / 1024)\n } else if (platform === 'linux') {\n // Parse /proc/meminfo: MemAvailable\n const { readFileSync } = await import('node:fs')\n const meminfo = readFileSync('/proc/meminfo', 'utf8')\n const match = meminfo.match(/MemAvailable:\\s+(\\d+)\\s+kB/)\n if (match) return Math.round(parseInt(match[1]!) / 1024)\n }\n } catch (err) { console.debug(`RAM probe failed, using total RAM fallback: ${errMsg(err)}`) }\n // Ultimate fallback: total RAM (conservative but safe)\n return Math.round(os.totalmem() / 1024 / 1024)\n }\n}\n\nexport class PlatformScorer {\n private cached: PlatformCapabilities | null = null\n private readonly logger: IScopedLogger\n\n constructor(logger: IScopedLogger = noopLogger) {\n this.logger = logger\n }\n\n /**\n * Probe hardware + runtimes and score all backend combos.\n *\n * An optional `onPhase` callback is invoked at each step so consumers\n * (e.g. the platform-probe addon) can emit live progress events on\n * the event bus. The callback takes a phase id + a typed payload; all\n * phases fire in strict order: `started` → `hardware` → `node-backends`\n * → `python-backends` → `scored` → `done`. On failure, `error` fires\n * once with the exception. Cached after first call.\n */\n async probe(onPhase?: (phase: string, payload?: Record<string, unknown>) => void): Promise<PlatformCapabilities> {\n if (this.cached) return this.cached\n\n const start = Date.now()\n onPhase?.('started', {})\n this.logger.info('Probing hardware...')\n const hardware = await this.probeHardware()\n this.logger.info('Hardware detected', {\n meta: {\n platform: hardware.platform,\n arch: hardware.arch,\n cpuModel: hardware.cpuModel,\n cpuCores: hardware.cpuCores,\n ramGB: Math.round(hardware.totalRAM_MB / 1024),\n },\n })\n if (hardware.gpu) this.logger.info('GPU detected', { meta: { name: hardware.gpu.name } })\n if (hardware.npu) this.logger.info('NPU detected', { meta: { type: hardware.npu.type } })\n onPhase?.('hardware', { hardware })\n\n this.logger.info('Probing Node.js backends...')\n const nodeBackends = await this.probeNodeBackends()\n this.logger.info('Node backends detected', { meta: { backends: nodeBackends.map(b => b.id) } })\n onPhase?.('node-backends', { backends: nodeBackends })\n\n this.logger.info('Probing Python backends...')\n const pythonInfo = await this.probePythonBackends()\n if (pythonInfo.pythonPath) {\n this.logger.info('Python backends detected', {\n meta: {\n pythonPath: pythonInfo.pythonPath,\n backends: pythonInfo.backends.filter(b => b.available).map(b => b.id),\n },\n })\n } else {\n this.logger.info('Python: not found')\n }\n onPhase?.('python-backends', { pythonPath: pythonInfo.pythonPath, backends: pythonInfo.backends })\n\n const scores = this.scoreBackends(hardware, nodeBackends, pythonInfo.backends)\n const bestScore = scores.find(s => s.available) ?? scores[scores.length - 1]!\n\n const elapsed = Date.now() - start\n this.logger.info('Scoring complete', { meta: { elapsedMs: elapsed, combos: scores.length } })\n this.logger.info('Best backend selected', {\n meta: {\n runtime: bestScore.runtime,\n backend: bestScore.backend,\n format: bestScore.format,\n reason: bestScore.reason,\n score: bestScore.score,\n },\n })\n for (const s of scores) {\n this.logger.debug('Score entry', {\n meta: {\n available: s.available,\n runtime: s.runtime,\n backend: s.backend,\n format: s.format,\n score: s.score,\n reason: s.reason,\n },\n })\n }\n\n this.cached = {\n hardware,\n scores,\n bestScore,\n pythonPath: pythonInfo.pythonPath,\n }\n onPhase?.('scored', { scores, bestScore, elapsedMs: elapsed })\n onPhase?.('done', { capabilities: this.cached })\n return this.cached\n }\n\n async probeHardware(): Promise<HardwareInfo> {\n const platform = os.platform() as HardwareInfo['platform']\n const arch = os.arch() as HardwareInfo['arch']\n const cpus = os.cpus()\n const cpuModel = cpus[0]?.model ?? 'unknown'\n const cpuCores = cpus.length\n const totalRAM_MB = Math.round(os.totalmem() / 1024 / 1024)\n const availableRAM_MB = await getAvailableRAM_MB()\n this.logger.debug('RAM probed', { meta: { totalRAM_MB, availableRAM_MB } })\n\n let gpu: GpuInfo | null = null\n let npu: NpuInfo | null = null\n\n // macOS Apple Silicon\n if (platform === 'darwin' && arch === 'arm64') {\n gpu = { type: 'apple', name: 'Apple Silicon GPU' }\n npu = { type: 'apple-ane' }\n }\n\n // Linux NVIDIA\n if (platform === 'linux') {\n try {\n const { stdout } = await execFileAsync('nvidia-smi', ['--query-gpu=name,memory.total', '--format=csv,noheader'], {\n encoding: 'utf8', timeout: 5000,\n })\n const output = stdout.trim()\n if (output) {\n const [name, mem] = output.split(',').map(s => s.trim())\n gpu = {\n type: 'nvidia',\n name: name ?? 'NVIDIA GPU',\n memoryMB: parseInt(mem ?? '0'),\n }\n }\n } catch (err) { this.logger.debug('NVIDIA GPU detection failed', { meta: { error: errMsg(err) } }) }\n }\n\n return { platform, arch, cpuModel, cpuCores, totalRAM_MB, availableRAM_MB, gpu, npu }\n }\n\n private async probeNodeBackends(): Promise<Array<{ id: string; available: boolean }>> {\n const backends: Array<{ id: string; available: boolean }> = [\n { id: 'cpu', available: true },\n ]\n\n try {\n const ort = await import('onnxruntime-node') as { InferenceSession?: { getAvailableProviders?: () => string[] } }\n const providers: string[] = ort.InferenceSession?.getAvailableProviders?.() ?? []\n for (const p of providers) {\n const n = p.toLowerCase().replace('executionprovider', '')\n if (n === 'coreml') backends.push({ id: 'coreml', available: true })\n else if (n === 'cuda') backends.push({ id: 'cuda', available: true })\n else if (n === 'tensorrt') backends.push({ id: 'tensorrt', available: true })\n }\n } catch (err) { this.logger.debug('onnxruntime-node not available', { meta: { error: errMsg(err) } }) }\n\n // macOS always has CoreML potential\n if (os.platform() === 'darwin' && !backends.some(b => b.id === 'coreml')) {\n backends.push({ id: 'coreml', available: true })\n }\n\n return backends\n }\n\n private async probePythonBackends(): Promise<{ pythonPath: string | null; backends: Array<{ id: string; format: string; available: boolean }> }> {\n let pythonPath: string | null = null\n for (const cmd of ['python3', 'python']) {\n try {\n await execFileAsync(cmd, ['--version'], { timeout: 5000 })\n pythonPath = cmd\n break\n } catch (err) { console.debug(`Python command \"${cmd}\" not found: ${errMsg(err)}`) }\n }\n\n if (!pythonPath) return { pythonPath: null, backends: [] }\n\n const checks: Array<[string, string, string]> = [\n // [pythonModule, backendId, modelFormat]\n ['coremltools', 'coreml', 'coreml'],\n ['openvino.runtime', 'openvino', 'openvino'],\n ['torch', 'pytorch', 'onnx'],\n ['onnxruntime', 'onnx-py', 'onnx'],\n ]\n\n // Run all module probes in parallel. Each probe is independent and a\n // missing module spends 30s waiting on its own timeout — serial would\n // add up to ~50s of cumulative wait. Parallel caps it at the slowest\n // probe. The execFile promise yields the event loop, so WS handshakes\n // and tRPC calls continue to flow during the wait.\n const results = await Promise.all(checks.map(async ([mod, id, format]) => {\n const probeStart = Date.now()\n let available = false\n try {\n await execFileAsync(pythonPath!, ['-c', `import ${mod}`], { timeout: 30000 })\n available = true\n } catch (err) { console.debug(`Python module \"${mod}\" not installed: ${errMsg(err)}`) }\n const probeMs = Date.now() - probeStart\n this.logger.debug('Python module probed', { meta: { module: mod, available, probeMs } })\n return { mod, id, format, available }\n }))\n\n const backends: Array<{ id: string; format: string; available: boolean }> = []\n for (const r of results) {\n // Always show CoreML on macOS even if not installed\n if (r.id === 'coreml' && os.platform() === 'darwin') {\n backends.push({ id: r.id, format: r.format, available: r.available })\n } else if (r.available) {\n backends.push({ id: r.id, format: r.format, available: r.available })\n }\n }\n\n return { pythonPath, backends }\n }\n\n private scoreBackends(\n hardware: HardwareInfo,\n nodeBackends: Array<{ id: string; available: boolean }>,\n pythonBackends: Array<{ id: string; format: string; available: boolean }>,\n ): PlatformScore[] {\n const scores: PlatformScore[] = []\n\n // macOS Apple Silicon\n if (hardware.platform === 'darwin' && hardware.arch === 'arm64') {\n const pyCoreMl = pythonBackends.find(b => b.id === 'coreml')\n if (pyCoreMl) {\n scores.push({ runtime: 'python', backend: 'coreml', format: 'coreml', score: 95, reason: 'Apple Neural Engine (Python CoreML)', available: pyCoreMl.available })\n }\n const nodeCoreMl = nodeBackends.find(b => b.id === 'coreml')\n if (nodeCoreMl) {\n scores.push({ runtime: 'node', backend: 'coreml', format: 'onnx', score: 90, reason: 'CoreML via ONNX Runtime', available: nodeCoreMl.available })\n }\n }\n\n // NVIDIA\n if (hardware.gpu?.type === 'nvidia') {\n const tensorrt = nodeBackends.find(b => b.id === 'tensorrt')\n if (tensorrt) scores.push({ runtime: 'node', backend: 'tensorrt', format: 'onnx', score: 95, reason: 'NVIDIA TensorRT', available: true })\n const cuda = nodeBackends.find(b => b.id === 'cuda')\n if (cuda) scores.push({ runtime: 'node', backend: 'cuda', format: 'onnx', score: 85, reason: 'NVIDIA CUDA', available: true })\n }\n\n // Intel OpenVINO\n const openvino = pythonBackends.find(b => b.id === 'openvino')\n if (openvino) {\n const score = hardware.npu?.type === 'intel-npu' ? 90 : 80\n scores.push({ runtime: 'python', backend: 'openvino', format: 'openvino', score, reason: 'Intel OpenVINO', available: openvino.available })\n }\n\n // CPU fallbacks\n scores.push({ runtime: 'node', backend: 'cpu', format: 'onnx', score: 50, reason: 'CPU (ONNX Runtime Node)', available: true })\n const pyOnnx = pythonBackends.find(b => b.id === 'onnx-py')\n if (pyOnnx) {\n scores.push({ runtime: 'python', backend: 'cpu', format: 'onnx', score: 45, reason: 'CPU (Python ONNX)', available: pyOnnx.available })\n }\n\n return scores.sort((a, b) => b.score - a.score)\n }\n}\n","import type { PlatformScore, HardwareInfo, ModelRequirement, ResolvedInferenceConfig } from '@camstack/types'\n\nexport class InferenceConfigResolver {\n constructor(\n private readonly scores: readonly PlatformScore[],\n private readonly hardware: HardwareInfo,\n ) {}\n\n /**\n * Compute accuracy/backend weights based on available system RAM.\n * availableRAM_MB is now sourced from systeminformation (reliable cross-platform),\n * not os.freemem() which is broken on macOS.\n *\n * - > 16 GB available: prefer larger, more accurate models (accuracy 0.6, backend 0.4)\n * - > 8 GB available: balanced (accuracy 0.5, backend 0.5)\n * - <= 8 GB available: prefer speed (accuracy 0.4, backend 0.6)\n */\n private getWeights(): { accuracyWeight: number; backendWeight: number } {\n const ramMB = this.hardware.availableRAM_MB\n if (ramMB > 16_384) return { accuracyWeight: 0.6, backendWeight: 0.4 }\n if (ramMB > 8_192) return { accuracyWeight: 0.5, backendWeight: 0.5 }\n return { accuracyWeight: 0.4, backendWeight: 0.6 }\n }\n\n /**\n * Given an addon's model requirements, pick the best model + runtime + backend.\n *\n * Algorithm:\n * 1. Filter models by available RAM (minRAM_MB < 25% of available RAM)\n * 2. For each remaining model, find the best platform score whose format\n * is available in the model's formats\n * 3. Pick the model with the highest combined score using RAM-adaptive weights:\n * - High RAM (>16 GB): accuracy × 0.6 + backend × 0.4 (prefer accuracy)\n * - Mid RAM (>8 GB): accuracy × 0.5 + backend × 0.5 (balanced)\n * - Low RAM (<=8 GB): accuracy × 0.4 + backend × 0.6 (prefer speed)\n */\n resolve(requirements: readonly ModelRequirement[]): ResolvedInferenceConfig {\n if (requirements.length === 0) {\n return { modelId: '', runtime: 'node', backend: 'cpu', format: 'onnx', reason: 'No models declared' }\n }\n\n // Budget: 25% of available RAM (now reliable via systeminformation)\n const ramBudget = this.hardware.availableRAM_MB * 0.25\n const { accuracyWeight, backendWeight } = this.getWeights()\n\n console.log(`[InferenceConfigResolver] availableRAM: ${this.hardware.availableRAM_MB}MB, budget: ${Math.round(ramBudget)}MB, weights: accuracy=${accuracyWeight}, backend=${backendWeight}`)\n\n // Filter models that fit in memory\n const fits = requirements.filter(m => m.minRAM_MB < ramBudget)\n const candidates = fits.length > 0 ? fits : [requirements[0]!] // fallback to first/smallest\n\n console.log(`[InferenceConfigResolver] ${candidates.length}/${requirements.length} models fit RAM budget`)\n\n // For each model, find best compatible platform score\n let bestCombo: { model: ModelRequirement; score: PlatformScore; combined: number } | null = null\n\n for (const model of candidates) {\n for (const score of this.scores) {\n if (!score.available) continue\n if (!model.formats.includes(score.format)) continue\n\n const combined = model.accuracyScore * accuracyWeight + score.score * backendWeight\n\n if (!bestCombo || combined > bestCombo.combined) {\n console.log(`[InferenceConfigResolver] New best: ${model.modelId} (accuracy=${model.accuracyScore}) + ${score.backend}/${score.format} (score=${score.score}) → combined=${Math.round(combined)}`)\n bestCombo = { model, score, combined }\n }\n }\n }\n\n if (!bestCombo) {\n return {\n modelId: candidates[0]!.modelId,\n runtime: 'node',\n backend: 'cpu',\n format: 'onnx',\n reason: 'No compatible backend — CPU fallback',\n }\n }\n\n return {\n modelId: bestCombo.model.modelId,\n runtime: bestCombo.score.runtime,\n backend: bestCombo.score.backend,\n format: bestCombo.score.format,\n reason: `${bestCombo.model.name} on ${bestCombo.score.reason} (score: ${Math.round(bestCombo.combined)})`,\n }\n }\n}\n","/**\n * Platform-probe core builtin.\n *\n * Infra addon — probes hardware + inference runtimes on the local node and\n * exposes the result via the `platform-probe` capability (singleton per\n * node). Promoted from `@camstack/addon-platform-probe-native` (separate\n * npm package) to a core builtin in Phase B of the bundles refactor —\n * lives here because every node already pulls `@camstack/core`, removing\n * a redundant package from REQUIRED_PACKAGES / AGENT_PACKAGES.\n *\n * Inference addons consume the cap via `ctx.api.platformProbe.*`.\n */\nimport type {\n ProviderRegistration,\n IPlatformProbeProvider,\n PlatformCapabilities,\n HardwareInfo,\n ModelRequirement,\n ResolvedInferenceConfig,\n HwAccelBackend,\n} from '@camstack/types'\nimport { BaseAddon, platformProbeCapability, errMsg, EventCategory } from '@camstack/types'\nimport { PlatformScorer } from './platform-scorer.js'\nimport { InferenceConfigResolver } from './inference-config-resolver.js'\n\nexport { PlatformScorer } from './platform-scorer.js'\nexport { InferenceConfigResolver } from './inference-config-resolver.js'\n\nexport class PlatformProbeNativeAddon extends BaseAddon {\n private scorer: PlatformScorer | null = null\n private cachedCaps: PlatformCapabilities | null = null\n\n constructor() { super({}) }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n this.scorer = new PlatformScorer(this.ctx.logger)\n\n // Kick off the probe in the background — provider methods await the\n // promise so early consumers block until capabilities are ready.\n // Each phase emits a `platform-probe.phase` event on the bus so the\n // benchmark UI (and any other consumer) can render live progress via\n // `live.onEvent({ category: EventCategory.PlatformProbePhase })`.\n const emitPhase = (phase: string, payload?: Record<string, unknown>): void => {\n this.ctx.eventBus?.emit({\n id: `platform-probe-${phase}-${Date.now()}`,\n timestamp: new Date(),\n source: { type: 'core', id: 'platform-probe-native' },\n category: EventCategory.PlatformProbePhase,\n data: { type: 'platform-probe.phase', phase, ...(payload ?? {}) },\n })\n }\n\n const probePromise: Promise<PlatformCapabilities> = this.scorer\n .probe(emitPhase)\n .then((caps) => {\n this.cachedCaps = caps\n this.ctx.logger.info('Platform probe complete', {\n meta: {\n cpuModel: caps.hardware.cpuModel,\n arch: caps.hardware.arch,\n totalRAM_MB: caps.hardware.totalRAM_MB,\n gpu: caps.hardware.gpu?.name ?? 'none',\n bestReason: caps.bestScore.reason,\n bestScore: caps.bestScore.score,\n },\n })\n return caps\n })\n .catch((err: unknown) => {\n const msg = errMsg(err)\n this.ctx.logger.error('Platform probe failed', { meta: { error: msg } })\n emitPhase('error', { message: msg })\n throw err\n })\n\n const getCaps = async (): Promise<PlatformCapabilities> => {\n return this.cachedCaps ?? probePromise\n }\n\n const provider: IPlatformProbeProvider = {\n getCapabilities: async (): Promise<PlatformCapabilities> => {\n return getCaps()\n },\n getHardware: async (): Promise<HardwareInfo> => {\n const caps = await getCaps()\n return caps.hardware\n },\n resolveInferenceConfig: async (input: {\n readonly requirements: readonly ModelRequirement[]\n }): Promise<ResolvedInferenceConfig> => {\n const caps = await getCaps()\n const resolver = new InferenceConfigResolver(caps.scores, caps.hardware)\n return resolver.resolve(input.requirements)\n },\n resolveHwAccel: async (input: {\n readonly prefer?: string | null\n readonly nodeId?: string\n }): Promise<{ preferred: readonly string[] }> => {\n const hwaccel = this.ctx.kernel.hwaccel\n if (!hwaccel) {\n return { preferred: [] }\n }\n const res = await hwaccel.resolve((input.prefer as HwAccelBackend | 'none' | null) ?? null)\n return { preferred: res.preferred }\n },\n }\n\n return [{ capability: platformProbeCapability, provider }]\n }\n\n protected async onShutdown(): Promise<void> {\n this.scorer = null\n this.cachedCaps = null\n }\n}\n\nexport default PlatformProbeNativeAddon\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/builtins/platform-probe/platform-scorer.ts","../../../src/builtins/platform-probe/inference-config-resolver.ts","../../../src/builtins/platform-probe/hardware-encoder-probe.ts","../../../src/builtins/platform-probe/index.ts"],"sourcesContent":["import * as os from 'node:os'\nimport { execFile } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport { errMsg } from '@camstack/types'\nimport type { HardwareInfo, GpuInfo, NpuInfo, PlatformScore, PlatformCapabilities, IScopedLogger } from '@camstack/types'\n\n/**\n * Promisified `execFile`. Used across the scorer so every subprocess\n * spawn yields the event loop — critical at boot because a single sync\n * spawn of a missing Python module hits its 30s timeout and freezes the\n * entire Node process (WS handshakes, tRPC subscriptions, metrics — all\n * stalled). Keeping it async turns the probe into a true background task.\n */\nconst execFileAsync = promisify(execFile)\n\n/** Minimal no-op logger for default parameter */\nconst noopLogger: IScopedLogger = {\n debug() {},\n info() {},\n warn() {},\n error() {},\n child() { return noopLogger },\n withTags() { return noopLogger },\n}\n\n/**\n * Get reliable \"available\" RAM in MB, cross-platform.\n * os.freemem() is unreliable on macOS (reports ~200MB on a 36GB system).\n * Uses systeminformation when available, falls back to native commands.\n */\nasync function getAvailableRAM_MB(): Promise<number> {\n try {\n const si = await import('systeminformation')\n const mem = await si.mem()\n return Math.round(mem.available / 1024 / 1024)\n } catch (err) {\n console.debug(`systeminformation not available, using platform-specific RAM probe: ${errMsg(err)}`)\n const platform = os.platform()\n try {\n if (platform === 'darwin') {\n // Parse vm_stat: (free + inactive + purgeable) * pageSize\n const { stdout: output } = await execFileAsync('vm_stat', [], { encoding: 'utf8', timeout: 3000 })\n const pageSize = parseInt(output.match(/page size of (\\d+)/)?.[1] ?? '16384')\n const free = parseInt(output.match(/Pages free:\\s+(\\d+)/)?.[1] ?? '0')\n const inactive = parseInt(output.match(/Pages inactive:\\s+(\\d+)/)?.[1] ?? '0')\n const purgeable = parseInt(output.match(/Pages purgeable:\\s+(\\d+)/)?.[1] ?? '0')\n return Math.round((free + inactive + purgeable) * pageSize / 1024 / 1024)\n } else if (platform === 'linux') {\n // Parse /proc/meminfo: MemAvailable\n const { readFileSync } = await import('node:fs')\n const meminfo = readFileSync('/proc/meminfo', 'utf8')\n const match = meminfo.match(/MemAvailable:\\s+(\\d+)\\s+kB/)\n if (match) return Math.round(parseInt(match[1]!) / 1024)\n }\n } catch (err) { console.debug(`RAM probe failed, using total RAM fallback: ${errMsg(err)}`) }\n // Ultimate fallback: total RAM (conservative but safe)\n return Math.round(os.totalmem() / 1024 / 1024)\n }\n}\n\nexport class PlatformScorer {\n private cached: PlatformCapabilities | null = null\n private readonly logger: IScopedLogger\n\n constructor(logger: IScopedLogger = noopLogger) {\n this.logger = logger\n }\n\n /**\n * Probe hardware + runtimes and score all backend combos.\n *\n * An optional `onPhase` callback is invoked at each step so consumers\n * (e.g. the platform-probe addon) can emit live progress events on\n * the event bus. The callback takes a phase id + a typed payload; all\n * phases fire in strict order: `started` → `hardware` → `node-backends`\n * → `python-backends` → `scored` → `done`. On failure, `error` fires\n * once with the exception. Cached after first call.\n */\n async probe(onPhase?: (phase: string, payload?: Record<string, unknown>) => void): Promise<PlatformCapabilities> {\n if (this.cached) return this.cached\n\n const start = Date.now()\n onPhase?.('started', {})\n this.logger.info('Probing hardware...')\n const hardware = await this.probeHardware()\n this.logger.info('Hardware detected', {\n meta: {\n platform: hardware.platform,\n arch: hardware.arch,\n cpuModel: hardware.cpuModel,\n cpuCores: hardware.cpuCores,\n ramGB: Math.round(hardware.totalRAM_MB / 1024),\n },\n })\n if (hardware.gpu) this.logger.info('GPU detected', { meta: { name: hardware.gpu.name } })\n if (hardware.npu) this.logger.info('NPU detected', { meta: { type: hardware.npu.type } })\n onPhase?.('hardware', { hardware })\n\n this.logger.info('Probing Node.js backends...')\n const nodeBackends = await this.probeNodeBackends()\n this.logger.info('Node backends detected', { meta: { backends: nodeBackends.map(b => b.id) } })\n onPhase?.('node-backends', { backends: nodeBackends })\n\n this.logger.info('Probing Python backends...')\n const pythonInfo = await this.probePythonBackends()\n if (pythonInfo.pythonPath) {\n this.logger.info('Python backends detected', {\n meta: {\n pythonPath: pythonInfo.pythonPath,\n backends: pythonInfo.backends.filter(b => b.available).map(b => b.id),\n },\n })\n } else {\n this.logger.info('Python: not found')\n }\n onPhase?.('python-backends', { pythonPath: pythonInfo.pythonPath, backends: pythonInfo.backends })\n\n const scores = this.scoreBackends(hardware, nodeBackends, pythonInfo.backends)\n const bestScore = scores.find(s => s.available) ?? scores[scores.length - 1]!\n\n const elapsed = Date.now() - start\n this.logger.info('Scoring complete', { meta: { elapsedMs: elapsed, combos: scores.length } })\n this.logger.info('Best backend selected', {\n meta: {\n runtime: bestScore.runtime,\n backend: bestScore.backend,\n format: bestScore.format,\n reason: bestScore.reason,\n score: bestScore.score,\n },\n })\n for (const s of scores) {\n this.logger.debug('Score entry', {\n meta: {\n available: s.available,\n runtime: s.runtime,\n backend: s.backend,\n format: s.format,\n score: s.score,\n reason: s.reason,\n },\n })\n }\n\n this.cached = {\n hardware,\n scores,\n bestScore,\n pythonPath: pythonInfo.pythonPath,\n }\n onPhase?.('scored', { scores, bestScore, elapsedMs: elapsed })\n onPhase?.('done', { capabilities: this.cached })\n return this.cached\n }\n\n async probeHardware(): Promise<HardwareInfo> {\n const platform = os.platform() as HardwareInfo['platform']\n const arch = os.arch() as HardwareInfo['arch']\n const cpus = os.cpus()\n const cpuModel = cpus[0]?.model ?? 'unknown'\n const cpuCores = cpus.length\n const totalRAM_MB = Math.round(os.totalmem() / 1024 / 1024)\n const availableRAM_MB = await getAvailableRAM_MB()\n this.logger.debug('RAM probed', { meta: { totalRAM_MB, availableRAM_MB } })\n\n let gpu: GpuInfo | null = null\n let npu: NpuInfo | null = null\n\n // macOS Apple Silicon\n if (platform === 'darwin' && arch === 'arm64') {\n gpu = { type: 'apple', name: 'Apple Silicon GPU' }\n npu = { type: 'apple-ane' }\n }\n\n // Linux NVIDIA\n if (platform === 'linux') {\n try {\n const { stdout } = await execFileAsync('nvidia-smi', ['--query-gpu=name,memory.total', '--format=csv,noheader'], {\n encoding: 'utf8', timeout: 5000,\n })\n const output = stdout.trim()\n if (output) {\n const [name, mem] = output.split(',').map(s => s.trim())\n gpu = {\n type: 'nvidia',\n name: name ?? 'NVIDIA GPU',\n memoryMB: parseInt(mem ?? '0'),\n }\n }\n } catch (err) { this.logger.debug('NVIDIA GPU detection failed', { meta: { error: errMsg(err) } }) }\n }\n\n return { platform, arch, cpuModel, cpuCores, totalRAM_MB, availableRAM_MB, gpu, npu }\n }\n\n private async probeNodeBackends(): Promise<Array<{ id: string; available: boolean }>> {\n const backends: Array<{ id: string; available: boolean }> = [\n { id: 'cpu', available: true },\n ]\n\n try {\n const ort = await import('onnxruntime-node') as { InferenceSession?: { getAvailableProviders?: () => string[] } }\n const providers: string[] = ort.InferenceSession?.getAvailableProviders?.() ?? []\n for (const p of providers) {\n const n = p.toLowerCase().replace('executionprovider', '')\n if (n === 'coreml') backends.push({ id: 'coreml', available: true })\n else if (n === 'cuda') backends.push({ id: 'cuda', available: true })\n else if (n === 'tensorrt') backends.push({ id: 'tensorrt', available: true })\n }\n } catch (err) { this.logger.debug('onnxruntime-node not available', { meta: { error: errMsg(err) } }) }\n\n // macOS always has CoreML potential\n if (os.platform() === 'darwin' && !backends.some(b => b.id === 'coreml')) {\n backends.push({ id: 'coreml', available: true })\n }\n\n return backends\n }\n\n private async probePythonBackends(): Promise<{ pythonPath: string | null; backends: Array<{ id: string; format: string; available: boolean }> }> {\n let pythonPath: string | null = null\n for (const cmd of ['python3', 'python']) {\n try {\n await execFileAsync(cmd, ['--version'], { timeout: 5000 })\n pythonPath = cmd\n break\n } catch (err) { console.debug(`Python command \"${cmd}\" not found: ${errMsg(err)}`) }\n }\n\n if (!pythonPath) return { pythonPath: null, backends: [] }\n\n const checks: Array<[string, string, string]> = [\n // [pythonModule, backendId, modelFormat]\n ['coremltools', 'coreml', 'coreml'],\n ['openvino.runtime', 'openvino', 'openvino'],\n ['torch', 'pytorch', 'onnx'],\n ['onnxruntime', 'onnx-py', 'onnx'],\n ]\n\n // Run all module probes in parallel. Each probe is independent and a\n // missing module spends 30s waiting on its own timeout — serial would\n // add up to ~50s of cumulative wait. Parallel caps it at the slowest\n // probe. The execFile promise yields the event loop, so WS handshakes\n // and tRPC calls continue to flow during the wait.\n const results = await Promise.all(checks.map(async ([mod, id, format]) => {\n const probeStart = Date.now()\n let available = false\n try {\n await execFileAsync(pythonPath!, ['-c', `import ${mod}`], { timeout: 30000 })\n available = true\n } catch (err) { console.debug(`Python module \"${mod}\" not installed: ${errMsg(err)}`) }\n const probeMs = Date.now() - probeStart\n this.logger.debug('Python module probed', { meta: { module: mod, available, probeMs } })\n return { mod, id, format, available }\n }))\n\n const backends: Array<{ id: string; format: string; available: boolean }> = []\n for (const r of results) {\n // Always show CoreML on macOS even if not installed\n if (r.id === 'coreml' && os.platform() === 'darwin') {\n backends.push({ id: r.id, format: r.format, available: r.available })\n } else if (r.available) {\n backends.push({ id: r.id, format: r.format, available: r.available })\n }\n }\n\n return { pythonPath, backends }\n }\n\n private scoreBackends(\n hardware: HardwareInfo,\n nodeBackends: Array<{ id: string; available: boolean }>,\n pythonBackends: Array<{ id: string; format: string; available: boolean }>,\n ): PlatformScore[] {\n const scores: PlatformScore[] = []\n\n // macOS Apple Silicon\n if (hardware.platform === 'darwin' && hardware.arch === 'arm64') {\n const pyCoreMl = pythonBackends.find(b => b.id === 'coreml')\n if (pyCoreMl) {\n scores.push({ runtime: 'python', backend: 'coreml', format: 'coreml', score: 95, reason: 'Apple Neural Engine (Python CoreML)', available: pyCoreMl.available })\n }\n const nodeCoreMl = nodeBackends.find(b => b.id === 'coreml')\n if (nodeCoreMl) {\n scores.push({ runtime: 'node', backend: 'coreml', format: 'onnx', score: 90, reason: 'CoreML via ONNX Runtime', available: nodeCoreMl.available })\n }\n }\n\n // NVIDIA\n if (hardware.gpu?.type === 'nvidia') {\n const tensorrt = nodeBackends.find(b => b.id === 'tensorrt')\n if (tensorrt) scores.push({ runtime: 'node', backend: 'tensorrt', format: 'onnx', score: 95, reason: 'NVIDIA TensorRT', available: true })\n const cuda = nodeBackends.find(b => b.id === 'cuda')\n if (cuda) scores.push({ runtime: 'node', backend: 'cuda', format: 'onnx', score: 85, reason: 'NVIDIA CUDA', available: true })\n }\n\n // Intel OpenVINO\n const openvino = pythonBackends.find(b => b.id === 'openvino')\n if (openvino) {\n const score = hardware.npu?.type === 'intel-npu' ? 90 : 80\n scores.push({ runtime: 'python', backend: 'openvino', format: 'openvino', score, reason: 'Intel OpenVINO', available: openvino.available })\n }\n\n // CPU fallbacks\n scores.push({ runtime: 'node', backend: 'cpu', format: 'onnx', score: 50, reason: 'CPU (ONNX Runtime Node)', available: true })\n const pyOnnx = pythonBackends.find(b => b.id === 'onnx-py')\n if (pyOnnx) {\n scores.push({ runtime: 'python', backend: 'cpu', format: 'onnx', score: 45, reason: 'CPU (Python ONNX)', available: pyOnnx.available })\n }\n\n return scores.sort((a, b) => b.score - a.score)\n }\n}\n","import type { PlatformScore, HardwareInfo, ModelRequirement, ResolvedInferenceConfig } from '@camstack/types'\n\nexport class InferenceConfigResolver {\n constructor(\n private readonly scores: readonly PlatformScore[],\n private readonly hardware: HardwareInfo,\n ) {}\n\n /**\n * Compute accuracy/backend weights based on available system RAM.\n * availableRAM_MB is now sourced from systeminformation (reliable cross-platform),\n * not os.freemem() which is broken on macOS.\n *\n * - > 16 GB available: prefer larger, more accurate models (accuracy 0.6, backend 0.4)\n * - > 8 GB available: balanced (accuracy 0.5, backend 0.5)\n * - <= 8 GB available: prefer speed (accuracy 0.4, backend 0.6)\n */\n private getWeights(): { accuracyWeight: number; backendWeight: number } {\n const ramMB = this.hardware.availableRAM_MB\n if (ramMB > 16_384) return { accuracyWeight: 0.6, backendWeight: 0.4 }\n if (ramMB > 8_192) return { accuracyWeight: 0.5, backendWeight: 0.5 }\n return { accuracyWeight: 0.4, backendWeight: 0.6 }\n }\n\n /**\n * Given an addon's model requirements, pick the best model + runtime + backend.\n *\n * Algorithm:\n * 1. Filter models by available RAM (minRAM_MB < 25% of available RAM)\n * 2. For each remaining model, find the best platform score whose format\n * is available in the model's formats\n * 3. Pick the model with the highest combined score using RAM-adaptive weights:\n * - High RAM (>16 GB): accuracy × 0.6 + backend × 0.4 (prefer accuracy)\n * - Mid RAM (>8 GB): accuracy × 0.5 + backend × 0.5 (balanced)\n * - Low RAM (<=8 GB): accuracy × 0.4 + backend × 0.6 (prefer speed)\n */\n resolve(requirements: readonly ModelRequirement[]): ResolvedInferenceConfig {\n if (requirements.length === 0) {\n return { modelId: '', runtime: 'node', backend: 'cpu', format: 'onnx', reason: 'No models declared' }\n }\n\n // Budget: 25% of available RAM (now reliable via systeminformation)\n const ramBudget = this.hardware.availableRAM_MB * 0.25\n const { accuracyWeight, backendWeight } = this.getWeights()\n\n console.log(`[InferenceConfigResolver] availableRAM: ${this.hardware.availableRAM_MB}MB, budget: ${Math.round(ramBudget)}MB, weights: accuracy=${accuracyWeight}, backend=${backendWeight}`)\n\n // Filter models that fit in memory\n const fits = requirements.filter(m => m.minRAM_MB < ramBudget)\n const candidates = fits.length > 0 ? fits : [requirements[0]!] // fallback to first/smallest\n\n console.log(`[InferenceConfigResolver] ${candidates.length}/${requirements.length} models fit RAM budget`)\n\n // For each model, find best compatible platform score\n let bestCombo: { model: ModelRequirement; score: PlatformScore; combined: number } | null = null\n\n for (const model of candidates) {\n for (const score of this.scores) {\n if (!score.available) continue\n if (!model.formats.includes(score.format)) continue\n\n const combined = model.accuracyScore * accuracyWeight + score.score * backendWeight\n\n if (!bestCombo || combined > bestCombo.combined) {\n console.log(`[InferenceConfigResolver] New best: ${model.modelId} (accuracy=${model.accuracyScore}) + ${score.backend}/${score.format} (score=${score.score}) → combined=${Math.round(combined)}`)\n bestCombo = { model, score, combined }\n }\n }\n }\n\n if (!bestCombo) {\n return {\n modelId: candidates[0]!.modelId,\n runtime: 'node',\n backend: 'cpu',\n format: 'onnx',\n reason: 'No compatible backend — CPU fallback',\n }\n }\n\n return {\n modelId: bestCombo.model.modelId,\n runtime: bestCombo.score.runtime,\n backend: bestCombo.score.backend,\n format: bestCombo.score.format,\n reason: `${bestCombo.model.name} on ${bestCombo.score.reason} (score: ${Math.round(bestCombo.combined)})`,\n }\n }\n}\n","import * as os from 'node:os'\nimport { execFile } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport { errMsg } from '@camstack/types'\nimport type {\n HardwareInfo,\n HardwareEncoderId,\n HardwareEncoderProbe,\n HardwareEncoders,\n IScopedLogger,\n} from '@camstack/types'\n\nconst execFileAsync = promisify(execFile)\n\ninterface CandidateEncoder {\n readonly encoder: HardwareEncoderId\n readonly codec: 'H264' | 'H265'\n readonly family: HardwareEncoderProbe['family']\n}\n\nfunction buildCandidates(hardware: HardwareInfo): readonly CandidateEncoder[] {\n const out: CandidateEncoder[] = []\n if (hardware.platform === 'darwin') {\n out.push({ encoder: 'h264_videotoolbox', codec: 'H264', family: 'videotoolbox' })\n out.push({ encoder: 'hevc_videotoolbox', codec: 'H265', family: 'videotoolbox' })\n }\n if (hardware.platform === 'linux') {\n if (hardware.gpu?.type === 'nvidia') {\n out.push({ encoder: 'h264_nvenc', codec: 'H264', family: 'nvenc' })\n out.push({ encoder: 'hevc_nvenc', codec: 'H265', family: 'nvenc' })\n }\n if (hardware.gpu?.type === 'intel' || hardware.gpu?.type === 'amd' || !hardware.gpu) {\n out.push({ encoder: 'h264_vaapi', codec: 'H264', family: 'vaapi' })\n out.push({ encoder: 'hevc_vaapi', codec: 'H265', family: 'vaapi' })\n }\n }\n return out\n}\n\ninterface ProbeOptions {\n readonly ffmpegPath?: string\n readonly timeoutMs?: number\n}\n\nasync function runTestEncode(\n candidate: CandidateEncoder,\n opts: ProbeOptions,\n): Promise<HardwareEncoderProbe> {\n const ffmpeg = opts.ffmpegPath ?? 'ffmpeg'\n const timeout = opts.timeoutMs ?? 8_000\n const nullSink = os.platform() === 'win32' ? 'NUL' : '/dev/null'\n\n const baseArgs: string[] = ['-hide_banner', '-loglevel', 'error']\n const inArgs: string[] = ['-f', 'lavfi', '-i', 'testsrc=duration=0.04:size=16x16:rate=25']\n let outArgs: string[]\n\n if (candidate.family === 'vaapi') {\n outArgs = [\n '-vaapi_device', '/dev/dri/renderD128',\n '-vf', 'format=nv12,hwupload',\n '-c:v', candidate.encoder,\n '-frames:v', '1',\n '-f', 'null', nullSink,\n ]\n } else {\n outArgs = [\n '-c:v', candidate.encoder,\n '-frames:v', '1',\n '-f', 'null', nullSink,\n ]\n }\n\n try {\n await execFileAsync(ffmpeg, [...baseArgs, ...inArgs, ...outArgs], { timeout, encoding: 'utf8' })\n return { encoder: candidate.encoder, codec: candidate.codec, family: candidate.family, available: true }\n } catch (err) {\n return {\n encoder: candidate.encoder,\n codec: candidate.codec,\n family: candidate.family,\n available: false,\n reason: errMsg(err),\n }\n }\n}\n\nexport class HardwareEncoderProber {\n private cached: HardwareEncoders | null = null\n private inflight: Promise<HardwareEncoders> | null = null\n private readonly logger: IScopedLogger\n private readonly ffmpegPath: string\n\n constructor(logger: IScopedLogger, ffmpegPath = 'ffmpeg') {\n this.logger = logger\n this.ffmpegPath = ffmpegPath\n }\n\n getCached(): HardwareEncoders | null {\n return this.cached\n }\n\n async probe(hardware: HardwareInfo, options: { readonly force?: boolean } = {}): Promise<HardwareEncoders> {\n if (!options.force && this.cached) return this.cached\n if (this.inflight) return this.inflight\n\n this.inflight = this.runProbe(hardware)\n .then((res) => {\n this.cached = res\n this.inflight = null\n return res\n })\n .catch((err: unknown) => {\n this.inflight = null\n throw err\n })\n return this.inflight\n }\n\n private async runProbe(hardware: HardwareInfo): Promise<HardwareEncoders> {\n const candidates = buildCandidates(hardware)\n const start = Date.now()\n this.logger.info('Probing hardware encoders', {\n meta: { platform: hardware.platform, arch: hardware.arch, candidates: candidates.length },\n })\n\n const probes = await Promise.all(\n candidates.map((c) => runTestEncode(c, { ffmpegPath: this.ffmpegPath })),\n )\n\n probes.push({ encoder: 'libx264', codec: 'H264', family: 'software', available: true })\n probes.push({ encoder: 'libx265', codec: 'H265', family: 'software', available: true })\n\n const pickDefault = (codec: 'H264' | 'H265'): HardwareEncoderId => {\n const hw = probes.find((p) => p.codec === codec && p.available && p.family !== 'software')\n if (hw) return hw.encoder\n return codec === 'H264' ? 'libx264' : 'libx265'\n }\n\n const result: HardwareEncoders = {\n encoders: probes,\n defaultH264: pickDefault('H264'),\n defaultH265: pickDefault('H265'),\n probedAt: Date.now(),\n }\n\n const elapsed = Date.now() - start\n this.logger.info('Hardware encoder probe complete', {\n meta: {\n elapsedMs: elapsed,\n defaultH264: result.defaultH264,\n defaultH265: result.defaultH265,\n availableHw: probes.filter((p) => p.available && p.family !== 'software').map((p) => p.encoder),\n },\n })\n\n return result\n }\n}\n","/**\n * Platform-probe core builtin.\n *\n * Infra addon — probes hardware + inference runtimes on the local node and\n * exposes the result via the `platform-probe` capability (singleton per\n * node).\n */\nimport type {\n ProviderRegistration,\n IPlatformProbeProvider,\n PlatformCapabilities,\n HardwareInfo,\n HardwareEncoders,\n ModelRequirement,\n ResolvedInferenceConfig,\n HwAccelBackend,\n} from '@camstack/types'\nimport { BaseAddon, platformProbeCapability, errMsg, EventCategory } from '@camstack/types'\nimport { PlatformScorer } from './platform-scorer.js'\nimport { InferenceConfigResolver } from './inference-config-resolver.js'\nimport { HardwareEncoderProber } from './hardware-encoder-probe.js'\n\nexport { PlatformScorer } from './platform-scorer.js'\nexport { InferenceConfigResolver } from './inference-config-resolver.js'\nexport { HardwareEncoderProber } from './hardware-encoder-probe.js'\n\nexport class PlatformProbeNativeAddon extends BaseAddon {\n private scorer: PlatformScorer | null = null\n private encoderProber: HardwareEncoderProber | null = null\n private cachedCaps: PlatformCapabilities | null = null\n\n constructor() { super({}) }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n this.scorer = new PlatformScorer(this.ctx.logger)\n this.encoderProber = new HardwareEncoderProber(this.ctx.logger)\n\n const emitPhase = (phase: string, payload?: Record<string, unknown>): void => {\n this.ctx.eventBus?.emit({\n id: `platform-probe-${phase}-${Date.now()}`,\n timestamp: new Date(),\n source: { type: 'core', id: 'platform-probe-native' },\n category: EventCategory.PlatformProbePhase,\n data: { type: 'platform-probe.phase', phase, ...(payload ?? {}) },\n })\n }\n\n const probePromise: Promise<PlatformCapabilities> = this.scorer\n .probe(emitPhase)\n .then((caps) => {\n this.cachedCaps = caps\n this.ctx.logger.info('Platform probe complete', {\n meta: {\n cpuModel: caps.hardware.cpuModel,\n arch: caps.hardware.arch,\n totalRAM_MB: caps.hardware.totalRAM_MB,\n gpu: caps.hardware.gpu?.name ?? 'none',\n bestReason: caps.bestScore.reason,\n bestScore: caps.bestScore.score,\n },\n })\n return caps\n })\n .catch((err: unknown) => {\n const msg = errMsg(err)\n this.ctx.logger.error('Platform probe failed', { meta: { error: msg } })\n emitPhase('error', { message: msg })\n throw err\n })\n\n const getCaps = async (): Promise<PlatformCapabilities> => {\n return this.cachedCaps ?? probePromise\n }\n\n const provider: IPlatformProbeProvider = {\n getCapabilities: async (): Promise<PlatformCapabilities> => {\n return getCaps()\n },\n getHardware: async (): Promise<HardwareInfo> => {\n const caps = await getCaps()\n return caps.hardware\n },\n resolveInferenceConfig: async (input: {\n readonly requirements: readonly ModelRequirement[]\n }): Promise<ResolvedInferenceConfig> => {\n const caps = await getCaps()\n const resolver = new InferenceConfigResolver(caps.scores, caps.hardware)\n return resolver.resolve(input.requirements)\n },\n resolveHwAccel: async (input: {\n readonly prefer?: string | null\n readonly nodeId?: string\n }): Promise<{ preferred: readonly string[] }> => {\n const hwaccel = this.ctx.kernel.hwaccel\n if (!hwaccel) {\n return { preferred: [] }\n }\n const res = await hwaccel.resolve((input.prefer as HwAccelBackend | 'none' | null) ?? null)\n return { preferred: res.preferred }\n },\n getHardwareEncoders: async (): Promise<HardwareEncoders> => {\n const prober = this.encoderProber\n if (!prober) {\n throw new Error('Hardware encoder prober not initialized')\n }\n const cached = prober.getCached()\n if (cached) return cached\n const caps = await getCaps()\n return prober.probe(caps.hardware)\n },\n refreshHardwareEncoders: async (): Promise<HardwareEncoders> => {\n const prober = this.encoderProber\n if (!prober) {\n throw new Error('Hardware encoder prober not initialized')\n }\n const caps = await getCaps()\n return prober.probe(caps.hardware, { force: true })\n },\n }\n\n return [{ capability: platformProbeCapability, provider }]\n }\n\n protected async onShutdown(): Promise<void> {\n this.scorer = null\n this.encoderProber = null\n this.cachedCaps = null\n }\n}\n\nexport default PlatformProbeNativeAddon\n"],"mappings":"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"}
|