@camstack/system 1.2.469 → 1.2.471

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 (91) hide show
  1. package/dist/addon-runner.js +97 -31
  2. package/dist/addon-runner.mjs +97 -31
  3. package/dist/addon-utils.mjs +1 -1
  4. package/dist/{agent-https-b-zX78oB.mjs → agent-https-DHBvFZJS.mjs} +1 -1
  5. package/dist/{agent-https-5OgOR-eF.js → agent-https-bH6bKDeK.js} +1 -1
  6. package/dist/auth/user-manager.d.ts +2 -2
  7. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  8. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +4 -4
  9. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  10. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +5 -5
  11. package/dist/builtins/alerts/alerts.addon.js +1 -1
  12. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  13. package/dist/builtins/autotrack/index.js +1 -1
  14. package/dist/builtins/autotrack/index.mjs +1 -1
  15. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
  16. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +5 -5
  17. package/dist/builtins/calendars/calendars.addon.js +1 -1
  18. package/dist/builtins/calendars/calendars.addon.mjs +1 -1
  19. package/dist/builtins/camera-grid/index.js +1 -1
  20. package/dist/builtins/camera-grid/index.mjs +1 -1
  21. package/dist/builtins/composer/composer.addon.js +2 -2
  22. package/dist/builtins/composer/composer.addon.mjs +2 -2
  23. package/dist/builtins/console-logging/index.js +1 -1
  24. package/dist/builtins/console-logging/index.mjs +1 -1
  25. package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
  26. package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
  27. package/dist/builtins/device-history/device-history.addon.js +1 -1
  28. package/dist/builtins/device-history/device-history.addon.mjs +1 -1
  29. package/dist/builtins/device-manager/device-manager.addon.js +1 -1
  30. package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
  31. package/dist/builtins/hub-forwarder/index.js +1 -1
  32. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  33. package/dist/builtins/keychain/index.js +3 -3
  34. package/dist/builtins/keychain/index.mjs +3 -3
  35. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  36. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  37. package/dist/builtins/local-auth/local-auth.addon.js +2 -2
  38. package/dist/builtins/local-auth/local-auth.addon.mjs +5 -5
  39. package/dist/builtins/local-network/local-network.addon.js +2 -2
  40. package/dist/builtins/local-network/local-network.addon.mjs +2 -2
  41. package/dist/builtins/loki-logging/index.js +1 -1
  42. package/dist/builtins/loki-logging/index.mjs +1 -1
  43. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  44. package/dist/builtins/native-metrics/native-metrics.addon.mjs +10 -10
  45. package/dist/builtins/platform-probe/accelerator-reprobe.d.ts +23 -0
  46. package/dist/builtins/platform-probe/coral-accelerators.d.ts +50 -11
  47. package/dist/builtins/platform-probe/index.d.ts +17 -1
  48. package/dist/builtins/platform-probe/index.js +196 -12
  49. package/dist/builtins/platform-probe/index.mjs +202 -19
  50. package/dist/builtins/platform-probe/platform-scorer.d.ts +14 -0
  51. package/dist/builtins/record-entries/index.js +1 -1
  52. package/dist/builtins/record-entries/index.mjs +1 -1
  53. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  54. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  55. package/dist/builtins/snapshot/index.js +1 -1
  56. package/dist/builtins/snapshot/index.mjs +1 -1
  57. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  58. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +20 -20
  59. package/dist/builtins/sqlite-storage/index.js +1 -1
  60. package/dist/builtins/sqlite-storage/index.mjs +1 -1
  61. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +2 -2
  62. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +2 -2
  63. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  64. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  65. package/dist/builtins/system-config/system-config.addon.js +3 -3
  66. package/dist/builtins/system-config/system-config.addon.mjs +115 -115
  67. package/dist/builtins/winston-logging/index.js +1 -1
  68. package/dist/builtins/winston-logging/index.mjs +1 -1
  69. package/dist/{composition-sources-BGXTiUZm.js → composition-sources-B5uJZXZ1.js} +1 -1
  70. package/dist/{composition-sources-DTdzzyf0.mjs → composition-sources-DRw_vBKH.mjs} +1 -1
  71. package/dist/{constants-md_POijo.mjs → constants-5lA8Z9d8.mjs} +1 -1
  72. package/dist/{constants-2mPqJjkR.js → constants-CToE4OFp.js} +1 -1
  73. package/dist/{coral-accelerators-oUXmmbc2.js → coral-accelerators-BSVsBVzt.js} +102 -30
  74. package/dist/coral-accelerators-BW3OcrLD.mjs +231 -0
  75. package/dist/{custom-action-registry-DZSCLBuT.mjs → custom-action-registry-BGYDjTqY.mjs} +37 -37
  76. package/dist/{device-ca-CFdyrqeJ.mjs → device-ca-URrs1lxK.mjs} +1 -1
  77. package/dist/{device-ca-CC58lmNV.js → device-ca-nOCYVFS4.js} +1 -1
  78. package/dist/{dist-CtVVtyTX.mjs → dist-5XUG7iYJ.mjs} +6839 -1416
  79. package/dist/{dist-DuBtzSCh.js → dist-BjcWEfRy.js} +6839 -1416
  80. package/dist/file-host-worker.js +1 -1
  81. package/dist/file-host-worker.mjs +1 -1
  82. package/dist/{hub-mdns-ciao-BLsX_2cL.js → hub-mdns-ciao-vEWhJ_SD.js} +2 -2
  83. package/dist/{hub-mdns-ciao-CmuWLPci.mjs → hub-mdns-ciao-zD3JojB_.mjs} +2 -2
  84. package/dist/index.js +8 -8
  85. package/dist/index.mjs +225 -225
  86. package/dist/{restore-archive-CxPAuaNs.js → restore-archive-B64m2nAj.js} +1 -1
  87. package/dist/{restore-archive-D2w3z_ob.mjs → restore-archive-CN1pJTRn.mjs} +94 -94
  88. package/dist/{wal-maintenance-EK28XXnc.js → wal-maintenance-BNXVX-ot.js} +0 -0
  89. package/dist/{wal-maintenance-DeluFl_1.mjs → wal-maintenance-Bl2I-ZcR.mjs} +0 -0
  90. package/package.json +1 -1
  91. package/dist/coral-accelerators-Qhn5TOYZ.mjs +0 -165
@@ -3,14 +3,58 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DuBtzSCh.js");
7
- const require_coral_accelerators = require("../../coral-accelerators-oUXmmbc2.js");
6
+ const require_dist = require("../../dist-BjcWEfRy.js");
7
+ const require_coral_accelerators = require("../../coral-accelerators-BSVsBVzt.js");
8
8
  let node_util = require("node:util");
9
9
  let node_os = require("node:os");
10
10
  node_os = require_chunk.__toESM(node_os);
11
11
  let node_fs = require("node:fs");
12
12
  node_fs = require_chunk.__toESM(node_fs);
13
13
  let node_child_process = require("node:child_process");
14
+ //#region src/builtins/platform-probe/accelerator-reprobe.ts
15
+ /**
16
+ * The accelerator part of the platform probe, re-run on its own (D919).
17
+ *
18
+ * The full probe runs once per process. A Coral is the one accelerator that is
19
+ * plugged in, pulled out and moved between nodes while the process runs, so
20
+ * its part (USB ids, PCIe apex units — reads of `/sys` and `/dev`, no model
21
+ * load) is re-run periodically and on demand, and the node's inference device
22
+ * list is rebuilt from the new answer. Pure: the scorer owns the cache.
23
+ */
24
+ /** A stable signature of a Coral answer — what decides "did it change". */
25
+ function coralSignature(coral) {
26
+ if (coral == null) return "none";
27
+ return `units=${(coral.units ?? []).map((u) => `${u.bus}:${u.count}`).join(",")};unavailable=${(coral.unavailable ?? []).map((u) => `${u.bus}:${u.count}:${u.reason}`).join(",")}`;
28
+ }
29
+ /** Which device keys a new probe added and removed. */
30
+ function diffInferenceDevices(previous, next) {
31
+ const before = new Set(previous.map((d) => d.key));
32
+ const after = new Set(next.map((d) => d.key));
33
+ return {
34
+ appeared: [...after].filter((k) => !before.has(k)).toSorted(),
35
+ disappeared: [...before].filter((k) => !after.has(k)).toSorted()
36
+ };
37
+ }
38
+ /**
39
+ * The capabilities with a new Coral answer: hardware, scores and the device
40
+ * enumeration are all derived again from it, so the edgetpu runtime and its
41
+ * devices follow the hardware rather than the boot-time snapshot.
42
+ */
43
+ function withCoral(caps, coral) {
44
+ const hardware = {
45
+ ...caps.hardware,
46
+ coral
47
+ };
48
+ const { scores, best } = require_dist.scoreRuntimes(hardware);
49
+ return {
50
+ ...caps,
51
+ hardware,
52
+ scores: [...scores],
53
+ bestScore: best,
54
+ devices: require_dist.enumerateInferenceDevices(hardware)
55
+ };
56
+ }
57
+ //#endregion
14
58
  //#region src/builtins/platform-probe/amd-npu.ts
15
59
  var amd_npu_exports = /* @__PURE__ */ require_chunk.__exportAll({
16
60
  classifyAmdNpu: () => classifyAmdNpu,
@@ -417,15 +461,7 @@ var PlatformScorer = class {
417
461
  } });
418
462
  let gpu = null;
419
463
  let npu = null;
420
- const coral = require_coral_accelerators.classifyCoralAccelerator(this.coralFs);
421
- if (coral) {
422
- if ((coral.units ?? []).length > 0) this.logger.info("Coral Edge TPU detected", { meta: { units: coral.units } });
423
- for (const unavailable of coral.unavailable ?? []) this.logger.warn("Coral Edge TPU present but unusable", { meta: {
424
- bus: unavailable.bus,
425
- count: unavailable.count,
426
- reason: unavailable.reason
427
- } });
428
- }
464
+ const coral = this.probeCoral().info;
429
465
  if (platform === "darwin" && arch === "arm64") {
430
466
  gpu = {
431
467
  type: "apple",
@@ -545,6 +581,67 @@ var PlatformScorer = class {
545
581
  coral
546
582
  };
547
583
  }
584
+ /**
585
+ * The Coral part of the probe, with the evidence that decided it logged —
586
+ * a node that reports a Coral, or stops reporting one, says WHY (D919).
587
+ */
588
+ probeCoral() {
589
+ const detection = require_coral_accelerators.detectCoralAccelerator(this.coralFs);
590
+ const coral = detection.info;
591
+ this.logger.info("Coral Edge TPU probe", { meta: {
592
+ usbBus: detection.usbBus,
593
+ decidedBy: detection.decidedBy,
594
+ units: coral?.units ?? [],
595
+ inaccessibleUsbNodes: detection.inaccessibleUsbNodes
596
+ } });
597
+ if (coral) {
598
+ if ((coral.units ?? []).length > 0) this.logger.info("Coral Edge TPU detected", { meta: {
599
+ units: coral.units,
600
+ decidedBy: detection.decidedBy
601
+ } });
602
+ for (const unavailable of coral.unavailable ?? []) this.logger.warn("Coral Edge TPU present but unusable", { meta: {
603
+ bus: unavailable.bus,
604
+ count: unavailable.count,
605
+ reason: unavailable.reason,
606
+ ...unavailable.bus === "usb" ? { nodes: detection.inaccessibleUsbNodes } : {}
607
+ } });
608
+ }
609
+ return detection;
610
+ }
611
+ /**
612
+ * Re-run ONLY the accelerator part (Coral USB ids + PCIe apex units: `/sys`
613
+ * and `/dev` reads, no model load) against the cached probe, and rebuild the
614
+ * device list from it when it changed. `null` before the first full probe
615
+ * completed — there is nothing to compare against yet. Logs the evidence
616
+ * only when the answer changed, so a 60 s cadence stays quiet.
617
+ */
618
+ reprobeAccelerators() {
619
+ const cached = this.cached;
620
+ if (cached === null) return null;
621
+ const detection = require_coral_accelerators.detectCoralAccelerator(this.coralFs);
622
+ if (coralSignature(detection.info) === coralSignature(cached.hardware.coral)) return {
623
+ caps: cached,
624
+ change: {
625
+ appeared: [],
626
+ disappeared: []
627
+ },
628
+ detection
629
+ };
630
+ this.logger.info("Coral Edge TPU probe changed", { meta: {
631
+ usbBus: detection.usbBus,
632
+ decidedBy: detection.decidedBy,
633
+ before: coralSignature(cached.hardware.coral),
634
+ after: coralSignature(detection.info),
635
+ inaccessibleUsbNodes: detection.inaccessibleUsbNodes
636
+ } });
637
+ const next = withCoral(cached, detection.info);
638
+ this.cached = next;
639
+ return {
640
+ caps: next,
641
+ change: diffInferenceDevices(cached.devices, next.devices),
642
+ detection
643
+ };
644
+ }
548
645
  /** NVIDIA's own tool: exact model and VRAM. Null when absent or failing. */
549
646
  async probeNvidiaSmi() {
550
647
  try {
@@ -733,9 +830,13 @@ function narrowHwAccelPrefer(value) {
733
830
  if (typeof value === "string" && isHwAccelBackend(value)) return value;
734
831
  return null;
735
832
  }
833
+ /** How often the accelerator part of the probe is re-run (D919). Cheap: `/sys` + `/dev` reads. */
834
+ var ACCELERATOR_REPROBE_INTERVAL_MS = 6e4;
736
835
  var PlatformProbeNativeAddon = class extends require_dist.BaseAddon {
737
836
  scorer = null;
738
837
  cachedCaps = null;
838
+ /** The periodic accelerator re-probe; started once the full probe completed. */
839
+ reprobeTimer = null;
739
840
  /**
740
841
  * Per-boot generation stamp for the manual readiness emissions below.
741
842
  * Constant for the lifetime of this addon instance (== one process boot);
@@ -788,7 +889,7 @@ var PlatformProbeNativeAddon = class extends require_dist.BaseAddon {
788
889
  this.ctx.logger.debug("ensurePython unavailable for platform probe", { meta: { error: require_dist.errMsg(err) } });
789
890
  return null;
790
891
  });
791
- this.scorer = new PlatformScorer(this.ctx.logger, embeddedPython);
892
+ this.scorer = new PlatformScorer(this.ctx.logger.withTags({ nodeId: this.bareLocalNodeId() }), embeddedPython);
792
893
  const emitPhase = (phase, payload) => {
793
894
  this.ctx.eventBus?.emit({
794
895
  id: `platform-probe-${phase}-${Date.now()}`,
@@ -816,6 +917,7 @@ var PlatformProbeNativeAddon = class extends require_dist.BaseAddon {
816
917
  bestScore: caps.bestScore.score
817
918
  } });
818
919
  this.emitProbeReadiness("ready");
920
+ this.startAcceleratorReprobe();
819
921
  return caps;
820
922
  }).catch((err) => {
821
923
  const msg = require_dist.errMsg(err);
@@ -850,17 +952,99 @@ var PlatformProbeNativeAddon = class extends require_dist.BaseAddon {
850
952
  preferred: res.preferred,
851
953
  rationale: res.rationale
852
954
  };
955
+ },
956
+ rescanAccelerators: async () => {
957
+ await getCaps();
958
+ const rescan = this.reprobeAccelerators("operator");
959
+ if (rescan === null) throw new Error("platform-probe: the probe is shutting down");
960
+ return rescan;
853
961
  }
854
962
  }
855
963
  }];
856
964
  }
965
+ /** Start the periodic accelerator re-probe (idempotent). */
966
+ startAcceleratorReprobe() {
967
+ if (this.reprobeTimer !== null) return;
968
+ this.reprobeTimer = setInterval(() => {
969
+ this.reprobeAccelerators("periodic");
970
+ }, ACCELERATOR_REPROBE_INTERVAL_MS);
971
+ this.reprobeTimer.unref?.();
972
+ }
973
+ /**
974
+ * Re-run the accelerator part of the probe and serve its answer from now on.
975
+ * Every device that appeared or disappeared is logged, naming the node and
976
+ * what triggered the re-probe; the orchestrator sees the new list on its next
977
+ * `getCapabilities` read and re-elects through its device-set-change path.
978
+ * `null` when the scorer is gone or has not completed its first probe.
979
+ */
980
+ reprobeAccelerators(trigger) {
981
+ const scorer = this.scorer;
982
+ if (scorer === null) return null;
983
+ let result;
984
+ try {
985
+ result = scorer.reprobeAccelerators();
986
+ } catch (err) {
987
+ this.ctx.logger.warn("accelerator re-probe failed — last device list kept", {
988
+ tags: { nodeId: this.bareLocalNodeId() },
989
+ meta: {
990
+ nodeId: this.bareLocalNodeId(),
991
+ trigger,
992
+ error: require_dist.errMsg(err)
993
+ }
994
+ });
995
+ return null;
996
+ }
997
+ if (result === null) return null;
998
+ this.cachedCaps = result.caps;
999
+ const nodeId = this.bareLocalNodeId();
1000
+ for (const deviceKey of result.change.appeared) this.ctx.logger.info("inference device appeared", {
1001
+ tags: { nodeId },
1002
+ meta: {
1003
+ nodeId,
1004
+ deviceKey,
1005
+ trigger,
1006
+ decidedBy: result.detection.decidedBy
1007
+ }
1008
+ });
1009
+ for (const deviceKey of result.change.disappeared) this.ctx.logger.info("inference device disappeared", {
1010
+ tags: { nodeId },
1011
+ meta: {
1012
+ nodeId,
1013
+ deviceKey,
1014
+ trigger,
1015
+ decidedBy: result.detection.decidedBy
1016
+ }
1017
+ });
1018
+ if (trigger === "operator") this.ctx.logger.info("inference devices rescanned", {
1019
+ tags: { nodeId },
1020
+ meta: {
1021
+ nodeId,
1022
+ usbBus: result.detection.usbBus,
1023
+ decidedBy: result.detection.decidedBy,
1024
+ appeared: result.change.appeared,
1025
+ disappeared: result.change.disappeared
1026
+ }
1027
+ });
1028
+ return {
1029
+ devices: result.caps.devices,
1030
+ appeared: result.change.appeared,
1031
+ disappeared: result.change.disappeared,
1032
+ usbBus: result.detection.usbBus,
1033
+ decidedBy: result.detection.decidedBy
1034
+ };
1035
+ }
857
1036
  async onShutdown() {
1037
+ if (this.reprobeTimer !== null) {
1038
+ clearInterval(this.reprobeTimer);
1039
+ this.reprobeTimer = null;
1040
+ }
858
1041
  this.emitProbeReadiness("down");
859
1042
  this.scorer = null;
860
1043
  this.cachedCaps = null;
861
1044
  }
862
1045
  };
863
1046
  //#endregion
1047
+ exports.ACCELERATOR_REPROBE_INTERVAL_MS = ACCELERATOR_REPROBE_INTERVAL_MS;
864
1048
  exports.InferenceConfigResolver = InferenceConfigResolver;
865
1049
  exports.PlatformProbeNativeAddon = PlatformProbeNativeAddon;
866
1050
  exports.default = PlatformProbeNativeAddon;
@@ -1,10 +1,54 @@
1
1
  import { o as __toESM, r as __exportAll } from "../../chunk-CNf5ZN-e.mjs";
2
- import { $n as platformProbeCapability, $r as errMsg, Da as EventCategory, Ji as emitReadiness, _r as scoreRuntimes, an as enumerateInferenceDevices, ei as BaseAddon } from "../../dist-CtVVtyTX.mjs";
3
- import { n as nodeCoralFs, t as classifyCoralAccelerator } from "../../coral-accelerators-Qhn5TOYZ.mjs";
2
+ import { $n as platformProbeCapability, $r as errMsg, Da as EventCategory, Ji as emitReadiness, _r as scoreRuntimes, an as enumerateInferenceDevices, ei as BaseAddon } from "../../dist-5XUG7iYJ.mjs";
3
+ import { n as detectCoralAccelerator, r as nodeCoralFs } from "../../coral-accelerators-BW3OcrLD.mjs";
4
4
  import { promisify } from "node:util";
5
5
  import * as os$1 from "node:os";
6
- import * as fs$1 from "node:fs";
6
+ import * as fs from "node:fs";
7
7
  import { execFile } from "node:child_process";
8
+ //#region src/builtins/platform-probe/accelerator-reprobe.ts
9
+ /**
10
+ * The accelerator part of the platform probe, re-run on its own (D919).
11
+ *
12
+ * The full probe runs once per process. A Coral is the one accelerator that is
13
+ * plugged in, pulled out and moved between nodes while the process runs, so
14
+ * its part (USB ids, PCIe apex units — reads of `/sys` and `/dev`, no model
15
+ * load) is re-run periodically and on demand, and the node's inference device
16
+ * list is rebuilt from the new answer. Pure: the scorer owns the cache.
17
+ */
18
+ /** A stable signature of a Coral answer — what decides "did it change". */
19
+ function coralSignature(coral) {
20
+ if (coral == null) return "none";
21
+ return `units=${(coral.units ?? []).map((u) => `${u.bus}:${u.count}`).join(",")};unavailable=${(coral.unavailable ?? []).map((u) => `${u.bus}:${u.count}:${u.reason}`).join(",")}`;
22
+ }
23
+ /** Which device keys a new probe added and removed. */
24
+ function diffInferenceDevices(previous, next) {
25
+ const before = new Set(previous.map((d) => d.key));
26
+ const after = new Set(next.map((d) => d.key));
27
+ return {
28
+ appeared: [...after].filter((k) => !before.has(k)).toSorted(),
29
+ disappeared: [...before].filter((k) => !after.has(k)).toSorted()
30
+ };
31
+ }
32
+ /**
33
+ * The capabilities with a new Coral answer: hardware, scores and the device
34
+ * enumeration are all derived again from it, so the edgetpu runtime and its
35
+ * devices follow the hardware rather than the boot-time snapshot.
36
+ */
37
+ function withCoral(caps, coral) {
38
+ const hardware = {
39
+ ...caps.hardware,
40
+ coral
41
+ };
42
+ const { scores, best } = scoreRuntimes(hardware);
43
+ return {
44
+ ...caps,
45
+ hardware,
46
+ scores: [...scores],
47
+ bestScore: best,
48
+ devices: enumerateInferenceDevices(hardware)
49
+ };
50
+ }
51
+ //#endregion
8
52
  //#region src/builtins/platform-probe/amd-npu.ts
9
53
  var amd_npu_exports = /* @__PURE__ */ __exportAll({
10
54
  classifyAmdNpu: () => classifyAmdNpu,
@@ -411,15 +455,7 @@ var PlatformScorer = class {
411
455
  } });
412
456
  let gpu = null;
413
457
  let npu = null;
414
- const coral = classifyCoralAccelerator(this.coralFs);
415
- if (coral) {
416
- if ((coral.units ?? []).length > 0) this.logger.info("Coral Edge TPU detected", { meta: { units: coral.units } });
417
- for (const unavailable of coral.unavailable ?? []) this.logger.warn("Coral Edge TPU present but unusable", { meta: {
418
- bus: unavailable.bus,
419
- count: unavailable.count,
420
- reason: unavailable.reason
421
- } });
422
- }
458
+ const coral = this.probeCoral().info;
423
459
  if (platform === "darwin" && arch === "arm64") {
424
460
  gpu = {
425
461
  type: "apple",
@@ -448,14 +484,14 @@ var PlatformScorer = class {
448
484
  if (nvidia) gpu = nvidia;
449
485
  const listRenderNodes = () => {
450
486
  try {
451
- return fs$1.readdirSync("/dev/dri").filter((n) => n.startsWith("renderD")).map((n) => `/dev/dri/${n}`);
487
+ return fs.readdirSync("/dev/dri").filter((n) => n.startsWith("renderD")).map((n) => `/dev/dri/${n}`);
452
488
  } catch {
453
489
  return [];
454
490
  }
455
491
  };
456
492
  const readDrmAttr = (node, attr) => {
457
493
  try {
458
- return fs$1.readFileSync(`/sys/class/drm/${node.split("/").pop()}/device/${attr}`, "utf8");
494
+ return fs.readFileSync(`/sys/class/drm/${node.split("/").pop()}/device/${attr}`, "utf8");
459
495
  } catch {
460
496
  return "";
461
497
  }
@@ -481,12 +517,12 @@ var PlatformScorer = class {
481
517
  }
482
518
  const listDir = (p) => {
483
519
  try {
484
- return fs$1.readdirSync(p);
520
+ return fs.readdirSync(p);
485
521
  } catch {
486
522
  return [];
487
523
  }
488
524
  };
489
- const exists = (p) => fs$1.existsSync(p);
525
+ const exists = (p) => fs.existsSync(p);
490
526
  if (!npu) {
491
527
  const { classifyIntelNpu } = await import("../../intel-npu-C8HKvRQF.mjs");
492
528
  npu = classifyIntelNpu({
@@ -514,7 +550,7 @@ var PlatformScorer = class {
514
550
  exists,
515
551
  readFile: (p) => {
516
552
  try {
517
- return fs$1.readFileSync(p, "utf8");
553
+ return fs.readFileSync(p, "utf8");
518
554
  } catch {
519
555
  return "";
520
556
  }
@@ -539,6 +575,67 @@ var PlatformScorer = class {
539
575
  coral
540
576
  };
541
577
  }
578
+ /**
579
+ * The Coral part of the probe, with the evidence that decided it logged —
580
+ * a node that reports a Coral, or stops reporting one, says WHY (D919).
581
+ */
582
+ probeCoral() {
583
+ const detection = detectCoralAccelerator(this.coralFs);
584
+ const coral = detection.info;
585
+ this.logger.info("Coral Edge TPU probe", { meta: {
586
+ usbBus: detection.usbBus,
587
+ decidedBy: detection.decidedBy,
588
+ units: coral?.units ?? [],
589
+ inaccessibleUsbNodes: detection.inaccessibleUsbNodes
590
+ } });
591
+ if (coral) {
592
+ if ((coral.units ?? []).length > 0) this.logger.info("Coral Edge TPU detected", { meta: {
593
+ units: coral.units,
594
+ decidedBy: detection.decidedBy
595
+ } });
596
+ for (const unavailable of coral.unavailable ?? []) this.logger.warn("Coral Edge TPU present but unusable", { meta: {
597
+ bus: unavailable.bus,
598
+ count: unavailable.count,
599
+ reason: unavailable.reason,
600
+ ...unavailable.bus === "usb" ? { nodes: detection.inaccessibleUsbNodes } : {}
601
+ } });
602
+ }
603
+ return detection;
604
+ }
605
+ /**
606
+ * Re-run ONLY the accelerator part (Coral USB ids + PCIe apex units: `/sys`
607
+ * and `/dev` reads, no model load) against the cached probe, and rebuild the
608
+ * device list from it when it changed. `null` before the first full probe
609
+ * completed — there is nothing to compare against yet. Logs the evidence
610
+ * only when the answer changed, so a 60 s cadence stays quiet.
611
+ */
612
+ reprobeAccelerators() {
613
+ const cached = this.cached;
614
+ if (cached === null) return null;
615
+ const detection = detectCoralAccelerator(this.coralFs);
616
+ if (coralSignature(detection.info) === coralSignature(cached.hardware.coral)) return {
617
+ caps: cached,
618
+ change: {
619
+ appeared: [],
620
+ disappeared: []
621
+ },
622
+ detection
623
+ };
624
+ this.logger.info("Coral Edge TPU probe changed", { meta: {
625
+ usbBus: detection.usbBus,
626
+ decidedBy: detection.decidedBy,
627
+ before: coralSignature(cached.hardware.coral),
628
+ after: coralSignature(detection.info),
629
+ inaccessibleUsbNodes: detection.inaccessibleUsbNodes
630
+ } });
631
+ const next = withCoral(cached, detection.info);
632
+ this.cached = next;
633
+ return {
634
+ caps: next,
635
+ change: diffInferenceDevices(cached.devices, next.devices),
636
+ detection
637
+ };
638
+ }
542
639
  /** NVIDIA's own tool: exact model and VRAM. Null when absent or failing. */
543
640
  async probeNvidiaSmi() {
544
641
  try {
@@ -727,9 +824,13 @@ function narrowHwAccelPrefer(value) {
727
824
  if (typeof value === "string" && isHwAccelBackend(value)) return value;
728
825
  return null;
729
826
  }
827
+ /** How often the accelerator part of the probe is re-run (D919). Cheap: `/sys` + `/dev` reads. */
828
+ var ACCELERATOR_REPROBE_INTERVAL_MS = 6e4;
730
829
  var PlatformProbeNativeAddon = class extends BaseAddon {
731
830
  scorer = null;
732
831
  cachedCaps = null;
832
+ /** The periodic accelerator re-probe; started once the full probe completed. */
833
+ reprobeTimer = null;
733
834
  /**
734
835
  * Per-boot generation stamp for the manual readiness emissions below.
735
836
  * Constant for the lifetime of this addon instance (== one process boot);
@@ -782,7 +883,7 @@ var PlatformProbeNativeAddon = class extends BaseAddon {
782
883
  this.ctx.logger.debug("ensurePython unavailable for platform probe", { meta: { error: errMsg(err) } });
783
884
  return null;
784
885
  });
785
- this.scorer = new PlatformScorer(this.ctx.logger, embeddedPython);
886
+ this.scorer = new PlatformScorer(this.ctx.logger.withTags({ nodeId: this.bareLocalNodeId() }), embeddedPython);
786
887
  const emitPhase = (phase, payload) => {
787
888
  this.ctx.eventBus?.emit({
788
889
  id: `platform-probe-${phase}-${Date.now()}`,
@@ -810,6 +911,7 @@ var PlatformProbeNativeAddon = class extends BaseAddon {
810
911
  bestScore: caps.bestScore.score
811
912
  } });
812
913
  this.emitProbeReadiness("ready");
914
+ this.startAcceleratorReprobe();
813
915
  return caps;
814
916
  }).catch((err) => {
815
917
  const msg = errMsg(err);
@@ -844,15 +946,96 @@ var PlatformProbeNativeAddon = class extends BaseAddon {
844
946
  preferred: res.preferred,
845
947
  rationale: res.rationale
846
948
  };
949
+ },
950
+ rescanAccelerators: async () => {
951
+ await getCaps();
952
+ const rescan = this.reprobeAccelerators("operator");
953
+ if (rescan === null) throw new Error("platform-probe: the probe is shutting down");
954
+ return rescan;
847
955
  }
848
956
  }
849
957
  }];
850
958
  }
959
+ /** Start the periodic accelerator re-probe (idempotent). */
960
+ startAcceleratorReprobe() {
961
+ if (this.reprobeTimer !== null) return;
962
+ this.reprobeTimer = setInterval(() => {
963
+ this.reprobeAccelerators("periodic");
964
+ }, ACCELERATOR_REPROBE_INTERVAL_MS);
965
+ this.reprobeTimer.unref?.();
966
+ }
967
+ /**
968
+ * Re-run the accelerator part of the probe and serve its answer from now on.
969
+ * Every device that appeared or disappeared is logged, naming the node and
970
+ * what triggered the re-probe; the orchestrator sees the new list on its next
971
+ * `getCapabilities` read and re-elects through its device-set-change path.
972
+ * `null` when the scorer is gone or has not completed its first probe.
973
+ */
974
+ reprobeAccelerators(trigger) {
975
+ const scorer = this.scorer;
976
+ if (scorer === null) return null;
977
+ let result;
978
+ try {
979
+ result = scorer.reprobeAccelerators();
980
+ } catch (err) {
981
+ this.ctx.logger.warn("accelerator re-probe failed — last device list kept", {
982
+ tags: { nodeId: this.bareLocalNodeId() },
983
+ meta: {
984
+ nodeId: this.bareLocalNodeId(),
985
+ trigger,
986
+ error: errMsg(err)
987
+ }
988
+ });
989
+ return null;
990
+ }
991
+ if (result === null) return null;
992
+ this.cachedCaps = result.caps;
993
+ const nodeId = this.bareLocalNodeId();
994
+ for (const deviceKey of result.change.appeared) this.ctx.logger.info("inference device appeared", {
995
+ tags: { nodeId },
996
+ meta: {
997
+ nodeId,
998
+ deviceKey,
999
+ trigger,
1000
+ decidedBy: result.detection.decidedBy
1001
+ }
1002
+ });
1003
+ for (const deviceKey of result.change.disappeared) this.ctx.logger.info("inference device disappeared", {
1004
+ tags: { nodeId },
1005
+ meta: {
1006
+ nodeId,
1007
+ deviceKey,
1008
+ trigger,
1009
+ decidedBy: result.detection.decidedBy
1010
+ }
1011
+ });
1012
+ if (trigger === "operator") this.ctx.logger.info("inference devices rescanned", {
1013
+ tags: { nodeId },
1014
+ meta: {
1015
+ nodeId,
1016
+ usbBus: result.detection.usbBus,
1017
+ decidedBy: result.detection.decidedBy,
1018
+ appeared: result.change.appeared,
1019
+ disappeared: result.change.disappeared
1020
+ }
1021
+ });
1022
+ return {
1023
+ devices: result.caps.devices,
1024
+ appeared: result.change.appeared,
1025
+ disappeared: result.change.disappeared,
1026
+ usbBus: result.detection.usbBus,
1027
+ decidedBy: result.detection.decidedBy
1028
+ };
1029
+ }
851
1030
  async onShutdown() {
1031
+ if (this.reprobeTimer !== null) {
1032
+ clearInterval(this.reprobeTimer);
1033
+ this.reprobeTimer = null;
1034
+ }
852
1035
  this.emitProbeReadiness("down");
853
1036
  this.scorer = null;
854
1037
  this.cachedCaps = null;
855
1038
  }
856
1039
  };
857
1040
  //#endregion
858
- export { InferenceConfigResolver, PlatformProbeNativeAddon, PlatformProbeNativeAddon as default, PlatformScorer };
1041
+ export { ACCELERATOR_REPROBE_INTERVAL_MS, InferenceConfigResolver, PlatformProbeNativeAddon, PlatformProbeNativeAddon as default, PlatformScorer };
@@ -1,5 +1,6 @@
1
1
  import type { HardwareInfo, PlatformCapabilities, IScopedLogger } from "@camstack/types";
2
2
  import { type CoralFs } from "./coral-accelerators.js";
3
+ import { type AcceleratorReprobe } from "./accelerator-reprobe.js";
3
4
  export declare class PlatformScorer {
4
5
  private cached;
5
6
  private readonly logger;
@@ -29,6 +30,19 @@ export declare class PlatformScorer {
29
30
  */
30
31
  probe(onPhase?: (phase: string, payload?: Record<string, unknown>) => void): Promise<PlatformCapabilities>;
31
32
  probeHardware(): Promise<HardwareInfo>;
33
+ /**
34
+ * The Coral part of the probe, with the evidence that decided it logged —
35
+ * a node that reports a Coral, or stops reporting one, says WHY (D919).
36
+ */
37
+ private probeCoral;
38
+ /**
39
+ * Re-run ONLY the accelerator part (Coral USB ids + PCIe apex units: `/sys`
40
+ * and `/dev` reads, no model load) against the cached probe, and rebuild the
41
+ * device list from it when it changed. `null` before the first full probe
42
+ * completed — there is nothing to compare against yet. Logs the evidence
43
+ * only when the answer changed, so a 60 s cadence stays quiet.
44
+ */
45
+ reprobeAccelerators(): AcceleratorReprobe | null;
32
46
  /** NVIDIA's own tool: exact model and VRAM. Null when absent or failing. */
33
47
  private probeNvidiaSmi;
34
48
  /** Windows WMI probe; bounded, and a failure is a log line, never a boot failure. */
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DuBtzSCh.js");
6
+ const require_dist = require("../../dist-BjcWEfRy.js");
7
7
  let zod = require("zod");
8
8
  let node_crypto = require("node:crypto");
9
9
  //#region src/builtins/record-entries/entries-service.ts
@@ -1,4 +1,4 @@
1
- import { ei as BaseAddon, ha as recordSecretRef, ji as UnknownRecordKindError, nr as recordEntriesCapability, sa as mapSecretLeaves, ui as InvalidRecordError } from "../../dist-CtVVtyTX.mjs";
1
+ import { ei as BaseAddon, ha as recordSecretRef, ji as UnknownRecordKindError, nr as recordEntriesCapability, sa as mapSecretLeaves, ui as InvalidRecordError } from "../../dist-5XUG7iYJ.mjs";
2
2
  import { z } from "zod";
3
3
  import { randomUUID } from "node:crypto";
4
4
  //#region src/builtins/record-entries/entries-service.ts
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DuBtzSCh.js");
6
+ const require_dist = require("../../dist-BjcWEfRy.js");
7
7
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
8
8
  /**
9
9
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -1,4 +1,4 @@
1
- import { Da as EventCategory, ei as BaseAddon } from "../../dist-CtVVtyTX.mjs";
1
+ import { Da as EventCategory, ei as BaseAddon } from "../../dist-5XUG7iYJ.mjs";
2
2
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
3
3
  /**
4
4
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DuBtzSCh.js");
6
+ const require_dist = require("../../dist-BjcWEfRy.js");
7
7
  const require_snapshot_courtesy = require("../../snapshot-courtesy-D_C5-nC0.js");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
@@ -1,4 +1,4 @@
1
- import { $r as errMsg, Da as EventCategory, Dt as batteryCapability, Et as bareAddonId, Hn as memoizePinnedFfmpeg, Qr as DeviceType, Xr as DeviceFeature, Xt as deriveBatteryPresence, ca as nodePin, ei as BaseAddon, kr as streamQualityLabel, o as BatteryStatusSchema, xr as snapshotCapability } from "../../dist-CtVVtyTX.mjs";
1
+ import { $r as errMsg, Da as EventCategory, Dt as batteryCapability, Et as bareAddonId, Hn as memoizePinnedFfmpeg, Qr as DeviceType, Xr as DeviceFeature, Xt as deriveBatteryPresence, ca as nodePin, ei as BaseAddon, kr as streamQualityLabel, o as BatteryStatusSchema, xr as snapshotCapability } from "../../dist-5XUG7iYJ.mjs";
2
2
  import { i as renderCourtesyOverlayJpeg, n as courtesyCacheKey, r as renderCourtesyJpeg } from "../../snapshot-courtesy-CyFs1M3I.mjs";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DuBtzSCh.js");
6
+ const require_dist = require("../../dist-BjcWEfRy.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let node_fs_promises = require("node:fs/promises");
9
9
  let node_path = require("node:path");