@camstack/addon-post-analysis 1.2.669 → 1.2.671
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/CHANGELOG.md +13 -0
- package/dist/{clip-model-registry-CbzBwacH.js → clip-model-registry-BhKHkBsg.js} +573 -547
- package/dist/{clip-model-registry-BPvijtCz.mjs → clip-model-registry-CDLYwWsU.mjs} +568 -548
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +2 -2
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-C4QK9e1e.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CRWJsnQv.mjs} +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D0SgNf77.mjs +26 -0
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DFMFx1IF.mjs +26 -0
- package/dist/pipeline-analytics/{hostInit-CPLvPO3K.mjs → hostInit-D503xVER.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +252 -41
- package/dist/pipeline-analytics/index.mjs +252 -41
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-9HsFj_VL.mjs +0 -26
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-anSYIl3S.mjs +0 -26
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_clip_model_registry = require("../clip-model-registry-
|
|
5
|
+
const require_clip_model_registry = require("../clip-model-registry-BhKHkBsg.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_clip_model_registry.__toESM(node_fs, 1);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -16773,6 +16773,68 @@ var MediaStore = class MediaStore {
|
|
|
16773
16773
|
};
|
|
16774
16774
|
}
|
|
16775
16775
|
/**
|
|
16776
|
+
* Rows and bytes per asked storage location, the unstamped (NULL) rows, the
|
|
16777
|
+
* rows naming a location that was NOT asked about, and the total.
|
|
16778
|
+
*
|
|
16779
|
+
* Same statements as {@link footprintByLocation} (one aggregate over
|
|
16780
|
+
* `idx_media_location_size` per location, one for NULL) plus one unfiltered
|
|
16781
|
+
* aggregate for the total (a covering-index scan, O(rows)); the unlisted
|
|
16782
|
+
* remainder is the difference, relative to the ids ASKED, so a row stamped with
|
|
16783
|
+
* a deleted location is counted without a distinct walk only when the caller
|
|
16784
|
+
* asked for every live location. A read that fails THROWS — never a zero.
|
|
16785
|
+
*/
|
|
16786
|
+
async censusByLocation(locationIds) {
|
|
16787
|
+
const fields = [{
|
|
16788
|
+
as: "bytes",
|
|
16789
|
+
field: "sizeBytes",
|
|
16790
|
+
op: "sum"
|
|
16791
|
+
}];
|
|
16792
|
+
const locations = [];
|
|
16793
|
+
let knownRows = 0;
|
|
16794
|
+
let knownBytes = 0;
|
|
16795
|
+
for (const locationId of locationIds) {
|
|
16796
|
+
const agg = await this.store.aggregate.query({
|
|
16797
|
+
collection: MEDIA_COLLECTION,
|
|
16798
|
+
fields: [...fields],
|
|
16799
|
+
filter: { where: { locationId } }
|
|
16800
|
+
});
|
|
16801
|
+
const bytes = agg.values["bytes"] ?? 0;
|
|
16802
|
+
locations.push({
|
|
16803
|
+
locationId,
|
|
16804
|
+
rows: agg.count,
|
|
16805
|
+
bytes
|
|
16806
|
+
});
|
|
16807
|
+
knownRows += agg.count;
|
|
16808
|
+
knownBytes += bytes;
|
|
16809
|
+
}
|
|
16810
|
+
const nullAgg = await this.store.aggregate.query({
|
|
16811
|
+
collection: MEDIA_COLLECTION,
|
|
16812
|
+
fields: [...fields],
|
|
16813
|
+
filter: { where: { locationId: null } }
|
|
16814
|
+
});
|
|
16815
|
+
const totalAgg = await this.store.aggregate.query({
|
|
16816
|
+
collection: MEDIA_COLLECTION,
|
|
16817
|
+
fields: [...fields]
|
|
16818
|
+
});
|
|
16819
|
+
const nullBytes = nullAgg.values["bytes"] ?? 0;
|
|
16820
|
+
const totalBytes = totalAgg.values["bytes"] ?? 0;
|
|
16821
|
+
return {
|
|
16822
|
+
locations,
|
|
16823
|
+
unstamped: {
|
|
16824
|
+
rows: nullAgg.count,
|
|
16825
|
+
bytes: nullBytes
|
|
16826
|
+
},
|
|
16827
|
+
unlisted: {
|
|
16828
|
+
rows: Math.max(0, totalAgg.count - knownRows - nullAgg.count),
|
|
16829
|
+
bytes: Math.max(0, totalBytes - knownBytes - nullBytes)
|
|
16830
|
+
},
|
|
16831
|
+
total: {
|
|
16832
|
+
rows: totalAgg.count,
|
|
16833
|
+
bytes: totalBytes
|
|
16834
|
+
}
|
|
16835
|
+
};
|
|
16836
|
+
}
|
|
16837
|
+
/**
|
|
16776
16838
|
* The media footprint broken down by KIND — fleet-wide, or for one camera.
|
|
16777
16839
|
*
|
|
16778
16840
|
* `footprintByDevice` answers "how much is where"; this answers "how much is
|
|
@@ -52541,7 +52603,7 @@ var FaceSettingsSchema = require_clip_model_registry.object({
|
|
|
52541
52603
|
* `resolveGlobalFaceEnabled` / `getDeviceSettingsContribution` (the field is
|
|
52542
52604
|
* stripped from the per-device schema).
|
|
52543
52605
|
*/
|
|
52544
|
-
|
|
52606
|
+
faceRecognitionEnabled: require_clip_model_registry.boolean().default(true),
|
|
52545
52607
|
/**
|
|
52546
52608
|
* Cosine similarity (on L2-normalized embeddings) required to match. A
|
|
52547
52609
|
* property of ONE model's cosine space: it moves with the default model.
|
|
@@ -52643,7 +52705,7 @@ function resolveFaceSettings(raw) {
|
|
|
52643
52705
|
return parsed.success ? parsed.data : FACE_DEFAULTS[key];
|
|
52644
52706
|
};
|
|
52645
52707
|
return {
|
|
52646
|
-
|
|
52708
|
+
faceRecognitionEnabled: pick("faceRecognitionEnabled"),
|
|
52647
52709
|
similarityThreshold: pick("similarityThreshold"),
|
|
52648
52710
|
margin: pick("margin"),
|
|
52649
52711
|
minFacePx: pick("minFacePx"),
|
|
@@ -56662,7 +56724,117 @@ async function migrateDeviceTemplateVariable(ports) {
|
|
|
56662
56724
|
};
|
|
56663
56725
|
}
|
|
56664
56726
|
//#endregion
|
|
56665
|
-
//#region src/migrations/
|
|
56727
|
+
//#region src/migrations/split-enabled-key.ts
|
|
56728
|
+
/**
|
|
56729
|
+
* The shared settings key `enabled` becomes two (D987, D986 §2).
|
|
56730
|
+
*
|
|
56731
|
+
* One key meant two things: "Face recognition on" and "Suppress parked objects". Toggling one
|
|
56732
|
+
* flipped the other. Each switch now has its own key. So today's behaviour does not change, the
|
|
56733
|
+
* value an install has stored under `enabled` is copied into BOTH new keys, and `enabled` is
|
|
56734
|
+
* deleted. That covers the cluster row (`addon-settings`) and every camera's own row
|
|
56735
|
+
* (`addon-device-settings`).
|
|
56736
|
+
*
|
|
56737
|
+
* A new key that already has a row is never overwritten (a seed never overwrites): it was written
|
|
56738
|
+
* by the build that reads it. The plan has nothing left to do once `enabled` is gone, so a rerun is
|
|
56739
|
+
* a no-op. A read that fails throws and nothing is written.
|
|
56740
|
+
*/
|
|
56741
|
+
var PIPELINE_ANALYTICS_ID = "pipeline-analytics";
|
|
56742
|
+
var RETIRED_ENABLED_KEY = "enabled";
|
|
56743
|
+
var NEW_KEYS = ["faceRecognitionEnabled", "stationarySuppressionEnabled"];
|
|
56744
|
+
/** The canonical settings table: one row per key, id `<addonId>.<key>`. */
|
|
56745
|
+
var SETTINGS_TABLE = "addon-settings";
|
|
56746
|
+
/** The per-device settings table: one row per field, id `<addonId>:<deviceId>.<field>`. */
|
|
56747
|
+
var DEVICE_SETTINGS_TABLE = "addon-device-settings";
|
|
56748
|
+
function clusterEdits(stored) {
|
|
56749
|
+
if (!(RETIRED_ENABLED_KEY in stored)) return [];
|
|
56750
|
+
const value = stored[RETIRED_ENABLED_KEY];
|
|
56751
|
+
const rowId = (key) => `${PIPELINE_ANALYTICS_ID}.${key}`;
|
|
56752
|
+
const missing = NEW_KEYS.filter((key) => !(key in stored));
|
|
56753
|
+
const edits = [];
|
|
56754
|
+
if (missing.length > 0) edits.push({
|
|
56755
|
+
op: "setKeys",
|
|
56756
|
+
scope: SETTINGS_TABLE,
|
|
56757
|
+
values: Object.fromEntries(missing.map((key) => [rowId(key), {
|
|
56758
|
+
addonId: PIPELINE_ANALYTICS_ID,
|
|
56759
|
+
key,
|
|
56760
|
+
value
|
|
56761
|
+
}])),
|
|
56762
|
+
precondition: require_clip_model_registry.preconditionOf(Object.fromEntries(missing.map((key) => [rowId(key), null])))
|
|
56763
|
+
});
|
|
56764
|
+
edits.push({
|
|
56765
|
+
op: "deleteKeys",
|
|
56766
|
+
scope: SETTINGS_TABLE,
|
|
56767
|
+
keys: [rowId(RETIRED_ENABLED_KEY)],
|
|
56768
|
+
precondition: require_clip_model_registry.preconditionOf({ [rowId(RETIRED_ENABLED_KEY)]: {
|
|
56769
|
+
addonId: PIPELINE_ANALYTICS_ID,
|
|
56770
|
+
key: RETIRED_ENABLED_KEY,
|
|
56771
|
+
value
|
|
56772
|
+
} })
|
|
56773
|
+
});
|
|
56774
|
+
return edits;
|
|
56775
|
+
}
|
|
56776
|
+
async function deviceEdits(ports) {
|
|
56777
|
+
const ids = await ports.listDeviceIds();
|
|
56778
|
+
if (ids.length === 0) return [];
|
|
56779
|
+
const retired = await ports.readDeviceField(ids, RETIRED_ENABLED_KEY);
|
|
56780
|
+
if (retired.size === 0) return [];
|
|
56781
|
+
const carriers = [...retired.keys()];
|
|
56782
|
+
const existing = /* @__PURE__ */ new Map();
|
|
56783
|
+
for (const key of NEW_KEYS) existing.set(key, await ports.readDeviceField(carriers, key));
|
|
56784
|
+
const rowId = (deviceId, field) => `${PIPELINE_ANALYTICS_ID}:${deviceId}.${field}`;
|
|
56785
|
+
const created = {};
|
|
56786
|
+
const retiredRows = {};
|
|
56787
|
+
for (const [deviceId, value] of retired) {
|
|
56788
|
+
for (const key of NEW_KEYS) {
|
|
56789
|
+
if (existing.get(key)?.has(deviceId) === true) continue;
|
|
56790
|
+
created[rowId(deviceId, key)] = {
|
|
56791
|
+
addonId: PIPELINE_ANALYTICS_ID,
|
|
56792
|
+
deviceId: String(deviceId),
|
|
56793
|
+
key,
|
|
56794
|
+
value
|
|
56795
|
+
};
|
|
56796
|
+
}
|
|
56797
|
+
retiredRows[rowId(deviceId, RETIRED_ENABLED_KEY)] = {
|
|
56798
|
+
addonId: PIPELINE_ANALYTICS_ID,
|
|
56799
|
+
deviceId: String(deviceId),
|
|
56800
|
+
key: RETIRED_ENABLED_KEY,
|
|
56801
|
+
value
|
|
56802
|
+
};
|
|
56803
|
+
}
|
|
56804
|
+
const edits = [];
|
|
56805
|
+
const createdIds = Object.keys(created);
|
|
56806
|
+
if (createdIds.length > 0) edits.push({
|
|
56807
|
+
op: "setKeys",
|
|
56808
|
+
scope: DEVICE_SETTINGS_TABLE,
|
|
56809
|
+
values: created,
|
|
56810
|
+
precondition: require_clip_model_registry.preconditionOf(Object.fromEntries(createdIds.map((id) => [id, null])))
|
|
56811
|
+
});
|
|
56812
|
+
edits.push({
|
|
56813
|
+
op: "deleteKeys",
|
|
56814
|
+
scope: DEVICE_SETTINGS_TABLE,
|
|
56815
|
+
keys: Object.keys(retiredRows),
|
|
56816
|
+
precondition: require_clip_model_registry.preconditionOf(retiredRows)
|
|
56817
|
+
});
|
|
56818
|
+
return edits;
|
|
56819
|
+
}
|
|
56820
|
+
/** Copy `enabled` into both new keys (cluster row and every camera's row) and delete it. */
|
|
56821
|
+
async function planSplitEnabledKey(ports) {
|
|
56822
|
+
return { edits: [...clusterEdits(await ports.readStored([RETIRED_ENABLED_KEY, ...NEW_KEYS])), ...await deviceEdits(ports)] };
|
|
56823
|
+
}
|
|
56824
|
+
/**
|
|
56825
|
+
* The port's read over an addon's three-way key read: each of `keys` that has a row, raw. A key
|
|
56826
|
+
* the store could not answer for throws, so the migration fails instead of treating a failed read
|
|
56827
|
+
* as "nothing to move".
|
|
56828
|
+
*/
|
|
56829
|
+
async function readRawKeys(read, keys) {
|
|
56830
|
+
const found = {};
|
|
56831
|
+
for (const key of keys) {
|
|
56832
|
+
const answer = await read(key);
|
|
56833
|
+
if (answer.kind === "unreadable") throw new Error(`${PIPELINE_ANALYTICS_ID}: the setting ${key} cannot be read (${answer.reason})`);
|
|
56834
|
+
if (answer.kind === "present") found[key] = answer.value;
|
|
56835
|
+
}
|
|
56836
|
+
return found;
|
|
56837
|
+
}
|
|
56666
56838
|
/** The `pipeline-analytics` catalogue (D987): offered to the engine at the hub's boot. */
|
|
56667
56839
|
var ADDON_MIGRATIONS = [{
|
|
56668
56840
|
meta: {
|
|
@@ -56679,6 +56851,21 @@ var ADDON_MIGRATIONS = [{
|
|
|
56679
56851
|
fixtureMark: "the rule name and the trigger conditions the migration does not know"
|
|
56680
56852
|
},
|
|
56681
56853
|
procedure: migrateDeviceTemplateVariable
|
|
56854
|
+
}, {
|
|
56855
|
+
meta: {
|
|
56856
|
+
id: "pipeline-analytics/2026-11-05-split-enabled-key",
|
|
56857
|
+
owner: PIPELINE_ANALYTICS_ID,
|
|
56858
|
+
level: "addon-state",
|
|
56859
|
+
seq: 2,
|
|
56860
|
+
dependsOn: [],
|
|
56861
|
+
store: "settings",
|
|
56862
|
+
scope: "cluster",
|
|
56863
|
+
onFailure: "block-owner",
|
|
56864
|
+
bodyKind: "plan",
|
|
56865
|
+
description: "Split the shared `enabled` setting into faceRecognitionEnabled and stationarySuppressionEnabled, copying the stored value into both, for the cluster and for every camera",
|
|
56866
|
+
fixtureMark: "a setting the migration does not know"
|
|
56867
|
+
},
|
|
56868
|
+
plan: planSplitEnabledKey
|
|
56682
56869
|
}];
|
|
56683
56870
|
//#endregion
|
|
56684
56871
|
//#region src/pipeline-analytics/clip-space-plan.ts
|
|
@@ -71545,7 +71732,6 @@ Object.freeze({
|
|
|
71545
71732
|
"addon-routes": ["getRoutes"],
|
|
71546
71733
|
"addon-widgets-source": ["listWidgets"],
|
|
71547
71734
|
"board-key-source": ["listSources"],
|
|
71548
|
-
"broker": ["list", "listProviders"],
|
|
71549
71735
|
"calendar-source": ["listCalendars", "listEvents"],
|
|
71550
71736
|
"custom-model-registry": ["listModels"],
|
|
71551
71737
|
"data-store-provider": ["histogram", "query"],
|
|
@@ -72092,7 +72278,7 @@ var SensorTransitionGate = class {
|
|
|
72092
72278
|
var StationarySettingsSchema = require_clip_model_registry.object({
|
|
72093
72279
|
/** Master switch. Off ⇒ this camera gathers no evidence and suppresses
|
|
72094
72280
|
* nothing (every parked object keeps spawning normal tracks). */
|
|
72095
|
-
|
|
72281
|
+
stationarySuppressionEnabled: require_clip_model_registry.boolean().default(true),
|
|
72096
72282
|
/** IoU at/above which a detection is the same parked object, unmoved →
|
|
72097
72283
|
* suppress its spawn. `SUPPRESS_IOU`. */
|
|
72098
72284
|
suppressIou: require_clip_model_registry.number().min(.3).max(.9).default(SUPPRESS_IOU),
|
|
@@ -72119,7 +72305,7 @@ function resolveStationarySettings(raw) {
|
|
|
72119
72305
|
return parsed.success ? parsed.data : STATIONARY_DEFAULTS[key];
|
|
72120
72306
|
};
|
|
72121
72307
|
return {
|
|
72122
|
-
|
|
72308
|
+
stationarySuppressionEnabled: pick("stationarySuppressionEnabled"),
|
|
72123
72309
|
suppressIou: pick("suppressIou"),
|
|
72124
72310
|
wakeAssocIou: pick("wakeAssocIou"),
|
|
72125
72311
|
sessionStillnessWindowMs: pick("sessionStillnessWindowMs"),
|
|
@@ -72530,10 +72716,10 @@ function buildDetectionSettingsSections() {
|
|
|
72530
72716
|
fields: [
|
|
72531
72717
|
{
|
|
72532
72718
|
type: "boolean",
|
|
72533
|
-
key: "
|
|
72719
|
+
key: "stationarySuppressionEnabled",
|
|
72534
72720
|
label: "Suppress parked objects",
|
|
72535
72721
|
description: "Promote a stopped object to a lightweight registry entry and suppress its detections from re-spawning tracks. Off = every parked object keeps spawning normal tracks on this camera.",
|
|
72536
|
-
default: STATIONARY_DEFAULTS.
|
|
72722
|
+
default: STATIONARY_DEFAULTS.stationarySuppressionEnabled
|
|
72537
72723
|
},
|
|
72538
72724
|
{
|
|
72539
72725
|
type: "slider",
|
|
@@ -72743,8 +72929,6 @@ function retagRetentionSection(sections) {
|
|
|
72743
72929
|
* model-substitution readout.
|
|
72744
72930
|
*/
|
|
72745
72931
|
var SUGGESTION_THRESHOLD_READOUT_KEY = "suggestionThresholdReadout";
|
|
72746
|
-
/** `getGlobalSettings({ cap })` scope for the face matching form. */
|
|
72747
|
-
var FACE_GALLERY_SETTINGS_CAP = "face-gallery";
|
|
72748
72932
|
var FACE_RECOGNITION_SECTION_ID = "face-recognition";
|
|
72749
72933
|
/** Percent, at most one decimal, without a trailing `.0`. Cosines here are
|
|
72750
72934
|
* two-decimal operator values; `36.0%` reads like false precision. */
|
|
@@ -72766,7 +72950,7 @@ function formatSuggestionThresholdReadout(similarityThreshold) {
|
|
|
72766
72950
|
* by each device's detection-pipeline steps, so a per-device toggle would be
|
|
72767
72951
|
* misleading. Keyed by section id → set of field keys to drop.
|
|
72768
72952
|
*/
|
|
72769
|
-
var GLOBAL_ONLY_FIELDS = { "face-recognition": new Set(["
|
|
72953
|
+
var GLOBAL_ONLY_FIELDS = { "face-recognition": new Set(["faceRecognitionEnabled", SUGGESTION_THRESHOLD_READOUT_KEY]) };
|
|
72770
72954
|
/**
|
|
72771
72955
|
* Whole sections that are cluster-wide (global page only) and must NOT appear
|
|
72772
72956
|
* in a per-device contribution (D62: a per-camera field whose reader only ever
|
|
@@ -72825,8 +73009,8 @@ function stripDeviceOnlyFields(sections) {
|
|
|
72825
73009
|
* in `BaseAddon.schema()`; the general sections below are followed by the
|
|
72826
73010
|
* detection-knob sections in the original order.
|
|
72827
73011
|
*/
|
|
72828
|
-
function buildGlobalSettingsSchema(
|
|
72829
|
-
return
|
|
73012
|
+
function buildGlobalSettingsSchema() {
|
|
73013
|
+
return { sections: [...[
|
|
72830
73014
|
{
|
|
72831
73015
|
id: "cluster-post-processing",
|
|
72832
73016
|
title: "Cluster post-processing",
|
|
@@ -73405,10 +73589,10 @@ function buildGlobalSettingsSchema(cap) {
|
|
|
73405
73589
|
fields: [
|
|
73406
73590
|
{
|
|
73407
73591
|
type: "boolean",
|
|
73408
|
-
key: "
|
|
73592
|
+
key: "faceRecognitionEnabled",
|
|
73409
73593
|
label: "Face recognition on",
|
|
73410
73594
|
description: "Off stops face matching for every camera. On by default.",
|
|
73411
|
-
default: FACE_DEFAULTS.
|
|
73595
|
+
default: FACE_DEFAULTS.faceRecognitionEnabled
|
|
73412
73596
|
},
|
|
73413
73597
|
{
|
|
73414
73598
|
type: "number",
|
|
@@ -73503,25 +73687,22 @@ function buildGlobalSettingsSchema(cap) {
|
|
|
73503
73687
|
}
|
|
73504
73688
|
]
|
|
73505
73689
|
}
|
|
73506
|
-
], ...buildDetectionSettingsSections()] }
|
|
73690
|
+
], ...buildDetectionSettingsSections()] };
|
|
73691
|
+
}
|
|
73692
|
+
/** The face matching form: the cluster form of `face-gallery`. */
|
|
73693
|
+
function buildFaceClusterSchema() {
|
|
73694
|
+
return { sections: buildGlobalSettingsSchema().sections.filter((section) => section.id === FACE_RECOGNITION_SECTION_ID) };
|
|
73695
|
+
}
|
|
73696
|
+
/** The cluster-wide knobs of `pipeline-analytics`: the sections a camera never overrides. */
|
|
73697
|
+
function buildAnalyticsClusterSchema() {
|
|
73698
|
+
return { sections: buildGlobalSettingsSchema().sections.filter((section) => GLOBAL_ONLY_SECTIONS.has(section.id)) };
|
|
73507
73699
|
}
|
|
73508
73700
|
/**
|
|
73509
|
-
* The
|
|
73510
|
-
*
|
|
73511
|
-
* → Settings asks for `face-gallery`; System → Settings omits `cap` and
|
|
73512
|
-
* still gets every section. An unknown cap is empty so this addon cannot
|
|
73513
|
-
* appear on a page that meant someone else.
|
|
73701
|
+
* The fleet defaults of `pipeline-analytics`: every knob a camera can override on its own form,
|
|
73702
|
+
* the face section excluded (it is the cluster form of `face-gallery`).
|
|
73514
73703
|
*/
|
|
73515
|
-
function
|
|
73516
|
-
|
|
73517
|
-
if (cap === FACE_GALLERY_SETTINGS_CAP) return {
|
|
73518
|
-
...schema,
|
|
73519
|
-
sections: schema.sections.filter((s) => s.id === FACE_RECOGNITION_SECTION_ID)
|
|
73520
|
-
};
|
|
73521
|
-
return {
|
|
73522
|
-
...schema,
|
|
73523
|
-
sections: []
|
|
73524
|
-
};
|
|
73704
|
+
function buildAnalyticsDeviceDefaultsSchema() {
|
|
73705
|
+
return { sections: buildGlobalSettingsSchema().sections.filter((section) => section.id !== FACE_RECOGNITION_SECTION_ID && !GLOBAL_ONLY_SECTIONS.has(section.id)) };
|
|
73525
73706
|
}
|
|
73526
73707
|
//#endregion
|
|
73527
73708
|
//#region src/pipeline-analytics/store/embedding-json-conversion.ts
|
|
@@ -124086,7 +124267,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
124086
124267
|
}
|
|
124087
124268
|
_faceGlobalEnabledState = null;
|
|
124088
124269
|
get faceGlobalEnabledState() {
|
|
124089
|
-
if (!this._faceGlobalEnabledState) this._faceGlobalEnabledState = this.state("
|
|
124270
|
+
if (!this._faceGlobalEnabledState) this._faceGlobalEnabledState = this.state("faceRecognitionEnabled", FaceSettingsSchema.shape.faceRecognitionEnabled, FACE_DEFAULTS.faceRecognitionEnabled);
|
|
124090
124271
|
return this._faceGlobalEnabledState;
|
|
124091
124272
|
}
|
|
124092
124273
|
constructor() {
|
|
@@ -124463,10 +124644,19 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
124463
124644
|
/** The addon's migration catalogue (D987), bound to the notification kinds. */
|
|
124464
124645
|
async runTemplateMigrations() {
|
|
124465
124646
|
const records = this.requireRecords();
|
|
124647
|
+
const settings = this.ctx.settings;
|
|
124648
|
+
if (settings === void 0) throw new Error("the per-device settings store is unavailable");
|
|
124466
124649
|
const ports = {
|
|
124467
124650
|
rules: records.kind(ncRecordKinds["rules"]),
|
|
124468
124651
|
timelapseRules: records.kind(ncRecordKinds["timelapse-rules"]),
|
|
124469
|
-
texts: records.kind(ncRecordKinds.texts)
|
|
124652
|
+
texts: records.kind(ncRecordKinds.texts),
|
|
124653
|
+
readStored: (keys) => readRawKeys((key) => this.readStoredGlobalKey(key), keys),
|
|
124654
|
+
listDeviceIds: () => settings.listDeviceStoreIds(),
|
|
124655
|
+
readDeviceField: async (deviceIds, field) => {
|
|
124656
|
+
const found = /* @__PURE__ */ new Map();
|
|
124657
|
+
for (const [deviceId, blob] of await settings.readDeviceStoreBatch(deviceIds)) if (field in blob) found.set(deviceId, blob[field]);
|
|
124658
|
+
return found;
|
|
124659
|
+
}
|
|
124470
124660
|
};
|
|
124471
124661
|
await this.runMigrations(require_clip_model_registry.defineAddonMigrations(ADDON_MIGRATIONS, ports));
|
|
124472
124662
|
}
|
|
@@ -127033,7 +127223,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
127033
127223
|
const stationarySettings = this.stationarySettingsFromCache(deviceId);
|
|
127034
127224
|
const packageSettings = this.packageDropSettingsFromCache(deviceId);
|
|
127035
127225
|
const packageClasses = new Set(packageSettings.packageDropClassFilter);
|
|
127036
|
-
if (planeRunsStationary(source) && this.stationaryRegistry && this.stationaryEvidence && stationarySettings.
|
|
127226
|
+
if (planeRunsStationary(source) && this.stationaryRegistry && this.stationaryEvidence && stationarySettings.stationarySuppressionEnabled) {
|
|
127037
127227
|
const evidence = this.stationaryEvidence;
|
|
127038
127228
|
for (const t of result.tracked) {
|
|
127039
127229
|
if (packageClasses.has(t.className)) continue;
|
|
@@ -129377,7 +129567,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
129377
129567
|
const registry = this.stationaryRegistry;
|
|
129378
129568
|
p.setStationaryGate({ filter: (input) => {
|
|
129379
129569
|
const s = this.stationarySettingsFromCache(deviceId);
|
|
129380
|
-
if (!s.
|
|
129570
|
+
if (!s.stationarySuppressionEnabled) return {
|
|
129381
129571
|
suppressedIndices: /* @__PURE__ */ new Set(),
|
|
129382
129572
|
continuableIndices: /* @__PURE__ */ new Set(),
|
|
129383
129573
|
confirmed: [],
|
|
@@ -132734,6 +132924,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
132734
132924
|
} });
|
|
132735
132925
|
return { jobId };
|
|
132736
132926
|
}
|
|
132927
|
+
/** Stored media per storage location (see the cap): indexed aggregates, no walk. */
|
|
132928
|
+
async getMediaLocationCensus(input) {
|
|
132929
|
+
return this.requireMediaStore().censusByLocation(input.locationIds);
|
|
132930
|
+
}
|
|
132737
132931
|
/**
|
|
132738
132932
|
* How many media / retrain rows still carry no `locationId`.
|
|
132739
132933
|
*
|
|
@@ -132937,8 +133131,25 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
132937
133131
|
});
|
|
132938
133132
|
return await readWinningBlob(candidates, pick, readBlob);
|
|
132939
133133
|
}
|
|
132940
|
-
|
|
132941
|
-
return
|
|
133134
|
+
scopedSettingsCapabilities() {
|
|
133135
|
+
return ["pipeline-analytics", "face-gallery"];
|
|
133136
|
+
}
|
|
133137
|
+
/**
|
|
133138
|
+
* D986 M4. The face matching form is the cluster form of `face-gallery`; the cluster-wide knobs
|
|
133139
|
+
* (post-processing node, index maintenance, device alerts, prune windows) are the cluster form
|
|
133140
|
+
* of `pipeline-analytics`. A page asks for ONE capability's form, and System → Settings asks for
|
|
133141
|
+
* all of them. The schemas are static; the only live value (the derived suggestion floor) is
|
|
133142
|
+
* injected into the hydrate blob by `getGlobalSettings`.
|
|
133143
|
+
*/
|
|
133144
|
+
async clusterSettingsSchema(cap) {
|
|
133145
|
+
if (cap === "face-gallery") return this.schema(buildFaceClusterSchema());
|
|
133146
|
+
if (cap === "pipeline-analytics") return this.schema(buildAnalyticsClusterSchema());
|
|
133147
|
+
return null;
|
|
133148
|
+
}
|
|
133149
|
+
/** The knobs a camera can override: stored as the fleet default, resolved per camera. */
|
|
133150
|
+
async deviceDefaultsSchema(cap) {
|
|
133151
|
+
if (cap === "pipeline-analytics") return this.schema(buildAnalyticsDeviceDefaultsSchema());
|
|
133152
|
+
return null;
|
|
132942
133153
|
}
|
|
132943
133154
|
/**
|
|
132944
133155
|
* The only reason this is overridden: the face suggestion floor is DERIVED
|
|
@@ -132952,11 +133163,11 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
132952
133163
|
* implies and has nothing to contradict it with. Everything else about this
|
|
132953
133164
|
* call is `BaseAddon`'s.
|
|
132954
133165
|
*
|
|
132955
|
-
* (Fix round, L3.) `
|
|
133166
|
+
* (Fix round, L3.) `buildGlobalSettingsSchema()` is reused, untouched, by
|
|
132956
133167
|
* `getDeviceSettingsContribution` below to build the PER-DEVICE form —
|
|
132957
133168
|
* `trackingTape` must stay there. So the strip happens here, on the result
|
|
132958
133169
|
* THIS method actually hands to the global settings page, never inside
|
|
132959
|
-
* `
|
|
133170
|
+
* `buildGlobalSettingsSchema()` itself, which would also remove it from the
|
|
132960
133171
|
* per-device form that shares the same base sections.
|
|
132961
133172
|
*/
|
|
132962
133173
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
@@ -132978,7 +133189,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
132978
133189
|
}
|
|
132979
133190
|
async getDeviceSettingsContribution(input) {
|
|
132980
133191
|
if (!await this.isCameraDevice(input.deviceId)) return null;
|
|
132981
|
-
const schema =
|
|
133192
|
+
const schema = buildGlobalSettingsSchema();
|
|
132982
133193
|
const raw = await this.readDeviceBlob(input.deviceId);
|
|
132983
133194
|
const globalRaw = await this.rawGlobalStoreMirror.resolve();
|
|
132984
133195
|
const baseSections = schema ? hydrateWithInheritedGlobals({
|