@camstack/core 0.1.33 → 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.
Files changed (42) hide show
  1. package/dist/auth/api-key-manager.d.ts +2 -2
  2. package/dist/auth/api-key-manager.d.ts.map +1 -1
  3. package/dist/auth/auth-manager.d.ts +70 -3
  4. package/dist/auth/auth-manager.d.ts.map +1 -1
  5. package/dist/auth/totp-manager.d.ts +53 -0
  6. package/dist/auth/totp-manager.d.ts.map +1 -0
  7. package/dist/auth/user-manager.d.ts +3 -3
  8. package/dist/auth/user-manager.d.ts.map +1 -1
  9. package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.d.ts.map +1 -1
  10. package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.js +29 -9
  11. package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.js.map +1 -1
  12. package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.mjs +29 -9
  13. package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.mjs.map +1 -1
  14. package/dist/builtins/local-auth/auth-schema.d.ts +14 -0
  15. package/dist/builtins/local-auth/auth-schema.d.ts.map +1 -1
  16. package/dist/builtins/local-auth/local-auth.addon.d.ts +1 -0
  17. package/dist/builtins/local-auth/local-auth.addon.d.ts.map +1 -1
  18. package/dist/builtins/local-auth/local-auth.addon.js +1014 -22
  19. package/dist/builtins/local-auth/local-auth.addon.js.map +1 -1
  20. package/dist/builtins/local-auth/local-auth.addon.mjs +1025 -33
  21. package/dist/builtins/local-auth/local-auth.addon.mjs.map +1 -1
  22. package/dist/builtins/platform-probe/hardware-encoder-probe.d.ts +14 -0
  23. package/dist/builtins/platform-probe/hardware-encoder-probe.d.ts.map +1 -0
  24. package/dist/builtins/platform-probe/index.d.ts +2 -0
  25. package/dist/builtins/platform-probe/index.d.ts.map +1 -1
  26. package/dist/builtins/platform-probe/index.js +198 -5
  27. package/dist/builtins/platform-probe/index.js.map +1 -1
  28. package/dist/builtins/platform-probe/index.mjs +198 -6
  29. package/dist/builtins/platform-probe/index.mjs.map +1 -1
  30. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +8 -0
  31. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts.map +1 -1
  32. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +27 -21
  33. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js.map +1 -1
  34. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +27 -21
  35. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs.map +1 -1
  36. package/dist/index.d.ts +1 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +558 -94
  39. package/dist/index.js.map +1 -1
  40. package/dist/index.mjs +558 -94
  41. package/dist/index.mjs.map +1 -1
  42. package/package.json +2 -1
@@ -10,7 +10,7 @@ import * as os from "node:os";
10
10
  * entire Node process (WS handshakes, tRPC subscriptions, metrics — all
11
11
  * stalled). Keeping it async turns the probe into a true background task.
12
12
  */
13
- var execFileAsync = promisify(execFile);
13
+ var execFileAsync$1 = promisify(execFile);
14
14
  /** Minimal no-op logger for default parameter */
15
15
  var noopLogger = {
16
16
  debug() {},
@@ -38,7 +38,7 @@ async function getAvailableRAM_MB() {
38
38
  const platform = os.platform();
39
39
  try {
40
40
  if (platform === "darwin") {
41
- const { stdout: output } = await execFileAsync("vm_stat", [], {
41
+ const { stdout: output } = await execFileAsync$1("vm_stat", [], {
42
42
  encoding: "utf8",
43
43
  timeout: 3e3
44
44
  });
@@ -163,7 +163,7 @@ var PlatformScorer = class {
163
163
  npu = { type: "apple-ane" };
164
164
  }
165
165
  if (platform === "linux") try {
166
- const { stdout } = await execFileAsync("nvidia-smi", ["--query-gpu=name,memory.total", "--format=csv,noheader"], {
166
+ const { stdout } = await execFileAsync$1("nvidia-smi", ["--query-gpu=name,memory.total", "--format=csv,noheader"], {
167
167
  encoding: "utf8",
168
168
  timeout: 5e3
169
169
  });
@@ -224,7 +224,7 @@ var PlatformScorer = class {
224
224
  async probePythonBackends() {
225
225
  let pythonPath = null;
226
226
  for (const cmd of ["python3", "python"]) try {
227
- await execFileAsync(cmd, ["--version"], { timeout: 5e3 });
227
+ await execFileAsync$1(cmd, ["--version"], { timeout: 5e3 });
228
228
  pythonPath = cmd;
229
229
  break;
230
230
  } catch (err) {
@@ -259,7 +259,7 @@ var PlatformScorer = class {
259
259
  const probeStart = Date.now();
260
260
  let available = false;
261
261
  try {
262
- await execFileAsync(pythonPath, ["-c", `import ${mod}`], { timeout: 3e4 });
262
+ await execFileAsync$1(pythonPath, ["-c", `import ${mod}`], { timeout: 3e4 });
263
263
  available = true;
264
264
  } catch (err) {
265
265
  console.debug(`Python module "${mod}" not installed: ${errMsg(err)}`);
@@ -453,15 +453,192 @@ var InferenceConfigResolver = class {
453
453
  }
454
454
  };
455
455
  //#endregion
456
+ //#region src/builtins/platform-probe/hardware-encoder-probe.ts
457
+ var execFileAsync = promisify(execFile);
458
+ function buildCandidates(hardware) {
459
+ const out = [];
460
+ if (hardware.platform === "darwin") {
461
+ out.push({
462
+ encoder: "h264_videotoolbox",
463
+ codec: "H264",
464
+ family: "videotoolbox"
465
+ });
466
+ out.push({
467
+ encoder: "hevc_videotoolbox",
468
+ codec: "H265",
469
+ family: "videotoolbox"
470
+ });
471
+ }
472
+ if (hardware.platform === "linux") {
473
+ if (hardware.gpu?.type === "nvidia") {
474
+ out.push({
475
+ encoder: "h264_nvenc",
476
+ codec: "H264",
477
+ family: "nvenc"
478
+ });
479
+ out.push({
480
+ encoder: "hevc_nvenc",
481
+ codec: "H265",
482
+ family: "nvenc"
483
+ });
484
+ }
485
+ if (hardware.gpu?.type === "intel" || hardware.gpu?.type === "amd" || !hardware.gpu) {
486
+ out.push({
487
+ encoder: "h264_vaapi",
488
+ codec: "H264",
489
+ family: "vaapi"
490
+ });
491
+ out.push({
492
+ encoder: "hevc_vaapi",
493
+ codec: "H265",
494
+ family: "vaapi"
495
+ });
496
+ }
497
+ }
498
+ return out;
499
+ }
500
+ async function runTestEncode(candidate, opts) {
501
+ const ffmpeg = opts.ffmpegPath ?? "ffmpeg";
502
+ const timeout = opts.timeoutMs ?? 8e3;
503
+ const nullSink = os.platform() === "win32" ? "NUL" : "/dev/null";
504
+ const baseArgs = [
505
+ "-hide_banner",
506
+ "-loglevel",
507
+ "error"
508
+ ];
509
+ const inArgs = [
510
+ "-f",
511
+ "lavfi",
512
+ "-i",
513
+ "testsrc=duration=0.04:size=16x16:rate=25"
514
+ ];
515
+ let outArgs;
516
+ if (candidate.family === "vaapi") outArgs = [
517
+ "-vaapi_device",
518
+ "/dev/dri/renderD128",
519
+ "-vf",
520
+ "format=nv12,hwupload",
521
+ "-c:v",
522
+ candidate.encoder,
523
+ "-frames:v",
524
+ "1",
525
+ "-f",
526
+ "null",
527
+ nullSink
528
+ ];
529
+ else outArgs = [
530
+ "-c:v",
531
+ candidate.encoder,
532
+ "-frames:v",
533
+ "1",
534
+ "-f",
535
+ "null",
536
+ nullSink
537
+ ];
538
+ try {
539
+ await execFileAsync(ffmpeg, [
540
+ ...baseArgs,
541
+ ...inArgs,
542
+ ...outArgs
543
+ ], {
544
+ timeout,
545
+ encoding: "utf8"
546
+ });
547
+ return {
548
+ encoder: candidate.encoder,
549
+ codec: candidate.codec,
550
+ family: candidate.family,
551
+ available: true
552
+ };
553
+ } catch (err) {
554
+ return {
555
+ encoder: candidate.encoder,
556
+ codec: candidate.codec,
557
+ family: candidate.family,
558
+ available: false,
559
+ reason: errMsg(err)
560
+ };
561
+ }
562
+ }
563
+ var HardwareEncoderProber = class {
564
+ cached = null;
565
+ inflight = null;
566
+ logger;
567
+ ffmpegPath;
568
+ constructor(logger, ffmpegPath = "ffmpeg") {
569
+ this.logger = logger;
570
+ this.ffmpegPath = ffmpegPath;
571
+ }
572
+ getCached() {
573
+ return this.cached;
574
+ }
575
+ async probe(hardware, options = {}) {
576
+ if (!options.force && this.cached) return this.cached;
577
+ if (this.inflight) return this.inflight;
578
+ this.inflight = this.runProbe(hardware).then((res) => {
579
+ this.cached = res;
580
+ this.inflight = null;
581
+ return res;
582
+ }).catch((err) => {
583
+ this.inflight = null;
584
+ throw err;
585
+ });
586
+ return this.inflight;
587
+ }
588
+ async runProbe(hardware) {
589
+ const candidates = buildCandidates(hardware);
590
+ const start = Date.now();
591
+ this.logger.info("Probing hardware encoders", { meta: {
592
+ platform: hardware.platform,
593
+ arch: hardware.arch,
594
+ candidates: candidates.length
595
+ } });
596
+ const probes = await Promise.all(candidates.map((c) => runTestEncode(c, { ffmpegPath: this.ffmpegPath })));
597
+ probes.push({
598
+ encoder: "libx264",
599
+ codec: "H264",
600
+ family: "software",
601
+ available: true
602
+ });
603
+ probes.push({
604
+ encoder: "libx265",
605
+ codec: "H265",
606
+ family: "software",
607
+ available: true
608
+ });
609
+ const pickDefault = (codec) => {
610
+ const hw = probes.find((p) => p.codec === codec && p.available && p.family !== "software");
611
+ if (hw) return hw.encoder;
612
+ return codec === "H264" ? "libx264" : "libx265";
613
+ };
614
+ const result = {
615
+ encoders: probes,
616
+ defaultH264: pickDefault("H264"),
617
+ defaultH265: pickDefault("H265"),
618
+ probedAt: Date.now()
619
+ };
620
+ const elapsed = Date.now() - start;
621
+ this.logger.info("Hardware encoder probe complete", { meta: {
622
+ elapsedMs: elapsed,
623
+ defaultH264: result.defaultH264,
624
+ defaultH265: result.defaultH265,
625
+ availableHw: probes.filter((p) => p.available && p.family !== "software").map((p) => p.encoder)
626
+ } });
627
+ return result;
628
+ }
629
+ };
630
+ //#endregion
456
631
  //#region src/builtins/platform-probe/index.ts
457
632
  var PlatformProbeNativeAddon = class extends BaseAddon {
458
633
  scorer = null;
634
+ encoderProber = null;
459
635
  cachedCaps = null;
460
636
  constructor() {
461
637
  super({});
462
638
  }
463
639
  async onInitialize() {
464
640
  this.scorer = new PlatformScorer(this.ctx.logger);
641
+ this.encoderProber = new HardwareEncoderProber(this.ctx.logger);
465
642
  const emitPhase = (phase, payload) => {
466
643
  this.ctx.eventBus?.emit({
467
644
  id: `platform-probe-${phase}-${Date.now()}`,
@@ -515,16 +692,31 @@ var PlatformProbeNativeAddon = class extends BaseAddon {
515
692
  const hwaccel = this.ctx.kernel.hwaccel;
516
693
  if (!hwaccel) return { preferred: [] };
517
694
  return { preferred: (await hwaccel.resolve(input.prefer ?? null)).preferred };
695
+ },
696
+ getHardwareEncoders: async () => {
697
+ const prober = this.encoderProber;
698
+ if (!prober) throw new Error("Hardware encoder prober not initialized");
699
+ const cached = prober.getCached();
700
+ if (cached) return cached;
701
+ const caps = await getCaps();
702
+ return prober.probe(caps.hardware);
703
+ },
704
+ refreshHardwareEncoders: async () => {
705
+ const prober = this.encoderProber;
706
+ if (!prober) throw new Error("Hardware encoder prober not initialized");
707
+ const caps = await getCaps();
708
+ return prober.probe(caps.hardware, { force: true });
518
709
  }
519
710
  }
520
711
  }];
521
712
  }
522
713
  async onShutdown() {
523
714
  this.scorer = null;
715
+ this.encoderProber = null;
524
716
  this.cachedCaps = null;
525
717
  }
526
718
  };
527
719
  //#endregion
528
- export { InferenceConfigResolver, PlatformProbeNativeAddon, PlatformProbeNativeAddon as default, PlatformScorer };
720
+ export { HardwareEncoderProber, InferenceConfigResolver, PlatformProbeNativeAddon, PlatformProbeNativeAddon as default, PlatformScorer };
529
721
 
530
722
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","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.mjs","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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"}
@@ -30,6 +30,14 @@ export declare class SqliteSettingsBackend implements ISettingsBackend {
30
30
  constructor(dbPath: string, runtimeDefaults?: Record<string, unknown>);
31
31
  initialize(): Promise<void>;
32
32
  private requireDeclared;
33
+ /**
34
+ * Decode a single column value off a SQLite row into the JS shape the
35
+ * caller's schema expects. Handles:
36
+ * - BOOLEAN columns: `0|1` → `false|true`
37
+ * - JSON columns (TEXT starting with `{` / `[`): parse
38
+ * - everything else: pass-through with null coalescing
39
+ */
40
+ private decodeColumnValue;
33
41
  shutdown(): Promise<void>;
34
42
  get({ namespace, collection, key }: SettingsGetInput): Promise<unknown>;
35
43
  set({ namespace, collection, key, value }: SettingsSetInput): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite-settings-backend.d.ts","sourceRoot":"","sources":["../../../src/builtins/sqlite-storage/sqlite-settings-backend.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sqlite-settings-backend.d.ts","sourceRoot":"","sources":["../../../src/builtins/sqlite-storage/sqlite-settings-backend.ts"],"names":[],"mappings":"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"}
@@ -70,7 +70,8 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
70
70
  }] });
71
71
  this.declaredCollections.set(collection, {
72
72
  primaryKey: "id",
73
- columns: new Set(["data"])
73
+ columns: new Set(["data"]),
74
+ booleanColumns: /* @__PURE__ */ new Set()
74
75
  });
75
76
  }
76
77
  if (await this.isEmpty({ collection: "system-settings" })) await this.seedDefaults();
@@ -80,6 +81,25 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
80
81
  if (!decl) throw new Error(`SqliteSettingsBackend: collection "${scoped}" is not declared. Call declareCollection() first or add it to CANONICAL_KV_COLLECTIONS.`);
81
82
  return decl;
82
83
  }
84
+ /**
85
+ * Decode a single column value off a SQLite row into the JS shape the
86
+ * caller's schema expects. Handles:
87
+ * - BOOLEAN columns: `0|1` → `false|true`
88
+ * - JSON columns (TEXT starting with `{` / `[`): parse
89
+ * - everything else: pass-through with null coalescing
90
+ */
91
+ decodeColumnValue(value, columnName, booleanColumns) {
92
+ if (booleanColumns.has(columnName)) {
93
+ if (value === null || value === void 0) return value ?? null;
94
+ return value !== 0 && value !== "0" && value !== false;
95
+ }
96
+ if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) try {
97
+ return JSON.parse(value);
98
+ } catch {
99
+ return value;
100
+ }
101
+ return value ?? null;
102
+ }
83
103
  async shutdown() {
84
104
  this.db?.close();
85
105
  this.db = null;
@@ -95,15 +115,7 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
95
115
  return typeof raw === "string" ? JSON.parse(raw) : raw;
96
116
  }
97
117
  const data = {};
98
- for (const c of decl.columns) {
99
- const v = row[c];
100
- if (typeof v === "string" && (v.startsWith("{") || v.startsWith("["))) try {
101
- data[c] = JSON.parse(v);
102
- } catch {
103
- data[c] = v;
104
- }
105
- else data[c] = v ?? null;
106
- }
118
+ for (const c of decl.columns) data[c] = this.decodeColumnValue(row[c], c, decl.booleanColumns);
107
119
  return data;
108
120
  }
109
121
  async set({ namespace, collection, key, value }) {
@@ -253,15 +265,7 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
253
265
  };
254
266
  }
255
267
  const data = { [decl.primaryKey]: r[decl.primaryKey] ?? null };
256
- for (const c of decl.columns) {
257
- const v = r[c];
258
- if (typeof v === "string" && (v.startsWith("{") || v.startsWith("["))) try {
259
- data[c] = JSON.parse(v);
260
- } catch {
261
- data[c] = v;
262
- }
263
- else data[c] = v ?? null;
264
- }
268
+ for (const c of decl.columns) data[c] = this.decodeColumnValue(r[c], c, decl.booleanColumns);
265
269
  return {
266
270
  id,
267
271
  data
@@ -415,7 +419,7 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
415
419
  const schema = {
416
420
  columns: columns.map((c) => ({
417
421
  name: c.name,
418
- type: c.type === "JSON" ? "TEXT" : c.type,
422
+ type: c.type === "JSON" ? "TEXT" : c.type === "BOOLEAN" ? "INTEGER" : c.type,
419
423
  ...c.primaryKey !== void 0 ? { primaryKey: c.primaryKey } : {},
420
424
  ...c.notNull !== void 0 ? { notNull: c.notNull } : {},
421
425
  ...c.unique !== void 0 ? { unique: c.unique } : {}
@@ -430,9 +434,11 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
430
434
  const pkCol = columns.find((c) => c.primaryKey === true);
431
435
  const primaryKey = pkCol ? pkCol.name : "id";
432
436
  const columnNames = new Set(columns.filter((c) => c.name !== primaryKey).map((c) => c.name));
437
+ const booleanColumns = new Set(columns.filter((c) => c.type === "BOOLEAN").map((c) => c.name));
433
438
  this.declaredCollections.set(table, {
434
439
  primaryKey,
435
- columns: columnNames
440
+ columns: columnNames,
441
+ booleanColumns
436
442
  });
437
443
  }
438
444
  /** Serialise per-column values for SQL binding: objects → JSON, booleans → 0/1. */