@camstack/addon-pipeline 1.2.192 → 1.2.193

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 (35) hide show
  1. package/dist/audio-analyzer/index.js +2 -2
  2. package/dist/audio-analyzer/index.mjs +2 -2
  3. package/dist/detection-pipeline/index.js +7 -7
  4. package/dist/detection-pipeline/index.mjs +5 -5
  5. package/dist/{dist-CXO_Fmx6.js → dist-HG5geL9t.js} +129 -1
  6. package/dist/{dist-B8J7wrtY.mjs → dist-mYDK58Jp.mjs} +118 -2
  7. package/dist/{lazy-sharp-o-aNNXuz.js → lazy-sharp-DmAwZ0s9.js} +1 -1
  8. package/dist/{local-frame-registry-BDeDpYQk.mjs → local-frame-registry-av9ObDG0.mjs} +1 -1
  9. package/dist/{local-frame-registry-P7tSfvaY.js → local-frame-registry-x3m6SNqf.js} +1 -1
  10. package/dist/motion-wasm/index.js +2 -2
  11. package/dist/motion-wasm/index.mjs +1 -1
  12. package/dist/{node-DF98pVP7.js → node--my8JTOW.js} +1 -1
  13. package/dist/{node-BSbB0or0.mjs → node-xRsaPwBa.mjs} +1 -1
  14. package/dist/pipeline-runner/index.js +255 -221
  15. package/dist/pipeline-runner/index.mjs +253 -219
  16. package/dist/{process-memory-BZTgJyE8.mjs → process-memory-BDHR3lxe.mjs} +1 -1
  17. package/dist/{process-memory-BVMk3gfY.js → process-memory-CoCSce63.js} +1 -1
  18. package/dist/recorder/index.js +319 -8
  19. package/dist/recorder/index.mjs +319 -8
  20. package/dist/{segment-demux-js-CLaSHw0n.js → segment-demux-js-CBynn9Gh.js} +1 -1
  21. package/dist/{segment-demux-js-0L1EF4Fg.mjs → segment-demux-js-CXvgKfsn.mjs} +1 -1
  22. package/dist/session-decode/decode-worker-child.js +2 -2
  23. package/dist/session-decode/decode-worker-child.mjs +1 -1
  24. package/dist/stream-broker/_stub.js +1 -1
  25. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BmNfrv_U.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CUjbLOm9.mjs} +3 -3
  26. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D8AK-_F6.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B_NVJXAy.mjs} +1 -1
  27. package/dist/stream-broker/demux-worker-child.js +1 -1
  28. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  29. package/dist/stream-broker/{hostInit-DQLtjntf.mjs → hostInit-DnBCyuSA.mjs} +3 -3
  30. package/dist/stream-broker/index.js +93 -43
  31. package/dist/stream-broker/index.mjs +93 -43
  32. package/dist/stream-broker/remoteEntry.js +1 -1
  33. package/dist/{worker-protocol-WiXU2VuW.mjs → worker-protocol-C0R1DDC0.mjs} +1 -1
  34. package/dist/{worker-protocol-BJzhw3t0.js → worker-protocol-DCsi4YS1.js} +1 -1
  35. package/package.json +1 -1
@@ -1,62 +1,17 @@
1
- import { B as defineCustomActions, Bt as nodePin, Ft as createEvent, Gt as _enum, H as deriveDetailCropRect, Kt as array, L as customAction, Qt as object, X as failureContributionCapability, Xt as literal, Yt as lazy, Zt as number, _ as FailureCounters, en as string, et as loadContributionCapability, ft as pipelineRunnerCapability, jt as BaseAddon, lt as pickDetailCropConvention, m as DEVICE_BACKEND_TO_FORMAT, nn as EventCategory, p as DEFAULT_NATIVE_LEASE_SETTINGS, qt as boolean, u as DEFAULT_DETAIL_CROP_CONVENTION, ut as pickNativeLeaseOverride, wt as errMsg } from "../dist-B8J7wrtY.mjs";
2
- import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-BSbB0or0.mjs";
3
- import { c as getStepDefinition, n as resolveFrameViewGeometry, t as localFrameRegistry } from "../local-frame-registry-BDeDpYQk.mjs";
4
- import { a as nativeLeaseSettingEnv, t as isWorkerReply } from "../worker-protocol-WiXU2VuW.mjs";
1
+ import { $t as number, Et as errMsg, Ht as nodePin, Jt as array, Lt as createEvent, Nt as BaseAddon, Qt as literal, R as customAction, U as deriveDetailCropRect, V as defineCustomActions, Yt as boolean, Z as failureContributionCapability, Zt as lazy, _ as FailureCounters, dt as pickNativeLeaseOverride, en as object, in as EventCategory, m as DEVICE_BACKEND_TO_FORMAT, nn as string, p as DEFAULT_NATIVE_LEASE_SETTINGS, pt as pipelineRunnerCapability, qt as _enum, tt as loadContributionCapability, u as DEFAULT_DETAIL_CROP_CONVENTION, ut as pickDetailCropConvention } from "../dist-mYDK58Jp.mjs";
2
+ import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-xRsaPwBa.mjs";
3
+ import { c as getStepDefinition, n as resolveFrameViewGeometry, t as localFrameRegistry } from "../local-frame-registry-av9ObDG0.mjs";
4
+ import { a as nativeLeaseSettingEnv, t as isWorkerReply } from "../worker-protocol-C0R1DDC0.mjs";
5
5
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
6
6
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
7
7
  import { n as withDetectionIntent } from "../restream-intent-B4BXZra7.mjs";
8
8
  import { t as attributeStall } from "../event-loop-stall-monitor-DXLNBMkY.mjs";
9
9
  import { n as profileForStreamId, t as isRemoteRestream } from "../remote-restream-BeHi78PZ.mjs";
10
+ import * as os from "node:os";
10
11
  import { fork } from "node:child_process";
11
12
  import sharp from "sharp";
12
13
  import { fileURLToPath } from "node:url";
13
14
  import { PerformanceObserver } from "node:perf_hooks";
14
- import * as os from "node:os";
15
- //#region src/pipeline-runner/first-detection-frame.ts
16
- /**
17
- * How long a camera waits, after `attachCamera`, before a decoded frame
18
- * actually reaches the detection queue.
19
- *
20
- * The orchestrator already measures `session motion → attach latency` and it
21
- * is small — 11, 13 and 18 ms across three consecutive sessions on the same
22
- * camera. What nothing measured is the NEXT leg: from the attach to the first
23
- * frame the detector is given. An operator reporting "detection starts late"
24
- * is describing that leg, and answering them required reading the timestamps
25
- * of unrelated INFO lines and inferring — which produced a wrong first answer
26
- * (the absence of a `frame processed` line means the frame produced nothing
27
- * notable, not that no frame arrived).
28
- *
29
- * One number, once per attach, so a slow start is a value rather than an
30
- * inference.
31
- */
32
- /** Pending attach marks, keyed by device. */
33
- var FirstDetectionFrameTimer = class {
34
- attachedAt = /* @__PURE__ */ new Map();
35
- /** A camera attached. Replaces any pending mark: the newest attach is the
36
- * one the next frame belongs to. */
37
- markAttached(deviceId, nowMs) {
38
- this.attachedAt.set(deviceId, nowMs);
39
- }
40
- /**
41
- * A decoded frame was admitted to the detection queue. Returns the elapsed
42
- * ms since the attach the FIRST time it is called after one, and `null`
43
- * every time after — so the caller logs once per attach, not once per frame.
44
- */
45
- takeLatency(deviceId, nowMs) {
46
- const at = this.attachedAt.get(deviceId);
47
- if (at === void 0) return null;
48
- this.attachedAt.delete(deviceId);
49
- return Math.max(0, nowMs - at);
50
- }
51
- /** A camera detached with no frame ever admitted — drop the mark so the
52
- * NEXT attach is not measured from this one. Returns whether one was
53
- * pending, which is itself the interesting fact: a session that ran and
54
- * fed the detector nothing. */
55
- forget(deviceId) {
56
- return this.attachedAt.delete(deviceId);
57
- }
58
- };
59
- //#endregion
60
15
  //#region src/session-decode/fork-decode-worker.ts
61
16
  /** Absolute path to the built decode-worker child entry (a vite build entry). */
62
17
  var CHILD_ENTRY_PATH = fileURLToPath(new URL("../session-decode/decode-worker-child.js", import.meta.url));
@@ -521,6 +476,57 @@ var NativeCropRegistry = class {
521
476
  }
522
477
  };
523
478
  //#endregion
479
+ //#region src/session-decode/node-decode-budget.ts
480
+ /**
481
+ * The decode RAM budget and worker cap THIS node runs under.
482
+ *
483
+ * `nodeDecodeBudgetMb` and `maxLiveDecodeWorkers` are cluster settings — one
484
+ * number for a 64 GB hub and a 16 GB Mac alike. Until 2026-09-05 the shipped
485
+ * 8192 MB was the hub's number applied everywhere; with the footprint fixed
486
+ * (D361) that admits 8 workers on the Mac, whose workers measure RSS p90
487
+ * 952 MB on a machine already at 80% — 7.6 GB of decode on 16 GB is a swap
488
+ * away from a stall — while the hub leaves 56 GB idle.
489
+ *
490
+ * Three sources, in order, and the winner is NAMED so a log line can say why:
491
+ * 1. the node's own agent setting (`decodeBudgetMb` / `maxLiveDecodeWorkers`
492
+ * in `AgentPipelineSettings`) — the operator sized this node;
493
+ * 2. the cluster setting when the operator MOVED it off the shipped default
494
+ * (a value equal to the default reads as "nobody chose", the same rule the
495
+ * native-lease knobs use, `readKnob`);
496
+ * 3. otherwise a share of the node's RAM, clamped — the shipped default
497
+ * becomes a per-node derivation instead of the hub's number.
498
+ */
499
+ var DECODE_BUDGET_RAM_SHARE = .5;
500
+ var DECODE_BUDGET_MIN_MB = 2048;
501
+ var DECODE_BUDGET_MAX_MB = 16384;
502
+ function derivedDecodeBudgetMb(totalMemMb) {
503
+ const share = Math.round(totalMemMb * DECODE_BUDGET_RAM_SHARE);
504
+ return Math.max(DECODE_BUDGET_MIN_MB, Math.min(DECODE_BUDGET_MAX_MB, share));
505
+ }
506
+ function resolveNodeDecodeLimits(input) {
507
+ const budget = input.node.decodeBudgetMb !== void 0 ? {
508
+ budgetMb: input.node.decodeBudgetMb,
509
+ budgetSource: "node"
510
+ } : input.clusterBudgetMb !== input.defaultBudgetMb ? {
511
+ budgetMb: input.clusterBudgetMb,
512
+ budgetSource: "cluster"
513
+ } : {
514
+ budgetMb: derivedDecodeBudgetMb(input.totalMemMb),
515
+ budgetSource: "derived"
516
+ };
517
+ const workers = input.node.maxLiveDecodeWorkers !== void 0 ? {
518
+ maxWorkers: input.node.maxLiveDecodeWorkers,
519
+ maxWorkersSource: "node"
520
+ } : {
521
+ maxWorkers: input.clusterMaxWorkers,
522
+ maxWorkersSource: "cluster"
523
+ };
524
+ return {
525
+ ...budget,
526
+ ...workers
527
+ };
528
+ }
529
+ //#endregion
524
530
  //#region src/session-decode/retained-content-fallback.ts
525
531
  /**
526
532
  * Honest ≤640 RAM fallback for `getNativeCrop` full-frame misses.
@@ -922,55 +928,6 @@ function cropRgb(frame, bbox, maxWidth) {
922
928
  };
923
929
  }
924
930
  //#endregion
925
- //#region src/pipeline-runner/frame-cutter-binding.ts
926
- /** The per-frame binding key — the same identity the retention handle carries. */
927
- function frameCutterKey(handle) {
928
- return `${handle.shmId}#${handle.slot}#${handle.seq}`;
929
- }
930
- var FrameCutterBinding = class {
931
- max;
932
- map = /* @__PURE__ */ new Map();
933
- constructor(max) {
934
- this.max = max;
935
- }
936
- /** Bind at delivery. Re-binding the same handle replaces without stranding the first. */
937
- bind(handle, cutter) {
938
- const key = frameCutterKey(handle);
939
- this.map.delete(key);
940
- this.map.set(key, cutter);
941
- while (this.map.size > this.max) {
942
- const oldest = this.map.keys().next().value;
943
- if (oldest === void 0) break;
944
- const stale = this.map.get(oldest);
945
- this.map.delete(oldest);
946
- release(stale);
947
- }
948
- }
949
- /** Consume the binding for a frame whose result arrived. `undefined` when already ended. */
950
- take(handle) {
951
- const key = frameCutterKey(handle);
952
- const cutter = this.map.get(key);
953
- this.map.delete(key);
954
- return cutter;
955
- }
956
- /**
957
- * End the hold of a frame the runner let go without a result. A no-op for a
958
- * frame whose result already consumed the binding, so the two paths cannot
959
- * double-release.
960
- */
961
- drop(handle) {
962
- release(this.take(handle));
963
- }
964
- get size() {
965
- return this.map.size;
966
- }
967
- };
968
- function release(cutter) {
969
- try {
970
- cutter?.release();
971
- } catch {}
972
- }
973
- //#endregion
974
931
  //#region src/session-decode/session-decode-coordinator.ts
975
932
  /** How long to wait after `kill()` before escalating to `kill('SIGKILL')`. */
976
933
  var SIGKILL_FALLBACK_MS = 1e3;
@@ -2920,6 +2877,168 @@ function summarizeDeviceLoad(attachedDeviceKeys) {
2920
2877
  })).toSorted((a, b) => a.deviceKey.localeCompare(b.deviceKey));
2921
2878
  }
2922
2879
  //#endregion
2880
+ //#region src/pipeline-runner/failure-report.ts
2881
+ /**
2882
+ * What `pipeline-runner` reports through `failure-contribution` — the inference
2883
+ * deadline, per camera, with the frames that made it as the denominator.
2884
+ *
2885
+ * ## The number that was never divided
2886
+ *
2887
+ * ~120 `inference timed out — permit released, result discarded` lines in six
2888
+ * hours across seven cameras, on 2026-08-28, never investigated. The line has
2889
+ * carried `tags: { deviceId }` for a while, so the count per camera was always
2890
+ * available; what was not available was the number to divide it by. 120
2891
+ * timeouts out of 120 000 admitted frames and 120 out of 1 200 are opposite
2892
+ * findings and produce identical log volume.
2893
+ *
2894
+ * So the counter takes BOTH outcomes of the same await, from the same place in
2895
+ * `awaitInferenceBounded`. A success counted anywhere else would eventually
2896
+ * drift from the failure and turn the ratio into fiction.
2897
+ *
2898
+ * ## Why there is no `variant` yet
2899
+ *
2900
+ * `failure-contribution` has a `variant` slot exactly so "which camera AND
2901
+ * which model" is one read, and the operator asked for it. The runner cannot
2902
+ * fill it honestly: `awaitInferenceBounded` wraps
2903
+ * `config.processFrame(deviceId, frameInput, executorHandle)`, which runs the
2904
+ * camera's WHOLE pipeline graph behind one deadline. Nothing at this seam knows
2905
+ * which step was still running when the 20 s expired — `CameraRegistration`
2906
+ * carries fps and mode, not models.
2907
+ *
2908
+ * Naming the step means the executor attributing the deadline to the step that
2909
+ * held it, and reporting THAT. Emitting a `variant` the runner guessed (the
2910
+ * camera's configured graph id, say) would be worse than leaving it absent: it
2911
+ * would look like an attribution and be a label. Absent, never invented — the
2912
+ * same rule `load-contribution` states for a measurement nobody made.
2913
+ */
2914
+ /** One bounded inference deadline per admitted detection frame. */
2915
+ var FAMILY_INFERENCE = "inference";
2916
+ /** The call did not return inside `inferenceTimeoutMs`. */
2917
+ var REASON_INFERENCE_TIMEOUT = "timeout";
2918
+ /**
2919
+ * The runner's `failure-contribution` counters.
2920
+ *
2921
+ * One instance per runner process, read (never drained) by the provider.
2922
+ */
2923
+ var RunnerFailureReport = class {
2924
+ now;
2925
+ counters;
2926
+ constructor(now = Date.now, counters = new FailureCounters()) {
2927
+ this.now = now;
2928
+ this.counters = counters;
2929
+ }
2930
+ /** One bounded inference call. `reason` absent = it returned in time. */
2931
+ noteInference(deviceId, reason) {
2932
+ this.counters.note({
2933
+ deviceId,
2934
+ family: FAMILY_INFERENCE,
2935
+ ...reason !== void 0 ? { reason } : {}
2936
+ }, this.now());
2937
+ }
2938
+ /** The `failure-contribution` provider's payload. Reads, never resets. */
2939
+ list() {
2940
+ return this.counters.snapshot(this.now());
2941
+ }
2942
+ /** Addon disposal. */
2943
+ clear() {
2944
+ this.counters.clear();
2945
+ }
2946
+ };
2947
+ //#endregion
2948
+ //#region src/pipeline-runner/first-detection-frame.ts
2949
+ /**
2950
+ * How long a camera waits, after `attachCamera`, before a decoded frame
2951
+ * actually reaches the detection queue.
2952
+ *
2953
+ * The orchestrator already measures `session motion → attach latency` and it
2954
+ * is small — 11, 13 and 18 ms across three consecutive sessions on the same
2955
+ * camera. What nothing measured is the NEXT leg: from the attach to the first
2956
+ * frame the detector is given. An operator reporting "detection starts late"
2957
+ * is describing that leg, and answering them required reading the timestamps
2958
+ * of unrelated INFO lines and inferring — which produced a wrong first answer
2959
+ * (the absence of a `frame processed` line means the frame produced nothing
2960
+ * notable, not that no frame arrived).
2961
+ *
2962
+ * One number, once per attach, so a slow start is a value rather than an
2963
+ * inference.
2964
+ */
2965
+ /** Pending attach marks, keyed by device. */
2966
+ var FirstDetectionFrameTimer = class {
2967
+ attachedAt = /* @__PURE__ */ new Map();
2968
+ /** A camera attached. Replaces any pending mark: the newest attach is the
2969
+ * one the next frame belongs to. */
2970
+ markAttached(deviceId, nowMs) {
2971
+ this.attachedAt.set(deviceId, nowMs);
2972
+ }
2973
+ /**
2974
+ * A decoded frame was admitted to the detection queue. Returns the elapsed
2975
+ * ms since the attach the FIRST time it is called after one, and `null`
2976
+ * every time after — so the caller logs once per attach, not once per frame.
2977
+ */
2978
+ takeLatency(deviceId, nowMs) {
2979
+ const at = this.attachedAt.get(deviceId);
2980
+ if (at === void 0) return null;
2981
+ this.attachedAt.delete(deviceId);
2982
+ return Math.max(0, nowMs - at);
2983
+ }
2984
+ /** A camera detached with no frame ever admitted — drop the mark so the
2985
+ * NEXT attach is not measured from this one. Returns whether one was
2986
+ * pending, which is itself the interesting fact: a session that ran and
2987
+ * fed the detector nothing. */
2988
+ forget(deviceId) {
2989
+ return this.attachedAt.delete(deviceId);
2990
+ }
2991
+ };
2992
+ //#endregion
2993
+ //#region src/pipeline-runner/frame-cutter-binding.ts
2994
+ /** The per-frame binding key — the same identity the retention handle carries. */
2995
+ function frameCutterKey(handle) {
2996
+ return `${handle.shmId}#${handle.slot}#${handle.seq}`;
2997
+ }
2998
+ var FrameCutterBinding = class {
2999
+ max;
3000
+ map = /* @__PURE__ */ new Map();
3001
+ constructor(max) {
3002
+ this.max = max;
3003
+ }
3004
+ /** Bind at delivery. Re-binding the same handle replaces without stranding the first. */
3005
+ bind(handle, cutter) {
3006
+ const key = frameCutterKey(handle);
3007
+ this.map.delete(key);
3008
+ this.map.set(key, cutter);
3009
+ while (this.map.size > this.max) {
3010
+ const oldest = this.map.keys().next().value;
3011
+ if (oldest === void 0) break;
3012
+ const stale = this.map.get(oldest);
3013
+ this.map.delete(oldest);
3014
+ release(stale);
3015
+ }
3016
+ }
3017
+ /** Consume the binding for a frame whose result arrived. `undefined` when already ended. */
3018
+ take(handle) {
3019
+ const key = frameCutterKey(handle);
3020
+ const cutter = this.map.get(key);
3021
+ this.map.delete(key);
3022
+ return cutter;
3023
+ }
3024
+ /**
3025
+ * End the hold of a frame the runner let go without a result. A no-op for a
3026
+ * frame whose result already consumed the binding, so the two paths cannot
3027
+ * double-release.
3028
+ */
3029
+ drop(handle) {
3030
+ release(this.take(handle));
3031
+ }
3032
+ get size() {
3033
+ return this.map.size;
3034
+ }
3035
+ };
3036
+ function release(cutter) {
3037
+ try {
3038
+ cutter?.release();
3039
+ } catch {}
3040
+ }
3041
+ //#endregion
2923
3042
  //#region src/pipeline-runner/frame-lazy-metrics.ts
2924
3043
  function emptyFrameLazyCounters() {
2925
3044
  return {
@@ -3492,57 +3611,6 @@ var NodeDecodeLimitsSource = class {
3492
3611
  }
3493
3612
  };
3494
3613
  //#endregion
3495
- //#region src/session-decode/node-decode-budget.ts
3496
- /**
3497
- * The decode RAM budget and worker cap THIS node runs under.
3498
- *
3499
- * `nodeDecodeBudgetMb` and `maxLiveDecodeWorkers` are cluster settings — one
3500
- * number for a 64 GB hub and a 16 GB Mac alike. Until 2026-09-05 the shipped
3501
- * 8192 MB was the hub's number applied everywhere; with the footprint fixed
3502
- * (D361) that admits 8 workers on the Mac, whose workers measure RSS p90
3503
- * 952 MB on a machine already at 80% — 7.6 GB of decode on 16 GB is a swap
3504
- * away from a stall — while the hub leaves 56 GB idle.
3505
- *
3506
- * Three sources, in order, and the winner is NAMED so a log line can say why:
3507
- * 1. the node's own agent setting (`decodeBudgetMb` / `maxLiveDecodeWorkers`
3508
- * in `AgentPipelineSettings`) — the operator sized this node;
3509
- * 2. the cluster setting when the operator MOVED it off the shipped default
3510
- * (a value equal to the default reads as "nobody chose", the same rule the
3511
- * native-lease knobs use, `readKnob`);
3512
- * 3. otherwise a share of the node's RAM, clamped — the shipped default
3513
- * becomes a per-node derivation instead of the hub's number.
3514
- */
3515
- var DECODE_BUDGET_RAM_SHARE = .5;
3516
- var DECODE_BUDGET_MIN_MB = 2048;
3517
- var DECODE_BUDGET_MAX_MB = 16384;
3518
- function derivedDecodeBudgetMb(totalMemMb) {
3519
- const share = Math.round(totalMemMb * DECODE_BUDGET_RAM_SHARE);
3520
- return Math.max(DECODE_BUDGET_MIN_MB, Math.min(DECODE_BUDGET_MAX_MB, share));
3521
- }
3522
- function resolveNodeDecodeLimits(input) {
3523
- const budget = input.node.decodeBudgetMb !== void 0 ? {
3524
- budgetMb: input.node.decodeBudgetMb,
3525
- budgetSource: "node"
3526
- } : input.clusterBudgetMb !== input.defaultBudgetMb ? {
3527
- budgetMb: input.clusterBudgetMb,
3528
- budgetSource: "cluster"
3529
- } : {
3530
- budgetMb: derivedDecodeBudgetMb(input.totalMemMb),
3531
- budgetSource: "derived"
3532
- };
3533
- const workers = input.node.maxLiveDecodeWorkers !== void 0 ? {
3534
- maxWorkers: input.node.maxLiveDecodeWorkers,
3535
- maxWorkersSource: "node"
3536
- } : {
3537
- maxWorkers: input.clusterMaxWorkers,
3538
- maxWorkersSource: "cluster"
3539
- };
3540
- return {
3541
- ...budget,
3542
- ...workers
3543
- };
3544
- }
3545
- //#endregion
3546
3614
  //#region src/session-decode/on-demand-jpeg-fallback.ts
3547
3615
  /** Single content-sized JPEG requested after a detection, never a continuous raw. */
3548
3616
  var ON_DEMAND_JPEG_VIEW = {
@@ -3888,74 +3956,6 @@ function packageZoneUnion(polygons) {
3888
3956
  };
3889
3957
  }
3890
3958
  //#endregion
3891
- //#region src/pipeline-runner/failure-report.ts
3892
- /**
3893
- * What `pipeline-runner` reports through `failure-contribution` — the inference
3894
- * deadline, per camera, with the frames that made it as the denominator.
3895
- *
3896
- * ## The number that was never divided
3897
- *
3898
- * ~120 `inference timed out — permit released, result discarded` lines in six
3899
- * hours across seven cameras, on 2026-08-28, never investigated. The line has
3900
- * carried `tags: { deviceId }` for a while, so the count per camera was always
3901
- * available; what was not available was the number to divide it by. 120
3902
- * timeouts out of 120 000 admitted frames and 120 out of 1 200 are opposite
3903
- * findings and produce identical log volume.
3904
- *
3905
- * So the counter takes BOTH outcomes of the same await, from the same place in
3906
- * `awaitInferenceBounded`. A success counted anywhere else would eventually
3907
- * drift from the failure and turn the ratio into fiction.
3908
- *
3909
- * ## Why there is no `variant` yet
3910
- *
3911
- * `failure-contribution` has a `variant` slot exactly so "which camera AND
3912
- * which model" is one read, and the operator asked for it. The runner cannot
3913
- * fill it honestly: `awaitInferenceBounded` wraps
3914
- * `config.processFrame(deviceId, frameInput, executorHandle)`, which runs the
3915
- * camera's WHOLE pipeline graph behind one deadline. Nothing at this seam knows
3916
- * which step was still running when the 20 s expired — `CameraRegistration`
3917
- * carries fps and mode, not models.
3918
- *
3919
- * Naming the step means the executor attributing the deadline to the step that
3920
- * held it, and reporting THAT. Emitting a `variant` the runner guessed (the
3921
- * camera's configured graph id, say) would be worse than leaving it absent: it
3922
- * would look like an attribution and be a label. Absent, never invented — the
3923
- * same rule `load-contribution` states for a measurement nobody made.
3924
- */
3925
- /** One bounded inference deadline per admitted detection frame. */
3926
- var FAMILY_INFERENCE = "inference";
3927
- /** The call did not return inside `inferenceTimeoutMs`. */
3928
- var REASON_INFERENCE_TIMEOUT = "timeout";
3929
- /**
3930
- * The runner's `failure-contribution` counters.
3931
- *
3932
- * One instance per runner process, read (never drained) by the provider.
3933
- */
3934
- var RunnerFailureReport = class {
3935
- now;
3936
- counters;
3937
- constructor(now = Date.now, counters = new FailureCounters()) {
3938
- this.now = now;
3939
- this.counters = counters;
3940
- }
3941
- /** One bounded inference call. `reason` absent = it returned in time. */
3942
- noteInference(deviceId, reason) {
3943
- this.counters.note({
3944
- deviceId,
3945
- family: FAMILY_INFERENCE,
3946
- ...reason !== void 0 ? { reason } : {}
3947
- }, this.now());
3948
- }
3949
- /** The `failure-contribution` provider's payload. Reads, never resets. */
3950
- list() {
3951
- return this.counters.snapshot(this.now());
3952
- }
3953
- /** Addon disposal. */
3954
- clear() {
3955
- this.counters.clear();
3956
- }
3957
- };
3958
- //#endregion
3959
3959
  //#region src/pipeline-runner/frame-governor.ts
3960
3960
  var FrameGovernor = class {
3961
3961
  config;
@@ -5265,6 +5265,40 @@ function isUsable(bbox) {
5265
5265
  //#endregion
5266
5266
  //#region src/pipeline-runner/index.ts
5267
5267
  /**
5268
+ * addon-pipeline-runner — per-node detection scheduler.
5269
+ *
5270
+ * Lives on every "vision node" (hub if hub is one, every agent that runs
5271
+ * decoders/cameras). Receives camera assignments from the hub-side
5272
+ * `addon-pipeline-orchestrator` via cap calls, owns the per-camera frame
5273
+ * queues + semaphore + motion → detection phase machine, calls the local
5274
+ * `motion-detection` and `pipeline-executor` capabilities for the actual
5275
+ * work, and emits typed events on the bus
5276
+ * (`detection.motion-analysis`, `pipeline.inference-result`, `pipeline.trace`).
5277
+ *
5278
+ * Design notes:
5279
+ *
5280
+ * - All cap inputs/outputs are serializable so the runner is transparently
5281
+ * routable across the cluster: when the runner is co-located with the
5282
+ * broker/decoder/motion/detection (the default and recommended deploy),
5283
+ * capability lookups return live singletons and frame data never crosses
5284
+ * a process boundary. When a deploy splits the runner from the executor
5285
+ * (rare), the lookup returns null and the runner falls back to
5286
+ * `this.ctx.api` (broker proxy) at the cost of serialization.
5287
+ *
5288
+ * - The scheduler core (`PipelineRunner`) is callback-driven and framework
5289
+ * agnostic. The addon class injects the inference + motion callbacks at
5290
+ * construction time, capturing references to the relevant providers
5291
+ * lazily on each invocation.
5292
+ *
5293
+ * - Result post-processing (analysis pipeline, class filters, notifications)
5294
+ * is intentionally NOT done by the runner. The runner emits the raw
5295
+ * inference output as a `pipeline.inference-result` event on the bus;
5296
+ * a hub-side subscriber consumes the event and runs the post-processing
5297
+ * pipeline. This keeps the runner stateless w.r.t. detection-side
5298
+ * business logic and makes it possible to deploy the post-processor
5299
+ * independently from the inference workers.
5300
+ */
5301
+ /**
5268
5302
  * How often a device's package zone-union geometry is re-read. An operator
5269
5303
  * redrawing a package zone takes effect within one interval; the read is a
5270
5304
  * cross-addon cap call reached from the per-frame package gate, so it must be
@@ -7823,6 +7857,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
7823
7857
  workerToRunnerBytes: 0
7824
7858
  });
7825
7859
  const admitted = current.detection && runner.enqueueReservedDetectionFrame(config.deviceId, frame, handle);
7860
+ if (admitted) nativeCrop?.markInferred();
7826
7861
  if (current.motion) {
7827
7862
  const motion = await resolveMotionFrameView(localFrameRegistry, frame.frameRef);
7828
7863
  if (motion.kind === "view") runner.enqueueMotionFrame(config.deviceId, {
@@ -7834,7 +7869,6 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
7834
7869
  capturedAt: frame.capturedAt
7835
7870
  }, void 0);
7836
7871
  }
7837
- if (admitted) nativeCrop?.markInferred();
7838
7872
  return admitted;
7839
7873
  }
7840
7874
  } : { inlineViewSpec: SPLIT_DETECTION_VIEW },
@@ -1,4 +1,4 @@
1
- import { ot as parseProcStatus } from "./dist-B8J7wrtY.mjs";
1
+ import { st as parseProcStatus } from "./dist-mYDK58Jp.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import { execFile } from "node:child_process";
4
4
  //#region src/shared/node-topology-platform.ts
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-CXO_Fmx6.js");
1
+ const require_dist = require("./dist-HG5geL9t.js");
2
2
  let node_fs = require("node:fs");
3
3
  node_fs = require_dist.__toESM(node_fs);
4
4
  let node_child_process = require("node:child_process");