@camstack/addon-post-analysis 1.2.213 → 1.2.214

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.
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
3
3
  var e = {
4
4
  "@camstack/sdk": {
5
5
  name: "@camstack/sdk",
6
- version: "1.2.92",
6
+ version: "1.2.93",
7
7
  scope: ["default"],
8
8
  loaded: !1,
9
9
  from: "addon_pipeline_analytics_widgets",
@@ -18,7 +18,7 @@ var e = {
18
18
  },
19
19
  "@camstack/types": {
20
20
  name: "@camstack/types",
21
- version: "1.2.172",
21
+ version: "1.2.173",
22
22
  scope: ["default"],
23
23
  loaded: !1,
24
24
  from: "addon_pipeline_analytics_widgets",
@@ -33,7 +33,7 @@ var e = {
33
33
  },
34
34
  "@camstack/ui-library": {
35
35
  name: "@camstack/ui-library",
36
- version: "1.2.140",
36
+ version: "1.2.141",
37
37
  scope: ["default"],
38
38
  loaded: !1,
39
39
  from: "addon_pipeline_analytics_widgets",
@@ -36,7 +36,7 @@ async function r() {
36
36
  }
37
37
  },
38
38
  "@camstack/types": {
39
- version: "1.2.172",
39
+ version: "1.2.173",
40
40
  scope: "default",
41
41
  shareConfig: {
42
42
  singleton: !0,
@@ -45,7 +45,7 @@ async function r() {
45
45
  }
46
46
  },
47
47
  "@camstack/sdk": {
48
- version: "1.2.92",
48
+ version: "1.2.93",
49
49
  scope: "default",
50
50
  shareConfig: {
51
51
  singleton: !0,
@@ -81,7 +81,7 @@ async function r() {
81
81
  }
82
82
  },
83
83
  "@camstack/ui-library": {
84
- version: "1.2.140",
84
+ version: "1.2.141",
85
85
  scope: "default",
86
86
  shareConfig: {
87
87
  singleton: !0,
@@ -10,6 +10,7 @@ let node_path$1 = require_dist.__toESM(node_path, 1);
10
10
  node_path = require_dist.__toESM(node_path);
11
11
  let node_crypto = require("node:crypto");
12
12
  let node_child_process = require("node:child_process");
13
+ let _camstack_system_addon_utils = require("@camstack/system/addon-utils");
13
14
  let sharp = require("sharp");
14
15
  sharp = require_dist.__toESM(sharp);
15
16
  let node_os = require("node:os");
@@ -49560,6 +49561,87 @@ var FaceRecognizer = class {
49560
49561
  }
49561
49562
  }
49562
49563
  };
49564
+ /** Read the window. Pure — this is the judgement the module exists to make. */
49565
+ function lagVerdict(report) {
49566
+ const bus = report.busLagMaxMs;
49567
+ if (bus === null) return "unknown";
49568
+ return bus >= 5e3 ? "starved" : "delivered-on-time";
49569
+ }
49570
+ /** Does this window need an operator's eyes? Unknown never counts as alarming
49571
+ * — that is the fabricated verdict D8 forbids. */
49572
+ function isAlarmingLagReport(report) {
49573
+ const bus = report.busLagMaxMs;
49574
+ const capture = report.captureLagMaxMs;
49575
+ return bus !== null && bus >= 5e3 || capture !== null && capture >= 5e3;
49576
+ }
49577
+ /**
49578
+ * Folds per-frame delivery lag into per-device windows.
49579
+ *
49580
+ * Not a logger: it returns the line to write (or null) and the caller decides
49581
+ * level and tags. That keeps the throttle — the part that can wreck the frame
49582
+ * path — testable without a logger fake.
49583
+ */
49584
+ var FrameLagObserver = class {
49585
+ windows = /* @__PURE__ */ new Map();
49586
+ /**
49587
+ * Fold one inference result into `deviceId`'s window.
49588
+ *
49589
+ * @param now Receipt time, read by the caller once at handler entry.
49590
+ * @param emittedAt Runner-side emit stamp; undefined ⇒ unknown (D8).
49591
+ * @param capturedAt Frame shm-commit stamp; undefined ⇒ unknown.
49592
+ * @returns The closed window when this frame ends one, else null.
49593
+ */
49594
+ observe(deviceId, now, emittedAt, capturedAt) {
49595
+ const existing = this.windows.get(deviceId);
49596
+ const w = existing ?? {
49597
+ startedAt: now,
49598
+ frames: 0,
49599
+ busMax: null,
49600
+ busMin: null,
49601
+ busUnknown: 0,
49602
+ captureMax: null,
49603
+ captureUnknown: 0
49604
+ };
49605
+ if (existing === void 0) this.windows.set(deviceId, w);
49606
+ w.frames += 1;
49607
+ if (typeof emittedAt === "number") {
49608
+ const busLag = now - emittedAt;
49609
+ if (w.busMax === null || busLag > w.busMax) w.busMax = busLag;
49610
+ if (w.busMin === null || busLag < w.busMin) w.busMin = busLag;
49611
+ } else w.busUnknown += 1;
49612
+ if (typeof capturedAt === "number") {
49613
+ const captureLag = now - capturedAt;
49614
+ if (w.captureMax === null || captureLag > w.captureMax) w.captureMax = captureLag;
49615
+ } else w.captureUnknown += 1;
49616
+ if (existing !== void 0 && now - w.startedAt < 3e4) return null;
49617
+ const report = {
49618
+ deviceId,
49619
+ frames: w.frames,
49620
+ busLagMaxMs: w.busMax,
49621
+ busLagMinMs: w.busMin,
49622
+ busLagUnknownFrames: w.busUnknown,
49623
+ captureLagMaxMs: w.captureMax,
49624
+ captureLagUnknownFrames: w.captureUnknown,
49625
+ windowMs: now - w.startedAt
49626
+ };
49627
+ w.startedAt = now;
49628
+ w.frames = 0;
49629
+ w.busMax = null;
49630
+ w.busMin = null;
49631
+ w.busUnknown = 0;
49632
+ w.captureMax = null;
49633
+ w.captureUnknown = 0;
49634
+ return report;
49635
+ }
49636
+ /** Drop a device's window (unbound camera, shutdown). */
49637
+ forget(deviceId) {
49638
+ this.windows.delete(deviceId);
49639
+ }
49640
+ /** Drop every window. */
49641
+ clear() {
49642
+ this.windows.clear();
49643
+ }
49644
+ };
49563
49645
  //#endregion
49564
49646
  //#region src/pipeline-analytics/location-aware-media-storage.ts
49565
49647
  /**
@@ -70729,6 +70811,22 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
70729
70811
  * a ≤640 RAM fallback (`tier: 'ram-fullframe'`). Null until init completes. */
70730
70812
  getNativeKeyFrameRgb = null;
70731
70813
  shuttingDown = false;
70814
+ /**
70815
+ * Per-device delivery-lag windows, folded at `handleInferenceResult` entry.
70816
+ * The instrument that tells a late DELIVERY (starved) apart from a slow
70817
+ * `processFrame` (waiting) — see `frame-lag-observer.ts`. Costs one map read
70818
+ * and ~10 numeric ops per frame, and writes one line per device per 30 s.
70819
+ */
70820
+ frameLag = new FrameLagObserver();
70821
+ /**
70822
+ * Stops the GC-attributing stall monitor armed in {@link onInitialize}.
70823
+ *
70824
+ * The other half of the frame-lag line: that one says whether the events
70825
+ * arrived late, this one says whether THIS loop was blocked while they did.
70826
+ * The 5-minute `[mem]` heartbeat cannot see a 10 s freeze — it fired at
70827
+ * dt = 300020 / 300066 / 299991 ms straight through the 2026-09-09 silences.
70828
+ */
70829
+ stopStallMonitor = null;
70732
70830
  /** True only on the cluster's designated post-processing node. When false the
70733
70831
  * addon subscribes to NOTHING — fully inert (no event/media generation). */
70734
70832
  isPostProcessingNode = true;
@@ -70947,6 +71045,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
70947
71045
  super({});
70948
71046
  }
70949
71047
  async onInitialize() {
71048
+ this.stopStallMonitor = (0, _camstack_system_addon_utils.startEventLoopStallMonitor)(this.ctx.logger);
70950
71049
  const rawApi = this.ctx.api;
70951
71050
  if (!rawApi) throw new Error("pipeline-analytics requires ctx.api (device-manager + settings-store)");
70952
71051
  const api = censusApi(rawApi, this.readCensus);
@@ -73451,6 +73550,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
73451
73550
  }
73452
73551
  async onShutdown() {
73453
73552
  this.shuttingDown = true;
73553
+ if (this.stopStallMonitor) {
73554
+ this.stopStallMonitor();
73555
+ this.stopStallMonitor = null;
73556
+ }
73454
73557
  this.sceneEngine?.stop();
73455
73558
  this.sceneEngine = null;
73456
73559
  if (this.sceneReloadTimer !== null) {
@@ -73495,6 +73598,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
73495
73598
  this.overlaySynthesisWarnAt.clear();
73496
73599
  this.processors.clear();
73497
73600
  this.lastActiveTrackIds.clear();
73601
+ this.frameLag.clear();
73498
73602
  this.lastFrameDimsByDevice.clear();
73499
73603
  this.dropoutSkipsByKey.clear();
73500
73604
  this.residents.clearAll();
@@ -73534,6 +73638,23 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
73534
73638
  async handleInferenceResult(data) {
73535
73639
  if (this.shuttingDown) return;
73536
73640
  const { deviceId, frame } = data;
73641
+ const lag = this.frameLag.observe(deviceId, Date.now(), data.emittedAt, data.capturedAt);
73642
+ if (lag !== null) {
73643
+ const verdict = lagVerdict(lag);
73644
+ (isAlarmingLagReport(lag) ? this.ctx.logger.warn.bind(this.ctx.logger) : this.ctx.logger.info.bind(this.ctx.logger))("pipeline-analytics frame lag", {
73645
+ tags: { deviceId },
73646
+ meta: {
73647
+ verdict,
73648
+ frames: lag.frames,
73649
+ windowMs: lag.windowMs,
73650
+ busLagMaxMs: lag.busLagMaxMs,
73651
+ busLagMinMs: lag.busLagMinMs,
73652
+ busLagUnknownFrames: lag.busLagUnknownFrames,
73653
+ captureLagMaxMs: lag.captureLagMaxMs,
73654
+ captureLagUnknownFrames: lag.captureLagUnknownFrames
73655
+ }
73656
+ });
73657
+ }
73537
73658
  await this.processFrame(deviceId, frame, "pipeline", data.frameHandle, data.detailSteps);
73538
73659
  }
73539
73660
  /**
@@ -6,6 +6,7 @@ import * as path$1 from "node:path";
6
6
  import path from "node:path";
7
7
  import { createHash, createHmac, randomUUID, timingSafeEqual } from "node:crypto";
8
8
  import { execFile } from "node:child_process";
9
+ import { startEventLoopStallMonitor } from "@camstack/system/addon-utils";
9
10
  import sharp from "sharp";
10
11
  import os from "node:os";
11
12
  import { Buffer as Buffer$1 } from "node:buffer";
@@ -49486,6 +49487,87 @@ var FaceRecognizer = class {
49486
49487
  }
49487
49488
  }
49488
49489
  };
49490
+ /** Read the window. Pure — this is the judgement the module exists to make. */
49491
+ function lagVerdict(report) {
49492
+ const bus = report.busLagMaxMs;
49493
+ if (bus === null) return "unknown";
49494
+ return bus >= 5e3 ? "starved" : "delivered-on-time";
49495
+ }
49496
+ /** Does this window need an operator's eyes? Unknown never counts as alarming
49497
+ * — that is the fabricated verdict D8 forbids. */
49498
+ function isAlarmingLagReport(report) {
49499
+ const bus = report.busLagMaxMs;
49500
+ const capture = report.captureLagMaxMs;
49501
+ return bus !== null && bus >= 5e3 || capture !== null && capture >= 5e3;
49502
+ }
49503
+ /**
49504
+ * Folds per-frame delivery lag into per-device windows.
49505
+ *
49506
+ * Not a logger: it returns the line to write (or null) and the caller decides
49507
+ * level and tags. That keeps the throttle — the part that can wreck the frame
49508
+ * path — testable without a logger fake.
49509
+ */
49510
+ var FrameLagObserver = class {
49511
+ windows = /* @__PURE__ */ new Map();
49512
+ /**
49513
+ * Fold one inference result into `deviceId`'s window.
49514
+ *
49515
+ * @param now Receipt time, read by the caller once at handler entry.
49516
+ * @param emittedAt Runner-side emit stamp; undefined ⇒ unknown (D8).
49517
+ * @param capturedAt Frame shm-commit stamp; undefined ⇒ unknown.
49518
+ * @returns The closed window when this frame ends one, else null.
49519
+ */
49520
+ observe(deviceId, now, emittedAt, capturedAt) {
49521
+ const existing = this.windows.get(deviceId);
49522
+ const w = existing ?? {
49523
+ startedAt: now,
49524
+ frames: 0,
49525
+ busMax: null,
49526
+ busMin: null,
49527
+ busUnknown: 0,
49528
+ captureMax: null,
49529
+ captureUnknown: 0
49530
+ };
49531
+ if (existing === void 0) this.windows.set(deviceId, w);
49532
+ w.frames += 1;
49533
+ if (typeof emittedAt === "number") {
49534
+ const busLag = now - emittedAt;
49535
+ if (w.busMax === null || busLag > w.busMax) w.busMax = busLag;
49536
+ if (w.busMin === null || busLag < w.busMin) w.busMin = busLag;
49537
+ } else w.busUnknown += 1;
49538
+ if (typeof capturedAt === "number") {
49539
+ const captureLag = now - capturedAt;
49540
+ if (w.captureMax === null || captureLag > w.captureMax) w.captureMax = captureLag;
49541
+ } else w.captureUnknown += 1;
49542
+ if (existing !== void 0 && now - w.startedAt < 3e4) return null;
49543
+ const report = {
49544
+ deviceId,
49545
+ frames: w.frames,
49546
+ busLagMaxMs: w.busMax,
49547
+ busLagMinMs: w.busMin,
49548
+ busLagUnknownFrames: w.busUnknown,
49549
+ captureLagMaxMs: w.captureMax,
49550
+ captureLagUnknownFrames: w.captureUnknown,
49551
+ windowMs: now - w.startedAt
49552
+ };
49553
+ w.startedAt = now;
49554
+ w.frames = 0;
49555
+ w.busMax = null;
49556
+ w.busMin = null;
49557
+ w.busUnknown = 0;
49558
+ w.captureMax = null;
49559
+ w.captureUnknown = 0;
49560
+ return report;
49561
+ }
49562
+ /** Drop a device's window (unbound camera, shutdown). */
49563
+ forget(deviceId) {
49564
+ this.windows.delete(deviceId);
49565
+ }
49566
+ /** Drop every window. */
49567
+ clear() {
49568
+ this.windows.clear();
49569
+ }
49570
+ };
49489
49571
  //#endregion
49490
49572
  //#region src/pipeline-analytics/location-aware-media-storage.ts
49491
49573
  /**
@@ -70655,6 +70737,22 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
70655
70737
  * a ≤640 RAM fallback (`tier: 'ram-fullframe'`). Null until init completes. */
70656
70738
  getNativeKeyFrameRgb = null;
70657
70739
  shuttingDown = false;
70740
+ /**
70741
+ * Per-device delivery-lag windows, folded at `handleInferenceResult` entry.
70742
+ * The instrument that tells a late DELIVERY (starved) apart from a slow
70743
+ * `processFrame` (waiting) — see `frame-lag-observer.ts`. Costs one map read
70744
+ * and ~10 numeric ops per frame, and writes one line per device per 30 s.
70745
+ */
70746
+ frameLag = new FrameLagObserver();
70747
+ /**
70748
+ * Stops the GC-attributing stall monitor armed in {@link onInitialize}.
70749
+ *
70750
+ * The other half of the frame-lag line: that one says whether the events
70751
+ * arrived late, this one says whether THIS loop was blocked while they did.
70752
+ * The 5-minute `[mem]` heartbeat cannot see a 10 s freeze — it fired at
70753
+ * dt = 300020 / 300066 / 299991 ms straight through the 2026-09-09 silences.
70754
+ */
70755
+ stopStallMonitor = null;
70658
70756
  /** True only on the cluster's designated post-processing node. When false the
70659
70757
  * addon subscribes to NOTHING — fully inert (no event/media generation). */
70660
70758
  isPostProcessingNode = true;
@@ -70873,6 +70971,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
70873
70971
  super({});
70874
70972
  }
70875
70973
  async onInitialize() {
70974
+ this.stopStallMonitor = startEventLoopStallMonitor(this.ctx.logger);
70876
70975
  const rawApi = this.ctx.api;
70877
70976
  if (!rawApi) throw new Error("pipeline-analytics requires ctx.api (device-manager + settings-store)");
70878
70977
  const api = censusApi(rawApi, this.readCensus);
@@ -73377,6 +73476,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
73377
73476
  }
73378
73477
  async onShutdown() {
73379
73478
  this.shuttingDown = true;
73479
+ if (this.stopStallMonitor) {
73480
+ this.stopStallMonitor();
73481
+ this.stopStallMonitor = null;
73482
+ }
73380
73483
  this.sceneEngine?.stop();
73381
73484
  this.sceneEngine = null;
73382
73485
  if (this.sceneReloadTimer !== null) {
@@ -73421,6 +73524,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
73421
73524
  this.overlaySynthesisWarnAt.clear();
73422
73525
  this.processors.clear();
73423
73526
  this.lastActiveTrackIds.clear();
73527
+ this.frameLag.clear();
73424
73528
  this.lastFrameDimsByDevice.clear();
73425
73529
  this.dropoutSkipsByKey.clear();
73426
73530
  this.residents.clearAll();
@@ -73460,6 +73564,23 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
73460
73564
  async handleInferenceResult(data) {
73461
73565
  if (this.shuttingDown) return;
73462
73566
  const { deviceId, frame } = data;
73567
+ const lag = this.frameLag.observe(deviceId, Date.now(), data.emittedAt, data.capturedAt);
73568
+ if (lag !== null) {
73569
+ const verdict = lagVerdict(lag);
73570
+ (isAlarmingLagReport(lag) ? this.ctx.logger.warn.bind(this.ctx.logger) : this.ctx.logger.info.bind(this.ctx.logger))("pipeline-analytics frame lag", {
73571
+ tags: { deviceId },
73572
+ meta: {
73573
+ verdict,
73574
+ frames: lag.frames,
73575
+ windowMs: lag.windowMs,
73576
+ busLagMaxMs: lag.busLagMaxMs,
73577
+ busLagMinMs: lag.busLagMinMs,
73578
+ busLagUnknownFrames: lag.busLagUnknownFrames,
73579
+ captureLagMaxMs: lag.captureLagMaxMs,
73580
+ captureLagUnknownFrames: lag.captureLagUnknownFrames
73581
+ }
73582
+ });
73583
+ }
73463
73584
  await this.processFrame(deviceId, frame, "pipeline", data.frameHandle, data.detailSteps);
73464
73585
  }
73465
73586
  /**
@@ -30,7 +30,7 @@ async function d(e) {
30
30
  }
31
31
  }
32
32
  async function f() {
33
- return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CxVox7bu.mjs")).catch((e) => {
33
+ return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-TUlCd-Zk.mjs")).catch((e) => {
34
34
  throw l = void 0, e;
35
35
  }), l;
36
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-post-analysis",
3
- "version": "1.2.213",
3
+ "version": "1.2.214",
4
4
  "description": "Post-Analysis bundle — enrichment, embedding-encoder, pipeline-analytics. Multi-entry npm package shipping addons that consume pipeline output.",
5
5
  "keywords": [
6
6
  "camstack",