@camstack/addon-post-analysis 1.2.196 → 1.2.198
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.
- package/dist/{dist-Cu4sfuYT.mjs → dist-BrszC4uJ.mjs} +721 -489
- package/dist/{dist-BRvbQOHc.js → dist-fmXXxa9D.js} +744 -488
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +1 -1
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-1iZqrgrt.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CTKbm1Jm.mjs} +3 -3
- package/dist/pipeline-analytics/{_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CJb6-kIP.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BsBwoxCb.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit-qX__e703.mjs → hostInit-DXFji_c2.mjs} +3 -3
- package/dist/pipeline-analytics/index.js +145 -23
- package/dist/pipeline-analytics/index.mjs +145 -23
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +5 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as audioModeOf, A as NcSnoozeSchema, At as
|
|
1
|
+
import { $ as audioModeOf, $t as EventCategory, A as NcSnoozeSchema, At as vectorDimFromBase64, B as SCENE_DEFAULT_UNCOVERED_POLICY, Bt as nodePin, C as NcConditionDescriptorSchema, Ct as readTimelapseGeneratedAt, D as NcRuleTargetSchema, Dt as storageOccupancyCapability, E as NcRuleSchema, Et as sceneMonitorCapability, Ft as CamProfileSchema, G as TimelapseRulePatchSchema, Gt as discriminatedUnion, H as SceneMonitorSchema, Ht as _enum, I as RECORDING_EXPORT_MAX_READ_BYTES, It as DeviceType, J as VISIT_MERGE_GAP_MS, Jt as object, K as TimelapseRuleSchema, Kt as literal, L as RetrainStatusSchema, Lt as createEvent, M as NcSystemEventKindSchema, Mt as zoneAnalyticsCapability, N as NcTaxonomySchema, Nt as errMsg$1, O as NcScheduleSchema, Ot as subKindsOf, P as OpsLogEntrySchema, Pt as BaseAddon, Q as audioMetricsCapability, Qt as unknown, Rt as hydrateSchema, S as NC_TAXONOMY, St as readDeviceStateFrom, T as NcRulePatchSchema, U as TIMELAPSE_DENSE_FLOOR_SEC, Ut as array, V as SCENE_DIVERGED, Vt as sleep$1, W as TimelapseRuleInputSchema, Wt as boolean, X as alarmPanelCapability, Xt as record, Y as addonWidgetsSourceCapability, Yt as partialRecord, Z as assertTimelapseCadences, Zt as string, _ as MediaFileKindEnum, _t as notificationRulesCapability, a as DEFAULT_EVENT_COLOR, b as NC_DEFAULT_SNOOZE_MINUTES, bt as pipelineAnalyticsCapability, c as DETECTION_MACRO_CLASSES, ct as evictionPolicyOfLocation, d as EVENT_KIND_BY_CAP, et as buildEventKindDescriptor, f as EVENT_PAD_MS, ft as isDetectionMacroClass, g as MACRO_LABELS, gt as mayWriteToLocation, h as LabelAttributionSchema, ht as kebabToCamel, i as COCO_TO_MACRO, it as deriveRecordingMode, j as NcSnoozeSuppressedSchema, jt as videoclipsCapability, k as NcSnoozeInputSchema, kt as systemEventFilterApplies, lt as faceGalleryCapability, m as FailureCounters, mt as isSourceCap, n as BaseDevice, nt as customAction, ot as encodeVectorBase64, p as FULL_IMAGE_BBOX, pt as isScheduleActive, q as TrackSourceSchema, qt as number, r as CLUSTER_MODEL_SCOPED_STEPS, rt as defineCustomActions, st as evaluateSensorEdge, t as AUDIO_MACRO_LABELS, tt as cosineSimilarity$1, u as DeclaredDevices, ut as failureContributionCapability, v as NC_ALARM_SYSTEM_EVENT_KINDS, w as NcRuleInputSchema, wt as resolveLocationMode, xt as plateGalleryCapability, y as NC_CONDITION_CATALOG, yt as pickClusterStepModels, z as SCENE_DEFAULT_ANCHOR_THRESHOLD, zt as isDeviceScopedCap } from "../dist-BrszC4uJ.mjs";
|
|
2
2
|
import { t as __exportAll } from "../embedding-encoder/index.mjs";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import { promises } from "node:fs";
|
|
@@ -9,6 +9,45 @@ import { execFile } from "node:child_process";
|
|
|
9
9
|
import sharp from "sharp";
|
|
10
10
|
import os from "node:os";
|
|
11
11
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
12
|
+
//#region src/pipeline-analytics/media-occupancy.ts
|
|
13
|
+
/** The bare type ref legacy NULL-stamped media rows belong to. */
|
|
14
|
+
var LEGACY_MEDIA_TYPE = "eventMedia";
|
|
15
|
+
/**
|
|
16
|
+
* Which of the requested locations the legacy `eventMedia` rows belong to, or
|
|
17
|
+
* `null` when it cannot be decided — the same rule `resolveRef` applies, and
|
|
18
|
+
* the same refusal when it is ambiguous.
|
|
19
|
+
*/
|
|
20
|
+
function legacyMediaLocationOf(locationIds) {
|
|
21
|
+
const ofType = locationIds.filter((id) => id.startsWith(`${LEGACY_MEDIA_TYPE}:`));
|
|
22
|
+
if (ofType.length === 1) return ofType[0] ?? null;
|
|
23
|
+
return ofType.find((id) => id === `eventMedia:default`) ?? null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The occupancy reports for one measurement.
|
|
27
|
+
*
|
|
28
|
+
* A location with nothing on it is OMITTED, not reported as zero: only the
|
|
29
|
+
* orchestrator knows whether nobody reported for a location or every reporter
|
|
30
|
+
* holds nothing there, and it is the one that must be able to tell them apart.
|
|
31
|
+
*/
|
|
32
|
+
function mediaOccupancyReports(footprint, locationIds, measuredAtMs) {
|
|
33
|
+
const bytesByLocation = new Map(footprint.byLocation);
|
|
34
|
+
const legacyTarget = footprint.legacyNullBytes > 0 ? legacyMediaLocationOf(locationIds) : null;
|
|
35
|
+
if (legacyTarget !== null) bytesByLocation.set(legacyTarget, (bytesByLocation.get(legacyTarget) ?? 0) + footprint.legacyNullBytes);
|
|
36
|
+
const reports = [];
|
|
37
|
+
for (const [locationId, ownedBytes] of bytesByLocation) {
|
|
38
|
+
if (ownedBytes <= 0) continue;
|
|
39
|
+
reports.push({
|
|
40
|
+
locationId,
|
|
41
|
+
ownedBytes,
|
|
42
|
+
measuredAtMs
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
reports,
|
|
47
|
+
unattributedBytes: legacyTarget === null ? footprint.legacyNullBytes : 0
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
12
51
|
//#region src/notification-center/action-token.ts
|
|
13
52
|
/**
|
|
14
53
|
* The authority behind a notification button.
|
|
@@ -28857,6 +28896,50 @@ var MediaStore = class {
|
|
|
28857
28896
|
return out;
|
|
28858
28897
|
}
|
|
28859
28898
|
/**
|
|
28899
|
+
* Bytes this store holds on each of the named storage locations (D388).
|
|
28900
|
+
*
|
|
28901
|
+
* One indexed aggregate per location (`idx_media_location`), plus one for the
|
|
28902
|
+
* legacy NULL-stamped rows — `locationId IS NULL` means "wherever the bare
|
|
28903
|
+
* `eventMedia` type ref resolves", and the CALLER attributes those, because
|
|
28904
|
+
* resolving a bare type ref is the orchestrator's rule and not this store's.
|
|
28905
|
+
*
|
|
28906
|
+
* A location this store holds nothing on is OMITTED, never reported as zero:
|
|
28907
|
+
* the difference between "we hold nothing here" and "nobody asked" is the
|
|
28908
|
+
* whole point of the occupancy contract, and only the caller can tell which
|
|
28909
|
+
* it is. A read that fails THROWS rather than degrading to zeros — the same
|
|
28910
|
+
* rule `footprintByDevice` follows, for the same reason.
|
|
28911
|
+
*/
|
|
28912
|
+
async footprintByLocation(locationIds) {
|
|
28913
|
+
const byLocation = /* @__PURE__ */ new Map();
|
|
28914
|
+
for (const locationId of locationIds) {
|
|
28915
|
+
const agg = await this.store.aggregate.query({
|
|
28916
|
+
collection: MEDIA_COLLECTION,
|
|
28917
|
+
fields: [{
|
|
28918
|
+
as: "bytes",
|
|
28919
|
+
field: "sizeBytes",
|
|
28920
|
+
op: "sum"
|
|
28921
|
+
}],
|
|
28922
|
+
filter: { where: { locationId } }
|
|
28923
|
+
});
|
|
28924
|
+
const bytes = agg.values["bytes"] ?? 0;
|
|
28925
|
+
if (agg.count === 0 || bytes <= 0) continue;
|
|
28926
|
+
byLocation.set(locationId, bytes);
|
|
28927
|
+
}
|
|
28928
|
+
const legacy = await this.store.aggregate.query({
|
|
28929
|
+
collection: MEDIA_COLLECTION,
|
|
28930
|
+
fields: [{
|
|
28931
|
+
as: "bytes",
|
|
28932
|
+
field: "sizeBytes",
|
|
28933
|
+
op: "sum"
|
|
28934
|
+
}],
|
|
28935
|
+
filter: { where: { locationId: null } }
|
|
28936
|
+
});
|
|
28937
|
+
return {
|
|
28938
|
+
byLocation,
|
|
28939
|
+
legacyNullBytes: legacy.count === 0 ? 0 : legacy.values["bytes"] ?? 0
|
|
28940
|
+
};
|
|
28941
|
+
}
|
|
28942
|
+
/**
|
|
28860
28943
|
* The media footprint broken down by KIND — fleet-wide, or for one camera.
|
|
28861
28944
|
*
|
|
28862
28945
|
* `footprintByDevice` answers "how much is where"; this answers "how much is
|
|
@@ -42066,21 +42149,16 @@ async function wipeClassSubtrees(input) {
|
|
|
42066
42149
|
}
|
|
42067
42150
|
//#endregion
|
|
42068
42151
|
//#region src/pipeline-analytics/retention/location-discard-policy.ts
|
|
42069
|
-
/**
|
|
42070
|
-
* Server-side eligibility for discarding a frozen location's leftover
|
|
42071
|
-
* class subtree. Same rules as the admin-ui card: frozen, known class,
|
|
42072
|
-
* writable sibling of the same type. The last writable of a type is
|
|
42073
|
-
* refused here even if the UI is bypassed.
|
|
42074
|
-
*/
|
|
42075
42152
|
var DISCARD_SUBTREES = {
|
|
42076
42153
|
eventMedia: ["events"],
|
|
42077
42154
|
recordingsLow: ["low"],
|
|
42078
42155
|
recordings: ["high", "mid"]
|
|
42079
42156
|
};
|
|
42157
|
+
/** Frozen = the system will not remove this footage on its own: eviction
|
|
42158
|
+
* policy `never` (D385), i.e. `readonly` or `disabled`. A `drain` location is
|
|
42159
|
+
* NOT frozen — the ratchet is already removing it. */
|
|
42080
42160
|
function locationIsFrozen(location) {
|
|
42081
|
-
|
|
42082
|
-
const readOnly = location.config["readOnly"] === true;
|
|
42083
|
-
return !enabled || readOnly;
|
|
42161
|
+
return evictionPolicyOfLocation(location) === "never";
|
|
42084
42162
|
}
|
|
42085
42163
|
function discardEligible(location, siblings) {
|
|
42086
42164
|
if (!(location.type in DISCARD_SUBTREES)) return false;
|
|
@@ -42088,9 +42166,7 @@ function discardEligible(location, siblings) {
|
|
|
42088
42166
|
return siblings.some((s) => {
|
|
42089
42167
|
if (s.id === location.id) return false;
|
|
42090
42168
|
if (s.type !== location.type) return false;
|
|
42091
|
-
|
|
42092
|
-
const readOnly = s.config["readOnly"] === true;
|
|
42093
|
-
return enabled && !readOnly;
|
|
42169
|
+
return mayWriteToLocation(s);
|
|
42094
42170
|
});
|
|
42095
42171
|
}
|
|
42096
42172
|
function assertDiscardAllowed(location, all, localNodeId) {
|
|
@@ -50711,13 +50787,22 @@ var MediaCaptureLogAggregator = class {
|
|
|
50711
50787
|
* when none is writable — the caller MUST refuse the write rather than fall
|
|
50712
50788
|
* back to a read-only location, which would fail far downstream as an opaque
|
|
50713
50789
|
* filesystem error instead of a named storage decision.
|
|
50790
|
+
*
|
|
50791
|
+
* `stickyId` is the pick this process last made (absent on a cold start). It
|
|
50792
|
+
* used to be the CLASS DEFAULT, which is gone (D383): the write set is every
|
|
50793
|
+
* enabled location, and among them the emptiest wins. Cross-restart stickiness
|
|
50794
|
+
* is deliberately not durable — every blob row stamps its own `locationId`, so
|
|
50795
|
+
* a restart that switches disk splits nothing that reads wrong.
|
|
50714
50796
|
*/
|
|
50715
|
-
function pickWritableMediaLocation(locations,
|
|
50716
|
-
const writable = locations.filter((l) =>
|
|
50797
|
+
function pickWritableMediaLocation(locations, stickyId) {
|
|
50798
|
+
const writable = locations.filter((l) => mayWriteToLocation({
|
|
50799
|
+
mode: l.mode,
|
|
50800
|
+
config: {}
|
|
50801
|
+
}));
|
|
50717
50802
|
if (writable.length === 0) return null;
|
|
50718
|
-
const keep = writable.find((l) => l.id ===
|
|
50803
|
+
const keep = stickyId === void 0 ? void 0 : writable.find((l) => l.id === stickyId);
|
|
50719
50804
|
if (keep) return keep.id;
|
|
50720
|
-
return writable.toSorted((a, b) => b.headroomBytes - a.headroomBytes)[0]?.id ?? null;
|
|
50805
|
+
return writable.toSorted((a, b) => b.headroomBytes - a.headroomBytes || a.id.localeCompare(b.id))[0]?.id ?? null;
|
|
50721
50806
|
}
|
|
50722
50807
|
//#endregion
|
|
50723
50808
|
//#region src/pipeline-analytics/media-relocate-engine.ts
|
|
@@ -67372,6 +67457,13 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
67372
67457
|
* `enabled` flags only change via operator reconfig.
|
|
67373
67458
|
*/
|
|
67374
67459
|
eventMediaWriteLocationCache = null;
|
|
67460
|
+
/**
|
|
67461
|
+
* The last location this process actually picked for an `eventMedia` write.
|
|
67462
|
+
* Survives the cache invalidation a migration performs, so a refresh keeps
|
|
67463
|
+
* writing where it was writing while that stays writable — the stickiness the
|
|
67464
|
+
* class default used to provide before it was deleted (D383).
|
|
67465
|
+
*/
|
|
67466
|
+
lastEventMediaWritePick = null;
|
|
67375
67467
|
mediaWriteGate = new MediaWriteGate();
|
|
67376
67468
|
storageMigrationLeaseId = null;
|
|
67377
67469
|
/** Per-camera history of LINKED-device sensor state changes (Part B). */
|
|
@@ -68716,9 +68808,9 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
68716
68808
|
}
|
|
68717
68809
|
/**
|
|
68718
68810
|
* Resolves which `eventMedia` location a NEW footage-class blob should be
|
|
68719
|
-
* written to (`MediaStoreDeps.resolveEventMediaLocation`). Reflects
|
|
68720
|
-
* `
|
|
68721
|
-
*
|
|
68811
|
+
* written to (`MediaStoreDeps.resolveEventMediaLocation`). Reflects the
|
|
68812
|
+
* location's `mode` (D385) — a location the operator has taken out of the
|
|
68813
|
+
* write set never receives new blobs (the relocate mover, exposed as
|
|
68722
68814
|
* `relocateMedia`, empties what is already on it in the background).
|
|
68723
68815
|
*
|
|
68724
68816
|
* Cached after the first successful resolution — this is a per-blob write
|
|
@@ -68733,6 +68825,32 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
68733
68825
|
* a test harness that never wires `api.storage`) must not turn every media
|
|
68734
68826
|
* write into a hard failure the way "no writable location" does.
|
|
68735
68827
|
*/
|
|
68828
|
+
/**
|
|
68829
|
+
* The `storage-occupancy` answer for event media (D388) — bytes HELD on each
|
|
68830
|
+
* named location, not bytes retention would be willing to remove.
|
|
68831
|
+
*
|
|
68832
|
+
* Answered from ONE indexed aggregate per location. A store that is not up
|
|
68833
|
+
* yet reports NOTHING, which reads as unknown; it never reports zeros, which
|
|
68834
|
+
* would claim the disks are empty. Legacy NULL-stamped rows are attributed by
|
|
68835
|
+
* `mediaOccupancyReports`, and bytes it cannot place are LOGGED rather than
|
|
68836
|
+
* spread over the candidates — a number no disk agrees with is worse than an
|
|
68837
|
+
* absent one.
|
|
68838
|
+
*/
|
|
68839
|
+
async mediaOccupancy(locationIds) {
|
|
68840
|
+
const store = this.mediaStore;
|
|
68841
|
+
if (!store) return [];
|
|
68842
|
+
const { reports, unattributedBytes } = mediaOccupancyReports(await store.footprintByLocation(locationIds), locationIds, Date.now());
|
|
68843
|
+
if (unattributedBytes > 0 && !this.mediaOccupancyUnattributedLogged) {
|
|
68844
|
+
this.mediaOccupancyUnattributedLogged = true;
|
|
68845
|
+
this.ctx.logger.warn("pipeline-analytics: event-media bytes could not be attributed to a location — the legacy `eventMedia` type ref resolves to no single location, so they are reported NOWHERE rather than onto a disk that does not hold them", { meta: {
|
|
68846
|
+
unattributedBytes,
|
|
68847
|
+
locationIds: [...locationIds]
|
|
68848
|
+
} });
|
|
68849
|
+
}
|
|
68850
|
+
return reports;
|
|
68851
|
+
}
|
|
68852
|
+
/** One line per process: this repeats on every occupancy refresh otherwise. */
|
|
68853
|
+
mediaOccupancyUnattributedLogged = false;
|
|
68736
68854
|
async resolveEventMediaWriteLocation(api, logger, deviceId) {
|
|
68737
68855
|
const cached = this.eventMediaWriteLocationCache;
|
|
68738
68856
|
if (cached !== null) return cached;
|
|
@@ -68746,10 +68864,9 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
68746
68864
|
}
|
|
68747
68865
|
const picked = pickWritableMediaLocation(locations.map((l) => ({
|
|
68748
68866
|
id: l.id,
|
|
68749
|
-
|
|
68750
|
-
enabled: l.enabled !== false,
|
|
68867
|
+
mode: resolveLocationMode(l),
|
|
68751
68868
|
headroomBytes: l.capacity?.availableBytes ?? 0
|
|
68752
|
-
})),
|
|
68869
|
+
})), this.lastEventMediaWritePick ?? void 0);
|
|
68753
68870
|
if (picked === null) {
|
|
68754
68871
|
const seen = locations.length > 0 ? locations.map((l) => l.id).join(", ") : "(none registered)";
|
|
68755
68872
|
logger.error("pipeline-analytics: no writable eventMedia storage location — refusing the write instead of failing far downstream as a filesystem error", {
|
|
@@ -68759,6 +68876,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
68759
68876
|
throw new Error(`no writable eventMedia storage location (seen: ${seen})`);
|
|
68760
68877
|
}
|
|
68761
68878
|
this.eventMediaWriteLocationCache = picked;
|
|
68879
|
+
this.lastEventMediaWritePick = picked;
|
|
68762
68880
|
return picked;
|
|
68763
68881
|
}
|
|
68764
68882
|
/** Constructs every SQLite-backed store plus the stationary/package-drop/
|
|
@@ -70703,6 +70821,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
70703
70821
|
capability: notificationRulesCapability,
|
|
70704
70822
|
provider: this.notificationCenter.buildProvider()
|
|
70705
70823
|
}] : [],
|
|
70824
|
+
{
|
|
70825
|
+
capability: storageOccupancyCapability,
|
|
70826
|
+
provider: { getOccupancy: async (input) => this.mediaOccupancy(input.locationIds) }
|
|
70827
|
+
},
|
|
70706
70828
|
{
|
|
70707
70829
|
capability: zoneAnalyticsCapability,
|
|
70708
70830
|
provider: providers.zoneAnalytics
|
|
@@ -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-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CTKbm1Jm.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.
|
|
3
|
+
"version": "1.2.198",
|
|
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",
|
|
@@ -101,6 +101,10 @@
|
|
|
101
101
|
},
|
|
102
102
|
{
|
|
103
103
|
"name": "failure-contribution"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"name": "storage-occupancy",
|
|
107
|
+
"optional": true
|
|
104
108
|
}
|
|
105
109
|
],
|
|
106
110
|
"storageLocations": [
|