@trillboards/edge-sdk 0.2.14 → 0.2.16

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 (58) hide show
  1. package/deploy/windows/README.md +7 -0
  2. package/deploy/windows/build-windows.ps1 +4 -0
  3. package/deploy/windows/ensure-sensing.ps1 +58 -0
  4. package/deploy/windows/install-update.ps1 +32 -6
  5. package/deploy/windows/stop-existing.ps1 +51 -1
  6. package/deploy/windows/trillboards-edge.cmd +8 -2
  7. package/deploy/windows/trillboards-hidden-run.vbs +47 -0
  8. package/deploy/windows/trillboards-setup.ps1 +25 -0
  9. package/dist/AudienceAnalyzeClient.d.ts +48 -4
  10. package/dist/AudienceAnalyzeClient.d.ts.map +1 -1
  11. package/dist/AudienceAnalyzeClient.js +33 -2
  12. package/dist/AudienceAnalyzeClient.js.map +1 -1
  13. package/dist/CloudSensingManager.d.ts +157 -7
  14. package/dist/CloudSensingManager.d.ts.map +1 -1
  15. package/dist/CloudSensingManager.js +392 -23
  16. package/dist/CloudSensingManager.js.map +1 -1
  17. package/dist/EdgeAgent.js +1 -1
  18. package/dist/SceneChangeDetector.d.ts +39 -3
  19. package/dist/SceneChangeDetector.d.ts.map +1 -1
  20. package/dist/SceneChangeDetector.js +84 -16
  21. package/dist/SceneChangeDetector.js.map +1 -1
  22. package/dist/VlmAdmissionGate.d.ts +14 -0
  23. package/dist/VlmAdmissionGate.d.ts.map +1 -1
  24. package/dist/VlmAdmissionGate.js +14 -0
  25. package/dist/VlmAdmissionGate.js.map +1 -1
  26. package/dist/VlmCandidateBuffer.d.ts +247 -0
  27. package/dist/VlmCandidateBuffer.d.ts.map +1 -0
  28. package/dist/VlmCandidateBuffer.js +349 -0
  29. package/dist/VlmCandidateBuffer.js.map +1 -0
  30. package/dist/config.d.ts +46 -0
  31. package/dist/config.d.ts.map +1 -1
  32. package/dist/config.js.map +1 -1
  33. package/dist/diagnose.d.ts +1 -1
  34. package/dist/diagnose.js +1 -1
  35. package/dist/discovery.d.ts +15 -0
  36. package/dist/discovery.d.ts.map +1 -1
  37. package/dist/discovery.js +65 -0
  38. package/dist/discovery.js.map +1 -1
  39. package/dist/index.d.ts +4 -2
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +7 -1
  42. package/dist/index.js.map +1 -1
  43. package/dist/localSensing.d.ts +2 -2
  44. package/dist/localSensing.d.ts.map +1 -1
  45. package/dist/localSensing.js +16 -0
  46. package/dist/localSensing.js.map +1 -1
  47. package/dist/recorderRanking.d.ts +71 -0
  48. package/dist/recorderRanking.d.ts.map +1 -0
  49. package/dist/recorderRanking.js +230 -0
  50. package/dist/recorderRanking.js.map +1 -0
  51. package/dist/sensingRunMode.d.ts.map +1 -1
  52. package/dist/sensingRunMode.js +40 -0
  53. package/dist/sensingRunMode.js.map +1 -1
  54. package/dist/setupMode.d.ts +9 -0
  55. package/dist/setupMode.d.ts.map +1 -1
  56. package/dist/setupMode.js +23 -1
  57. package/dist/setupMode.js.map +1 -1
  58. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"VlmAdmissionGate.js","sourceRoot":"","sources":["../src/VlmAdmissionGate.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;;;AAoDH,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAChC,MAAM,wBAAwB,GAAG,MAAO,CAAC;AAEzC,MAAa,gBAAgB;IAgB3B,YAAY,UAAmC,EAAE;QAVhC,aAAQ,GAAG,IAAI,GAAG,EAAsB,CAAC;QAClD,oBAAe,GAAG,CAAC,CAAC;QAEpB,aAAQ,GAAG,CAAC,CAAC;QACb,oBAAe,GAAG,CAAC,CAAC;QACpB,uBAAkB,GAAG,CAAC,CAAC;QACvB,mBAAc,GAAG,CAAC,CAAC;QACnB,iBAAY,GAAG,CAAC,CAAC;QACR,YAAO,GAAG,IAAI,GAAG,EAAiC,CAAC;QAGlE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,IAAI,qBAAqB,CAAC,CAAC,CAAC;QACzF,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,IAAI,CAAC,CAAC,CAAC,CAAC;QACzE,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,CAC3B,CAAC,EACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,IAAI,wBAAwB,CAAC,CAC9D,CAAC;QACF,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACrC,CAAC;IAED,kDAAkD;IAClD,cAAc;QACZ,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED,8DAA8D;IAC9D,WAAW,CAAC,KAAc;QACxB,IAAI,CAAC,YAAY,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACvC,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACH,UAAU,CAAC,IAAY,EAAE,KAAc;QACrC,MAAM,GAAG,GAAG,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QAChC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QAEvB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAC3C,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,GAAG,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;YAC/B,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1B,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,MAAM,GAAuB,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC;QAC/D,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1B,IAAI,CAAC,eAAe,GAAG,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC;QAChD,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,IAAI,CAAC,gBAAgB,GAAG,GAAG,CAAC;QAC5B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,MAA6C;QACnD,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YAClC,IAAI,CAAC,YAAY,EAAE,CAAC;QACtB,CAAC;IACH,CAAC;IAED,2DAA2D;IAC3D,mBAAmB,CAAC,KAAc;QAChC,MAAM,GAAG,GAAG,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QAChC,IAAI,MAAM,GAAkB,IAAI,CAAC;QACjC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;YAC9C,IAAI,MAAM,KAAK,IAAI,IAAI,GAAG,GAAG,MAAM;gBAAE,MAAM,GAAG,GAAG,CAAC;QACpD,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,yEAAyE;IACzE,KAAK;QACH,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAClB,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC;QACxB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED,QAAQ,CAAC,KAAc;QACrB,IAAI,CAAC,YAAY,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACvC,OAAO;YACL,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI;YAC5B,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,eAAe,EAAE,IAAI,CAAC,eAAe;YACrC,kBAAkB,EAAE,IAAI,CAAC,kBAAkB;YAC3C,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;SAC3D,CAAC;IACJ,CAAC;IAED,+EAA+E;IAEvE,SAAS,CAAC,IAAY;QAC5B,IAAI,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC;YACjE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACK,YAAY,CAAC,KAAa;QAChC,IAAI,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO;QAChE,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC9B,IAAI,KAAK,GAAG,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;gBAChD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBACxB,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,CAAC;QACH,CAAC;IACH,CAAC;CACF;AA3ID,4CA2IC"}
1
+ {"version":3,"file":"VlmAdmissionGate.js","sourceRoot":"","sources":["../src/VlmAdmissionGate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,247 @@
1
+ /**
2
+ * VlmCandidateBuffer — the bounded hold between the cascade and the admission
3
+ * gate, for frames the cascade has ALREADY decided are interesting.
4
+ *
5
+ * WHY THIS EXISTS, AND WHY IT IS NOT THE QUEUE THE GATE REFUSES.
6
+ * `VlmAdmissionGate` drops rather than queues, and its reason is correct as
7
+ * stated: "by the time a queued frame reached the model the scene has moved
8
+ * on." That is a statement about AMBIENT SAMPLING, where every frame is
9
+ * interchangeable and the newest one is strictly the best one, so holding an
10
+ * old frame is pure loss.
11
+ *
12
+ * Alerting is the other case, and alerting is the product. By the time a frame
13
+ * reaches this buffer the motion cascade has already spent its decode and its
14
+ * detector and concluded THIS frame is different from the scene it had — a
15
+ * person walked in, something fell, an aisle that was empty is not. Those
16
+ * frames are not interchangeable with the next one: at a 30 s scan cadence the
17
+ * next look is a different event, and on a saturated recorder it may be
18
+ * thirteen minutes away. Dropping the one frame that carried the event and
19
+ * later analysing an empty aisle is not "staying fresh"; it is answering a
20
+ * question nobody asked while the one that mattered went unrecorded.
21
+ *
22
+ * So the buffer holds CANDIDATES ONLY. A frame the cascade classified as
23
+ * no-change must never enter — that would rebuild exactly the undifferentiated
24
+ * backlog the gate exists to prevent, and the caller is responsible for that
25
+ * invariant because only the caller has the cascade verdict.
26
+ *
27
+ * THREE BOUNDS, ALL COUNTED. A hold that is unbounded in size is a memory leak
28
+ * and a hold that is unbounded in age is the gate's original complaint. So:
29
+ *
30
+ * - `maxPerFeed` — one busy camera can never hold more than its share, which
31
+ * is what keeps camera 12 from starving the other 31.
32
+ * - `maxFrames` / `maxBytes` — a hard ceiling on the whole buffer, enforced
33
+ * on the way IN. When it binds, the candidate dropped is the OLDEST of the
34
+ * feed that currently holds the MOST, so pressure is paid by whoever is
35
+ * causing it.
36
+ * - `ttlMs` — past this age the gate's insight takes over and the candidate
37
+ * is dropped as expired rather than analysed.
38
+ *
39
+ * Every one of those three drops is COUNTED. A silent eviction is the same
40
+ * defect as the silent drop this class was built to fix — it would move the
41
+ * lost frame from one invisible place to another.
42
+ *
43
+ * SERVICE ORDER is least-recently-served feed first, oldest candidate within
44
+ * that feed. Across feeds that is round-robin, so service cannot be won by
45
+ * whichever timer happens to fire first. Within a feed it is FIFO, because
46
+ * consecutive candidates from one feed are frames of the SAME event and the
47
+ * earliest one is the one that shows the transition — a person entering a
48
+ * restricted area is visible in the frame where they enter, and by the last
49
+ * frame of the burst they may already be out of view.
50
+ *
51
+ * THREADING. Node is single-threaded and no method here awaits, so `peek()`
52
+ * followed by `take()` is one critical section by construction: they are
53
+ * guaranteed to name the same candidate. `CloudSensingManager.pumpCandidates`
54
+ * depends on that — it peeks to learn the feed, acquires an admission slot for
55
+ * it, and only then commits with `take()`.
56
+ */
57
+ /** One frame the cascade judged worth an inference, waiting for a slot. */
58
+ export interface VlmCandidate {
59
+ /** Feed the frame came from. */
60
+ readonly name: string;
61
+ /** The encoded frame, exactly as the capture path produced it. */
62
+ readonly jpeg: Buffer;
63
+ /** Clock reading when the cascade admitted it as a candidate. */
64
+ readonly enqueuedAtMs: number;
65
+ /** The capture mode the tick would have POSTed with. */
66
+ readonly captureMode: string;
67
+ /** The cascade reason that made this a candidate (diagnostics). */
68
+ readonly reason: string;
69
+ /**
70
+ * The cascade's stationary-suppression key for this frame. Carried rather
71
+ * than recomputed so that when the frame finally reaches the model, THAT
72
+ * frame's baseline is the one committed — a held candidate can be served long
73
+ * after later frames from the same feed were evaluated.
74
+ */
75
+ readonly signature: string | null;
76
+ /**
77
+ * The dimensions THIS frame was decoded at, carried rather than re-read.
78
+ *
79
+ * A held candidate can be served long after the camera moved on — and on a
80
+ * feed that renegotiates resolution, asking the camera at serve time would
81
+ * describe a frame that is not the one in `jpeg`. Same reasoning as
82
+ * `signature`: the datum belongs to the frame, so it travels with it.
83
+ * Optional because a provider need not implement `getResolution()`.
84
+ */
85
+ readonly imageWidth?: number;
86
+ readonly imageHeight?: number;
87
+ }
88
+ export interface VlmCandidateBufferOptions {
89
+ /**
90
+ * Candidates held across ALL feeds. Default 32 — one per feed on the
91
+ * 32-camera recorder this was sized against, so the common case is "every
92
+ * camera may hold its event" and contention only appears when several fire
93
+ * at once.
94
+ */
95
+ maxFrames?: number;
96
+ /**
97
+ * Candidates one feed may hold. Default 2 — the onset frame plus one
98
+ * follow-up. Motion is temporally clustered, so a third frame of the same
99
+ * burst is a near-duplicate of the second and buys nothing a slot could not.
100
+ */
101
+ maxPerFeed?: number;
102
+ /**
103
+ * Total encoded bytes held. Default 16 MiB. This is the bound that stays
104
+ * true when the frame size changes: measured on the production CCTV fixtures
105
+ * at `jpegQuality` 70, a 640x480 frame encodes to ~54 KB and a 1920x1080
106
+ * frame to ~199 KB, so 32 candidates is ~1.7 MB at the default capture size
107
+ * and ~6.2 MB at 1080p — the FRAME cap binds first at every realistic
108
+ * resolution, and the byte cap is what keeps that true if it stops being.
109
+ */
110
+ maxBytes?: number;
111
+ /**
112
+ * How long a candidate stays worth a slot (ms). Default 90000 — see
113
+ * DEFAULT_CANDIDATE_TTL_MS.
114
+ */
115
+ ttlMs?: number;
116
+ /** Clock injection for tests. Default `Date.now`. */
117
+ now?: () => number;
118
+ }
119
+ export interface VlmCandidateFeedStats {
120
+ name: string;
121
+ /** Candidates this feed is holding right now. */
122
+ buffered: number;
123
+ enqueued: number;
124
+ served: number;
125
+ evicted: number;
126
+ expired: number;
127
+ }
128
+ export interface VlmCandidateBufferStats {
129
+ /** Candidates held right now, across all feeds. */
130
+ buffered: number;
131
+ /** Encoded bytes held right now. */
132
+ bytes: number;
133
+ maxFrames: number;
134
+ maxPerFeed: number;
135
+ maxBytes: number;
136
+ ttlMs: number;
137
+ /** Cumulative candidates accepted. */
138
+ enqueued: number;
139
+ /** Cumulative candidates handed to the model. */
140
+ served: number;
141
+ /** Cumulative candidates dropped to make room — the fairness cost, visible. */
142
+ evicted: number;
143
+ /** Cumulative candidates dropped for being older than `ttlMs`. */
144
+ expired: number;
145
+ /** Age (ms) of the oldest held candidate, or null when empty. */
146
+ oldestAgeMs: number | null;
147
+ perFeed: VlmCandidateFeedStats[];
148
+ }
149
+ /**
150
+ * DEFAULT_CANDIDATE_TTL_MS — how long an alert is still an alert.
151
+ *
152
+ * The admission gate's sentence is right at a long enough horizon: a frame the
153
+ * model looks at ten minutes late describes a scene that no longer exists, and
154
+ * the slot it burns would have been better spent on something current. The
155
+ * only question is where that horizon actually sits, and the answer comes from
156
+ * what the frame is FOR, not from how fresh it is in the abstract.
157
+ *
158
+ * The events this connector exists to catch — a spill, a fall, someone in a
159
+ * restricted area — persist on the order of a minute or two, and the response
160
+ * to one is a person walking over to look. An alert that lands 90 s late still
161
+ * finds the spill on the floor and the person still down, and is worth every
162
+ * bit of the slot it takes. One that lands ten minutes late is a report rather
163
+ * than an alert: either somebody already noticed or nobody is going to.
164
+ *
165
+ * 90 s is also three full 30 s face-triggered cadences, so a candidate
166
+ * survives a couple of rounds of contention on a saturated recorder, and it is
167
+ * well inside the 5 min still-scene heartbeat, so a held candidate can never
168
+ * outlive the feed's own next guaranteed report. The buffer may delay a frame;
169
+ * it can never stand in for the cadence.
170
+ */
171
+ export declare const DEFAULT_CANDIDATE_TTL_MS = 90000;
172
+ /** Why an `offer()` did not result in a held candidate. */
173
+ export type VlmCandidateOfferResult =
174
+ /** Held. */
175
+ 'buffered'
176
+ /** Could not be held at all (a single frame larger than the whole budget). */
177
+ | 'rejected';
178
+ export declare class VlmCandidateBuffer {
179
+ private readonly maxFrames;
180
+ private readonly maxPerFeed;
181
+ private readonly maxBytes;
182
+ private readonly ttlMs;
183
+ private readonly now;
184
+ private readonly feeds;
185
+ private held;
186
+ private heldBytes;
187
+ private enqueued;
188
+ private served;
189
+ private evicted;
190
+ private expired;
191
+ /** Monotonic tie-break so two feeds served in the same millisecond rotate. */
192
+ private serveTick;
193
+ private readonly serveOrder;
194
+ constructor(options?: VlmCandidateBufferOptions);
195
+ getMaxFrames(): number;
196
+ getMaxPerFeed(): number;
197
+ getTtlMs(): number;
198
+ /** Candidates held right now. */
199
+ size(): number;
200
+ /** Encoded bytes held right now. */
201
+ bytes(): number;
202
+ /**
203
+ * Hold one candidate. The CALLER guarantees the cascade classified it as
204
+ * interesting — a no-change frame must never be offered, because a buffer of
205
+ * undifferentiated frames is the queue the admission gate exists to refuse.
206
+ *
207
+ * Never blocks and never grows past the caps: room is made by evicting, and
208
+ * every eviction is counted.
209
+ */
210
+ offer(candidate: Omit<VlmCandidate, 'enqueuedAtMs'> & {
211
+ enqueuedAtMs?: number;
212
+ }, nowMs?: number): VlmCandidateOfferResult;
213
+ /**
214
+ * The candidate that would be served next, WITHOUT removing it. Least
215
+ * recently served feed, oldest candidate within it.
216
+ *
217
+ * Nothing here awaits, so a `peek()` immediately followed by `take()` is
218
+ * guaranteed to name the same candidate — which is what lets a caller
219
+ * acquire an admission slot for the right feed before committing.
220
+ */
221
+ peek(nowMs?: number): VlmCandidate | null;
222
+ /** Remove and return the next candidate (same one `peek()` names). */
223
+ take(nowMs?: number): VlmCandidate | null;
224
+ /**
225
+ * Drop every candidate older than `ttlMs`, counting each one. Idempotent and
226
+ * safe to call as often as convenient — it is a scan over a few dozen items.
227
+ */
228
+ expire(nowMs?: number): number;
229
+ /** Age (ms) of the oldest held candidate, or null when empty. */
230
+ oldestAgeMs(nowMs?: number): number | null;
231
+ /** Candidates this feed is holding right now. */
232
+ bufferedFor(name: string): number;
233
+ /** Drop everything and zero every counter (a stop()/start() cycle is clean). */
234
+ reset(): void;
235
+ getStats(nowMs?: number): VlmCandidateBufferStats;
236
+ private feedQueue;
237
+ /**
238
+ * Least-recently-served feed that is holding something. A feed the buffer has
239
+ * never served outranks one it has, so a camera whose event has been waiting
240
+ * is not overtaken by the camera that was just served.
241
+ */
242
+ private nextFeed;
243
+ /** Drop the feed that holds the MOST candidates by one. False when empty. */
244
+ private evictFromFullestFeed;
245
+ private evictOldestOf;
246
+ }
247
+ //# sourceMappingURL=VlmCandidateBuffer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VlmCandidateBuffer.d.ts","sourceRoot":"","sources":["../src/VlmCandidateBuffer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,349 @@
1
+ "use strict";
2
+ /**
3
+ * VlmCandidateBuffer — the bounded hold between the cascade and the admission
4
+ * gate, for frames the cascade has ALREADY decided are interesting.
5
+ *
6
+ * WHY THIS EXISTS, AND WHY IT IS NOT THE QUEUE THE GATE REFUSES.
7
+ * `VlmAdmissionGate` drops rather than queues, and its reason is correct as
8
+ * stated: "by the time a queued frame reached the model the scene has moved
9
+ * on." That is a statement about AMBIENT SAMPLING, where every frame is
10
+ * interchangeable and the newest one is strictly the best one, so holding an
11
+ * old frame is pure loss.
12
+ *
13
+ * Alerting is the other case, and alerting is the product. By the time a frame
14
+ * reaches this buffer the motion cascade has already spent its decode and its
15
+ * detector and concluded THIS frame is different from the scene it had — a
16
+ * person walked in, something fell, an aisle that was empty is not. Those
17
+ * frames are not interchangeable with the next one: at a 30 s scan cadence the
18
+ * next look is a different event, and on a saturated recorder it may be
19
+ * thirteen minutes away. Dropping the one frame that carried the event and
20
+ * later analysing an empty aisle is not "staying fresh"; it is answering a
21
+ * question nobody asked while the one that mattered went unrecorded.
22
+ *
23
+ * So the buffer holds CANDIDATES ONLY. A frame the cascade classified as
24
+ * no-change must never enter — that would rebuild exactly the undifferentiated
25
+ * backlog the gate exists to prevent, and the caller is responsible for that
26
+ * invariant because only the caller has the cascade verdict.
27
+ *
28
+ * THREE BOUNDS, ALL COUNTED. A hold that is unbounded in size is a memory leak
29
+ * and a hold that is unbounded in age is the gate's original complaint. So:
30
+ *
31
+ * - `maxPerFeed` — one busy camera can never hold more than its share, which
32
+ * is what keeps camera 12 from starving the other 31.
33
+ * - `maxFrames` / `maxBytes` — a hard ceiling on the whole buffer, enforced
34
+ * on the way IN. When it binds, the candidate dropped is the OLDEST of the
35
+ * feed that currently holds the MOST, so pressure is paid by whoever is
36
+ * causing it.
37
+ * - `ttlMs` — past this age the gate's insight takes over and the candidate
38
+ * is dropped as expired rather than analysed.
39
+ *
40
+ * Every one of those three drops is COUNTED. A silent eviction is the same
41
+ * defect as the silent drop this class was built to fix — it would move the
42
+ * lost frame from one invisible place to another.
43
+ *
44
+ * SERVICE ORDER is least-recently-served feed first, oldest candidate within
45
+ * that feed. Across feeds that is round-robin, so service cannot be won by
46
+ * whichever timer happens to fire first. Within a feed it is FIFO, because
47
+ * consecutive candidates from one feed are frames of the SAME event and the
48
+ * earliest one is the one that shows the transition — a person entering a
49
+ * restricted area is visible in the frame where they enter, and by the last
50
+ * frame of the burst they may already be out of view.
51
+ *
52
+ * THREADING. Node is single-threaded and no method here awaits, so `peek()`
53
+ * followed by `take()` is one critical section by construction: they are
54
+ * guaranteed to name the same candidate. `CloudSensingManager.pumpCandidates`
55
+ * depends on that — it peeks to learn the feed, acquires an admission slot for
56
+ * it, and only then commits with `take()`.
57
+ */
58
+ Object.defineProperty(exports, "__esModule", { value: true });
59
+ exports.VlmCandidateBuffer = exports.DEFAULT_CANDIDATE_TTL_MS = void 0;
60
+ /**
61
+ * DEFAULT_CANDIDATE_TTL_MS — how long an alert is still an alert.
62
+ *
63
+ * The admission gate's sentence is right at a long enough horizon: a frame the
64
+ * model looks at ten minutes late describes a scene that no longer exists, and
65
+ * the slot it burns would have been better spent on something current. The
66
+ * only question is where that horizon actually sits, and the answer comes from
67
+ * what the frame is FOR, not from how fresh it is in the abstract.
68
+ *
69
+ * The events this connector exists to catch — a spill, a fall, someone in a
70
+ * restricted area — persist on the order of a minute or two, and the response
71
+ * to one is a person walking over to look. An alert that lands 90 s late still
72
+ * finds the spill on the floor and the person still down, and is worth every
73
+ * bit of the slot it takes. One that lands ten minutes late is a report rather
74
+ * than an alert: either somebody already noticed or nobody is going to.
75
+ *
76
+ * 90 s is also three full 30 s face-triggered cadences, so a candidate
77
+ * survives a couple of rounds of contention on a saturated recorder, and it is
78
+ * well inside the 5 min still-scene heartbeat, so a held candidate can never
79
+ * outlive the feed's own next guaranteed report. The buffer may delay a frame;
80
+ * it can never stand in for the cadence.
81
+ */
82
+ exports.DEFAULT_CANDIDATE_TTL_MS = 90000;
83
+ const DEFAULT_MAX_FRAMES = 32;
84
+ const DEFAULT_MAX_PER_FEED = 2;
85
+ const DEFAULT_MAX_BYTES = 16 * 1024 * 1024;
86
+ class VlmCandidateBuffer {
87
+ constructor(options = {}) {
88
+ this.feeds = new Map();
89
+ this.held = 0;
90
+ this.heldBytes = 0;
91
+ this.enqueued = 0;
92
+ this.served = 0;
93
+ this.evicted = 0;
94
+ this.expired = 0;
95
+ /** Monotonic tie-break so two feeds served in the same millisecond rotate. */
96
+ this.serveTick = 0;
97
+ this.serveOrder = new Map();
98
+ this.maxFrames = Math.max(1, Math.trunc(options.maxFrames ?? DEFAULT_MAX_FRAMES));
99
+ // A per-feed cap above the global cap is not a cap; clamp so the two can
100
+ // never disagree about what "one feed's share" means.
101
+ this.maxPerFeed = Math.min(this.maxFrames, Math.max(1, Math.trunc(options.maxPerFeed ?? DEFAULT_MAX_PER_FEED)));
102
+ this.maxBytes = Math.max(1, Math.trunc(options.maxBytes ?? DEFAULT_MAX_BYTES));
103
+ this.ttlMs = Math.max(1, Math.trunc(options.ttlMs ?? exports.DEFAULT_CANDIDATE_TTL_MS));
104
+ this.now = options.now ?? Date.now;
105
+ }
106
+ getMaxFrames() {
107
+ return this.maxFrames;
108
+ }
109
+ getMaxPerFeed() {
110
+ return this.maxPerFeed;
111
+ }
112
+ getTtlMs() {
113
+ return this.ttlMs;
114
+ }
115
+ /** Candidates held right now. */
116
+ size() {
117
+ return this.held;
118
+ }
119
+ /** Encoded bytes held right now. */
120
+ bytes() {
121
+ return this.heldBytes;
122
+ }
123
+ /**
124
+ * Hold one candidate. The CALLER guarantees the cascade classified it as
125
+ * interesting — a no-change frame must never be offered, because a buffer of
126
+ * undifferentiated frames is the queue the admission gate exists to refuse.
127
+ *
128
+ * Never blocks and never grows past the caps: room is made by evicting, and
129
+ * every eviction is counted.
130
+ */
131
+ offer(candidate, nowMs) {
132
+ const now = nowMs ?? this.now();
133
+ this.expire(now);
134
+ const entry = {
135
+ name: candidate.name,
136
+ jpeg: candidate.jpeg,
137
+ captureMode: candidate.captureMode,
138
+ reason: candidate.reason,
139
+ signature: candidate.signature,
140
+ imageWidth: candidate.imageWidth,
141
+ imageHeight: candidate.imageHeight,
142
+ enqueuedAtMs: candidate.enqueuedAtMs ?? now,
143
+ };
144
+ const incomingBytes = entry.jpeg.length;
145
+ // A single frame larger than the entire budget can never be held, and
146
+ // emptying the buffer for it would trade N real candidates for zero. Say
147
+ // no, and count it — an uncounted refusal is the defect this class fixes.
148
+ if (incomingBytes > this.maxBytes) {
149
+ const feed = this.feedQueue(entry.name);
150
+ feed.evicted++;
151
+ this.evicted++;
152
+ return 'rejected';
153
+ }
154
+ const feed = this.feedQueue(entry.name);
155
+ // 1. This feed's own share first, so the eviction lands on the feed that
156
+ // is over its allowance rather than on a quiet neighbour.
157
+ while (feed.items.length >= this.maxPerFeed) {
158
+ this.evictOldestOf(feed);
159
+ }
160
+ // 2. Then the global ceilings. Pressure is paid by whoever holds the most.
161
+ while (this.held + 1 > this.maxFrames ||
162
+ this.heldBytes + incomingBytes > this.maxBytes) {
163
+ if (!this.evictFromFullestFeed())
164
+ break;
165
+ }
166
+ feed.items.push(entry);
167
+ this.held++;
168
+ this.heldBytes += incomingBytes;
169
+ feed.enqueued++;
170
+ this.enqueued++;
171
+ return 'buffered';
172
+ }
173
+ /**
174
+ * The candidate that would be served next, WITHOUT removing it. Least
175
+ * recently served feed, oldest candidate within it.
176
+ *
177
+ * Nothing here awaits, so a `peek()` immediately followed by `take()` is
178
+ * guaranteed to name the same candidate — which is what lets a caller
179
+ * acquire an admission slot for the right feed before committing.
180
+ */
181
+ peek(nowMs) {
182
+ this.expire(nowMs ?? this.now());
183
+ const feed = this.nextFeed();
184
+ return feed ? feed.items[0] : null;
185
+ }
186
+ /** Remove and return the next candidate (same one `peek()` names). */
187
+ take(nowMs) {
188
+ const now = nowMs ?? this.now();
189
+ this.expire(now);
190
+ const feed = this.nextFeed();
191
+ if (!feed)
192
+ return null;
193
+ const item = feed.items.shift();
194
+ if (!item)
195
+ return null;
196
+ this.held--;
197
+ this.heldBytes -= item.jpeg.length;
198
+ feed.served++;
199
+ this.served++;
200
+ feed.lastServedAtMs = now;
201
+ this.serveOrder.set(feed.name, ++this.serveTick);
202
+ return item;
203
+ }
204
+ /**
205
+ * Drop every candidate older than `ttlMs`, counting each one. Idempotent and
206
+ * safe to call as often as convenient — it is a scan over a few dozen items.
207
+ */
208
+ expire(nowMs) {
209
+ const now = nowMs ?? this.now();
210
+ let dropped = 0;
211
+ for (const feed of this.feeds.values()) {
212
+ while (feed.items.length > 0 && now - feed.items[0].enqueuedAtMs > this.ttlMs) {
213
+ const item = feed.items.shift();
214
+ this.held--;
215
+ this.heldBytes -= item.jpeg.length;
216
+ feed.expired++;
217
+ this.expired++;
218
+ dropped++;
219
+ }
220
+ }
221
+ return dropped;
222
+ }
223
+ /** Age (ms) of the oldest held candidate, or null when empty. */
224
+ oldestAgeMs(nowMs) {
225
+ const now = nowMs ?? this.now();
226
+ let oldest = null;
227
+ for (const feed of this.feeds.values()) {
228
+ const head = feed.items[0];
229
+ if (!head)
230
+ continue;
231
+ const age = Math.max(0, now - head.enqueuedAtMs);
232
+ if (oldest === null || age > oldest)
233
+ oldest = age;
234
+ }
235
+ return oldest;
236
+ }
237
+ /** Candidates this feed is holding right now. */
238
+ bufferedFor(name) {
239
+ return this.feeds.get(name)?.items.length ?? 0;
240
+ }
241
+ /** Drop everything and zero every counter (a stop()/start() cycle is clean). */
242
+ reset() {
243
+ this.feeds.clear();
244
+ this.serveOrder.clear();
245
+ this.held = 0;
246
+ this.heldBytes = 0;
247
+ this.enqueued = 0;
248
+ this.served = 0;
249
+ this.evicted = 0;
250
+ this.expired = 0;
251
+ this.serveTick = 0;
252
+ }
253
+ getStats(nowMs) {
254
+ const now = nowMs ?? this.now();
255
+ this.expire(now);
256
+ return {
257
+ buffered: this.held,
258
+ bytes: this.heldBytes,
259
+ maxFrames: this.maxFrames,
260
+ maxPerFeed: this.maxPerFeed,
261
+ maxBytes: this.maxBytes,
262
+ ttlMs: this.ttlMs,
263
+ enqueued: this.enqueued,
264
+ served: this.served,
265
+ evicted: this.evicted,
266
+ expired: this.expired,
267
+ oldestAgeMs: this.oldestAgeMs(now),
268
+ perFeed: [...this.feeds.values()].map((f) => ({
269
+ name: f.name,
270
+ buffered: f.items.length,
271
+ enqueued: f.enqueued,
272
+ served: f.served,
273
+ evicted: f.evicted,
274
+ expired: f.expired,
275
+ })),
276
+ };
277
+ }
278
+ // ─── Private ────────────────────────────────────────────────────────────────
279
+ feedQueue(name) {
280
+ let feed = this.feeds.get(name);
281
+ if (!feed) {
282
+ feed = {
283
+ name,
284
+ items: [],
285
+ lastServedAtMs: null,
286
+ enqueued: 0,
287
+ served: 0,
288
+ evicted: 0,
289
+ expired: 0,
290
+ };
291
+ this.feeds.set(name, feed);
292
+ }
293
+ return feed;
294
+ }
295
+ /**
296
+ * Least-recently-served feed that is holding something. A feed the buffer has
297
+ * never served outranks one it has, so a camera whose event has been waiting
298
+ * is not overtaken by the camera that was just served.
299
+ */
300
+ nextFeed() {
301
+ let best = null;
302
+ let bestOrder = Infinity;
303
+ let bestOldest = Infinity;
304
+ for (const feed of this.feeds.values()) {
305
+ const head = feed.items[0];
306
+ if (!head)
307
+ continue;
308
+ // Never served ⇒ order 0, which sorts ahead of every served feed.
309
+ const order = this.serveOrder.get(feed.name) ?? 0;
310
+ if (order < bestOrder || (order === bestOrder && head.enqueuedAtMs < bestOldest)) {
311
+ best = feed;
312
+ bestOrder = order;
313
+ bestOldest = head.enqueuedAtMs;
314
+ }
315
+ }
316
+ return best;
317
+ }
318
+ /** Drop the feed that holds the MOST candidates by one. False when empty. */
319
+ evictFromFullestFeed() {
320
+ let victim = null;
321
+ for (const feed of this.feeds.values()) {
322
+ if (feed.items.length === 0)
323
+ continue;
324
+ if (victim === null ||
325
+ feed.items.length > victim.items.length ||
326
+ // Same depth ⇒ the one whose head is oldest, so ties are broken toward
327
+ // the frame closest to expiring anyway.
328
+ (feed.items.length === victim.items.length &&
329
+ feed.items[0].enqueuedAtMs < victim.items[0].enqueuedAtMs)) {
330
+ victim = feed;
331
+ }
332
+ }
333
+ if (!victim)
334
+ return false;
335
+ this.evictOldestOf(victim);
336
+ return true;
337
+ }
338
+ evictOldestOf(feed) {
339
+ const item = feed.items.shift();
340
+ if (!item)
341
+ return;
342
+ this.held--;
343
+ this.heldBytes -= item.jpeg.length;
344
+ feed.evicted++;
345
+ this.evicted++;
346
+ }
347
+ }
348
+ exports.VlmCandidateBuffer = VlmCandidateBuffer;
349
+ //# sourceMappingURL=VlmCandidateBuffer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VlmCandidateBuffer.js","sourceRoot":"","sources":["../src/VlmCandidateBuffer.ts"],"names":[],"mappings":"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"}