@camstack/addon-post-analysis 1.2.660 → 1.2.661
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 +7 -0
- package/dist/{clip-model-registry-D9BbIQ-h.mjs → clip-model-registry-CVajwmEk.mjs} +78 -7
- package/dist/{clip-model-registry-DAKPBZfa.js → clip-model-registry-RvEPUqvS.js} +83 -6
- 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-CvrKdp_8.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CwKrui6c.mjs} +2 -2
- package/dist/pipeline-analytics/{_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BwWOFFOc.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BGdtpHOa.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit--CbMNXQy.mjs → hostInit-DR75U6YM.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +330 -168
- package/dist/pipeline-analytics/index.mjs +330 -168
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +2 -2
|
@@ -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-RvEPUqvS.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");
|
|
@@ -8888,11 +8888,25 @@ function cleanSourcePreference(ownerKind) {
|
|
|
8888
8888
|
* The ONE placeholder grammar. `renderTemplate` substitutes what it matches and
|
|
8889
8889
|
* {@link templatePlaceholders} lists it; a second regex here would be a legend
|
|
8890
8890
|
* that disagrees with the renderer about what a variable is, which is the
|
|
8891
|
-
* failure mode where an editor advertises `{{
|
|
8891
|
+
* failure mode where an editor advertises `{{ device }}` as unsupported and the
|
|
8892
8892
|
* notification prints the camera anyway.
|
|
8893
8893
|
*/
|
|
8894
8894
|
var PLACEHOLDER_PATTERN = /\{\{\s*(\w+)\s*\}\}/g;
|
|
8895
8895
|
/**
|
|
8896
|
+
* Names that were renamed and still resolve at render time, mapped to the name
|
|
8897
|
+
* that replaced them. `{{camera}}` became `{{device}}` (a rule on an appliance
|
|
8898
|
+
* names that device, not a camera); a template written by an older client still
|
|
8899
|
+
* prints the right name. An alias is NOT declared in the catalog and no picker
|
|
8900
|
+
* offers it: the stored rules are rewritten by the `pipeline-analytics` template
|
|
8901
|
+
* migration, and this table only covers a client that has not updated yet.
|
|
8902
|
+
*/
|
|
8903
|
+
var NC_DEPRECATED_TEMPLATE_VARS = { camera: "device" };
|
|
8904
|
+
/** The deprecated names a template still uses, in order of first appearance. */
|
|
8905
|
+
function deprecatedPlaceholders(template) {
|
|
8906
|
+
if (template === void 0) return [];
|
|
8907
|
+
return templatePlaceholders(template).filter((name) => name in NC_DEPRECATED_TEMPLATE_VARS);
|
|
8908
|
+
}
|
|
8909
|
+
/**
|
|
8896
8910
|
* The variable names a template names, de-duplicated, in order of first
|
|
8897
8911
|
* appearance.
|
|
8898
8912
|
*
|
|
@@ -8918,14 +8932,19 @@ function templatePlaceholders(template) {
|
|
|
8918
8932
|
*/
|
|
8919
8933
|
function renderTemplate(template, vars) {
|
|
8920
8934
|
if (template === void 0 || template.trim().length === 0) return null;
|
|
8921
|
-
return template.replace(PLACEHOLDER_PATTERN, (_m, name) =>
|
|
8935
|
+
return template.replace(PLACEHOLDER_PATTERN, (_m, name) => {
|
|
8936
|
+
const current = vars[name];
|
|
8937
|
+
if (current !== void 0) return current;
|
|
8938
|
+
const renamed = NC_DEPRECATED_TEMPLATE_VARS[name];
|
|
8939
|
+
return (renamed !== void 0 ? vars[renamed] : void 0) ?? "";
|
|
8940
|
+
});
|
|
8922
8941
|
}
|
|
8923
8942
|
//#endregion
|
|
8924
8943
|
//#region src/notification-center/texts/en.json
|
|
8925
8944
|
var en_default = {
|
|
8926
8945
|
"detection.title": "{{rule}}",
|
|
8927
|
-
"detection.body": "{{subject}} on {{
|
|
8928
|
-
"detection.body.trackEnd": "{{subject}} on {{
|
|
8946
|
+
"detection.body": "{{subject}} on {{device}}{{inZones}}",
|
|
8947
|
+
"detection.body.trackEnd": "{{subject}} on {{device}}{{inZones}} — visit ended",
|
|
8929
8948
|
"detection.subject": "{{classNoun}}",
|
|
8930
8949
|
"detection.subject.labelled": "{{classNoun}} ({{label}})",
|
|
8931
8950
|
"detection.subject.identity": "{{label}}",
|
|
@@ -8997,11 +9016,11 @@ var en_default = {
|
|
|
8997
9016
|
"other": "{{count}} other {{classNoun}}"
|
|
8998
9017
|
},
|
|
8999
9018
|
"package.delivered.title": "Parcel delivered",
|
|
9000
|
-
"package.delivered.body": "Delivered at {{
|
|
9019
|
+
"package.delivered.body": "Delivered at {{device}}{{inZones}}",
|
|
9001
9020
|
"package.delivered.possible.title": "Possible parcel",
|
|
9002
|
-
"package.delivered.possible.body": "Possible parcel at {{
|
|
9021
|
+
"package.delivered.possible.body": "Possible parcel at {{device}}{{inZones}}",
|
|
9003
9022
|
"package.picked-up.title": "Parcel collected",
|
|
9004
|
-
"package.picked-up.body": "Collected from {{
|
|
9023
|
+
"package.picked-up.body": "Collected from {{device}}{{inZones}}",
|
|
9005
9024
|
"occupancy.body": "{{scope}} {{op}} ({{count}}/{{capacity}})",
|
|
9006
9025
|
"occupancy.op.occupied": "occupied",
|
|
9007
9026
|
"occupancy.op.free": "free",
|
|
@@ -9137,7 +9156,7 @@ var en_default = {
|
|
|
9137
9156
|
"system.camera-enabled.title": "Device switched on",
|
|
9138
9157
|
"system.camera-enabled.body": "Device {{deviceName}} was switched back on",
|
|
9139
9158
|
"test.prefix": "[TEST] {{body}}",
|
|
9140
|
-
"timelapse.title": "Timelapse — {{
|
|
9159
|
+
"timelapse.title": "Timelapse — {{device}}",
|
|
9141
9160
|
"timelapse.body.window": "{{rule}} — {{from}}→{{to}}",
|
|
9142
9161
|
"timelapse.body.tooManyEvents": "too many events for dense sampling: video at a uniform cadence",
|
|
9143
9162
|
"timelapse.body.denseRanges": {
|
|
@@ -9151,7 +9170,7 @@ var en_default = {
|
|
|
9151
9170
|
},
|
|
9152
9171
|
"snooze.digest.header": "{{scope}} was snoozed, {{from}}–{{to}}.",
|
|
9153
9172
|
"snooze.digest.header.global": "{{scope}} was snoozed for everyone, {{from}}–{{to}}.",
|
|
9154
|
-
"snooze.digest.line": "• {{rule}} on {{
|
|
9173
|
+
"snooze.digest.line": "• {{rule}} on {{device}} — {{count}}",
|
|
9155
9174
|
"snooze.scope.all": "Everything",
|
|
9156
9175
|
"snooze.scope.anyDevice": "A camera",
|
|
9157
9176
|
"snooze.scope.anyRule": "A rule",
|
|
@@ -9330,8 +9349,8 @@ var en_default = {
|
|
|
9330
9349
|
//#region src/notification-center/texts/it.json
|
|
9331
9350
|
var it_default = {
|
|
9332
9351
|
"detection.title": "{{rule}}",
|
|
9333
|
-
"detection.body": "{{subject}} su {{
|
|
9334
|
-
"detection.body.trackEnd": "{{subject}} su {{
|
|
9352
|
+
"detection.body": "{{subject}} su {{device}}{{inZones}}",
|
|
9353
|
+
"detection.body.trackEnd": "{{subject}} su {{device}}{{inZones}} — visita terminata",
|
|
9335
9354
|
"detection.subject": "{{classNoun}}",
|
|
9336
9355
|
"detection.subject.labelled": "{{classNoun}} ({{label}})",
|
|
9337
9356
|
"detection.subject.identity": "{{label}}",
|
|
@@ -9403,11 +9422,11 @@ var it_default = {
|
|
|
9403
9422
|
"other": "{{count}} altri {{classNoun}}"
|
|
9404
9423
|
},
|
|
9405
9424
|
"package.delivered.title": "Pacco consegnato",
|
|
9406
|
-
"package.delivered.body": "Consegnato su {{
|
|
9425
|
+
"package.delivered.body": "Consegnato su {{device}}{{inZones}}",
|
|
9407
9426
|
"package.delivered.possible.title": "Possibile pacco",
|
|
9408
|
-
"package.delivered.possible.body": "Possibile pacco su {{
|
|
9427
|
+
"package.delivered.possible.body": "Possibile pacco su {{device}}{{inZones}}",
|
|
9409
9428
|
"package.picked-up.title": "Pacco ritirato",
|
|
9410
|
-
"package.picked-up.body": "Ritirato da {{
|
|
9429
|
+
"package.picked-up.body": "Ritirato da {{device}}{{inZones}}",
|
|
9411
9430
|
"occupancy.body": "{{scope}} {{op}} ({{count}}/{{capacity}})",
|
|
9412
9431
|
"occupancy.op.occupied": "occupata",
|
|
9413
9432
|
"occupancy.op.free": "libera",
|
|
@@ -9543,7 +9562,7 @@ var it_default = {
|
|
|
9543
9562
|
"system.camera-enabled.title": "Dispositivo riacceso",
|
|
9544
9563
|
"system.camera-enabled.body": "Il dispositivo {{deviceName}} è stato riacceso",
|
|
9545
9564
|
"test.prefix": "[PROVA] {{body}}",
|
|
9546
|
-
"timelapse.title": "Sequenza — {{
|
|
9565
|
+
"timelapse.title": "Sequenza — {{device}}",
|
|
9547
9566
|
"timelapse.body.window": "{{rule}} — {{from}}→{{to}}",
|
|
9548
9567
|
"timelapse.body.tooManyEvents": "troppi eventi per il campionamento denso: video a cadenza uniforme",
|
|
9549
9568
|
"timelapse.body.denseRanges": {
|
|
@@ -9557,7 +9576,7 @@ var it_default = {
|
|
|
9557
9576
|
},
|
|
9558
9577
|
"snooze.digest.header": "{{scope}} in pausa, {{from}}–{{to}}.",
|
|
9559
9578
|
"snooze.digest.header.global": "{{scope}} in pausa per tutti, {{from}}–{{to}}.",
|
|
9560
|
-
"snooze.digest.line": "• {{rule}} su {{
|
|
9579
|
+
"snooze.digest.line": "• {{rule}} su {{device}} — {{count}}",
|
|
9561
9580
|
"snooze.scope.all": "Tutto",
|
|
9562
9581
|
"snooze.scope.anyDevice": "Una camera",
|
|
9563
9582
|
"snooze.scope.anyRule": "Una regola",
|
|
@@ -9781,7 +9800,7 @@ var it_default = {
|
|
|
9781
9800
|
* as plain strings. {@link renderTemplate} never re-scans a replacement, so
|
|
9782
9801
|
* there is exactly one level of composition and it cannot grow: a fragment that
|
|
9783
9802
|
* names another composite renders it empty, and a value that happens to look
|
|
9784
|
-
* like `{{
|
|
9803
|
+
* like `{{device}}` is printed, not substituted.
|
|
9785
9804
|
*/
|
|
9786
9805
|
/** The languages the hub ships texts for. */
|
|
9787
9806
|
var NC_LANGUAGES = ["en", "it"];
|
|
@@ -56230,6 +56249,258 @@ var ZoneCatalogueCache = class {
|
|
|
56230
56249
|
}
|
|
56231
56250
|
};
|
|
56232
56251
|
//#endregion
|
|
56252
|
+
//#region src/notification-center/record-kinds.ts
|
|
56253
|
+
/**
|
|
56254
|
+
* Record kinds owned by the pipeline-analytics addon (records plan phase 2,
|
|
56255
|
+
* steps 18-24): the notification-center config tables.
|
|
56256
|
+
*
|
|
56257
|
+
* The notification-center module is hosted inside the pipeline-analytics addon.
|
|
56258
|
+
* That addon declares no `execution.group`, so the UDS owner stamp is the addon
|
|
56259
|
+
* id: `pipeline-analytics`. Declared hub-only (agents have no `ctx.records`).
|
|
56260
|
+
* One deploy covers all 7 kinds.
|
|
56261
|
+
*/
|
|
56262
|
+
/**
|
|
56263
|
+
* The hub language and operator-authored per-key overrides.
|
|
56264
|
+
* One row, id `texts`. Losing it degrades to the shipped English catalog.
|
|
56265
|
+
*/
|
|
56266
|
+
var TextsBodySchema = require_clip_model_registry.object({
|
|
56267
|
+
language: NcLanguageSchema,
|
|
56268
|
+
overrides: require_clip_model_registry.partialRecord(NcLanguageSchema, require_clip_model_registry.record(require_clip_model_registry.string(), require_clip_model_registry.string()))
|
|
56269
|
+
});
|
|
56270
|
+
/**
|
|
56271
|
+
* A shared action sequence (without id, which is the record key).
|
|
56272
|
+
* Extends the wire shape with `createdAt` / `updatedAt` (stored by the legacy table).
|
|
56273
|
+
*/
|
|
56274
|
+
var SequenceBodySchema = require_clip_model_registry.NcSharedSequenceSchema.omit({ id: true }).extend({
|
|
56275
|
+
createdAt: require_clip_model_registry.number(),
|
|
56276
|
+
updatedAt: require_clip_model_registry.number()
|
|
56277
|
+
});
|
|
56278
|
+
/**
|
|
56279
|
+
* The AI section of one ordinary rule. Record id = the rule id.
|
|
56280
|
+
*/
|
|
56281
|
+
var RuleAiBodySchema = require_clip_model_registry.object({
|
|
56282
|
+
ai: NcRuleAiSchema,
|
|
56283
|
+
updatedAt: require_clip_model_registry.number()
|
|
56284
|
+
});
|
|
56285
|
+
/** A calendar-events rule (without `id`). */
|
|
56286
|
+
var CalendarEventsRuleBodySchema = NcCalendarEventsRuleSchema.omit({ id: true });
|
|
56287
|
+
/** A multi-camera digest rule (without `id`). */
|
|
56288
|
+
var SummaryRuleBodySchema = NcSummaryRuleSchema.omit({ id: true });
|
|
56289
|
+
/** A timelapse rule (without `id`). */
|
|
56290
|
+
var TimelapseRuleBodySchema = require_clip_model_registry.TimelapseRuleSchema.omit({ id: true });
|
|
56291
|
+
/** A notification rule (without `id`). */
|
|
56292
|
+
var RuleBodySchema = require_clip_model_registry.NcRuleSchema.omit({ id: true });
|
|
56293
|
+
var ncRecordKinds = require_clip_model_registry.defineRecordKinds({
|
|
56294
|
+
/**
|
|
56295
|
+
* The hub language + per-key text overrides. One row (id `texts`).
|
|
56296
|
+
*/
|
|
56297
|
+
texts: {
|
|
56298
|
+
schema: TextsBodySchema,
|
|
56299
|
+
class: "config",
|
|
56300
|
+
writeTrigger: "an operator changes the hub language (setLanguage) or replaces the per-key override set (setOverrides)",
|
|
56301
|
+
device: "none",
|
|
56302
|
+
at: "none",
|
|
56303
|
+
retention: { none: "one row (id `texts`); losing it costs the chosen language and household wording, never a notification — every read degrades to the shipped English catalog" }
|
|
56304
|
+
},
|
|
56305
|
+
/**
|
|
56306
|
+
* Shared action sequences — named, ordered runs of steps with their own throttle.
|
|
56307
|
+
* One row per sequence, id = sequence UUID.
|
|
56308
|
+
*/
|
|
56309
|
+
sequences: {
|
|
56310
|
+
schema: SequenceBodySchema,
|
|
56311
|
+
class: "config",
|
|
56312
|
+
writeTrigger: "an admin creates/edits/deletes a shared sequence via notificationRules.createSequence / updateSequence / deleteSequence, or the one-shot boot migration that materialises legacy inline sequences",
|
|
56313
|
+
device: "none",
|
|
56314
|
+
at: "none",
|
|
56315
|
+
retention: { none: "a row goes only when an admin deletes it; bounded by the sequence set a human is willing to author" }
|
|
56316
|
+
},
|
|
56317
|
+
/**
|
|
56318
|
+
* Per-rule AI section: whether and how a rule narrates itself.
|
|
56319
|
+
* Record id = the rule id it belongs to.
|
|
56320
|
+
*/
|
|
56321
|
+
"rule-ai": {
|
|
56322
|
+
schema: RuleAiBodySchema,
|
|
56323
|
+
class: "config",
|
|
56324
|
+
writeTrigger: "an operator switches the AI section of a notification rule on or off, or edits its prompt / word cap / language",
|
|
56325
|
+
device: "none",
|
|
56326
|
+
at: "none",
|
|
56327
|
+
retention: { none: "a row goes when the operator clears the section or deletes the rule; bounded by the rule set a human is willing to author" }
|
|
56328
|
+
},
|
|
56329
|
+
/**
|
|
56330
|
+
* Calendar-events rules. One row per rule, id = rule UUID.
|
|
56331
|
+
*/
|
|
56332
|
+
"calendar-events-rules": {
|
|
56333
|
+
schema: CalendarEventsRuleBodySchema,
|
|
56334
|
+
class: "config",
|
|
56335
|
+
writeTrigger: "an operator creates or edits a calendar-events rule, or the producer stamps a delivered window via markGenerated",
|
|
56336
|
+
device: "none",
|
|
56337
|
+
at: "none",
|
|
56338
|
+
indexes: [{
|
|
56339
|
+
name: "enabled",
|
|
56340
|
+
fields: ["enabled"]
|
|
56341
|
+
}],
|
|
56342
|
+
retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
|
|
56343
|
+
},
|
|
56344
|
+
/**
|
|
56345
|
+
* Multi-camera digest rules. One row per rule, id = rule UUID.
|
|
56346
|
+
*/
|
|
56347
|
+
"summary-rules": {
|
|
56348
|
+
schema: SummaryRuleBodySchema,
|
|
56349
|
+
class: "config",
|
|
56350
|
+
writeTrigger: "an operator creates or edits a digest rule, or the producer stamps a delivered window via markGenerated",
|
|
56351
|
+
device: "none",
|
|
56352
|
+
at: "none",
|
|
56353
|
+
indexes: [{
|
|
56354
|
+
name: "enabled",
|
|
56355
|
+
fields: ["enabled"]
|
|
56356
|
+
}],
|
|
56357
|
+
retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
|
|
56358
|
+
},
|
|
56359
|
+
/**
|
|
56360
|
+
* Timelapse rules. One row per rule, id = rule UUID.
|
|
56361
|
+
* Three rows on prod (2026-10-18).
|
|
56362
|
+
*/
|
|
56363
|
+
"timelapse-rules": {
|
|
56364
|
+
schema: TimelapseRuleBodySchema,
|
|
56365
|
+
class: "config",
|
|
56366
|
+
writeTrigger: "an operator creates or edits a timelapse rule, or the scheduler stamps a successful per-camera generation via markGenerated",
|
|
56367
|
+
device: "none",
|
|
56368
|
+
at: "none",
|
|
56369
|
+
indexes: [{
|
|
56370
|
+
name: "enabled",
|
|
56371
|
+
fields: ["enabled"]
|
|
56372
|
+
}],
|
|
56373
|
+
retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
|
|
56374
|
+
},
|
|
56375
|
+
/**
|
|
56376
|
+
* Notification rules — the main rule set. One row per rule, id = rule UUID.
|
|
56377
|
+
* 18 rows on prod (2026-10-18); most coupled kind in this owner.
|
|
56378
|
+
*/
|
|
56379
|
+
rules: {
|
|
56380
|
+
schema: RuleBodySchema,
|
|
56381
|
+
class: "config",
|
|
56382
|
+
writeTrigger: "an operator creates, edits, enables/disables a rule or opts a target out of it; nothing writes on the evaluation path",
|
|
56383
|
+
device: "none",
|
|
56384
|
+
at: "none",
|
|
56385
|
+
indexes: [{
|
|
56386
|
+
name: "delivery",
|
|
56387
|
+
fields: ["delivery"]
|
|
56388
|
+
}],
|
|
56389
|
+
retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
|
|
56390
|
+
}
|
|
56391
|
+
});
|
|
56392
|
+
//#endregion
|
|
56393
|
+
//#region src/migrations/device-template-var.ts
|
|
56394
|
+
/** `{{camera}}` and `{{ camera }}`: the whitespace the renderer allows is kept as written. */
|
|
56395
|
+
var OLD_VARIABLE = /\{\{(\s*)camera(\s*)\}\}/g;
|
|
56396
|
+
/** The template text with the variable renamed; the same string when it names none. */
|
|
56397
|
+
function renameDeviceVariable(text) {
|
|
56398
|
+
return text.replace(OLD_VARIABLE, "{{$1device$2}}");
|
|
56399
|
+
}
|
|
56400
|
+
/** A `{ title, body }` template with both fields renamed; `null` when neither changed. */
|
|
56401
|
+
function renamedTemplate(template) {
|
|
56402
|
+
if (template === void 0) return null;
|
|
56403
|
+
const title = template.title === void 0 ? void 0 : renameDeviceVariable(template.title);
|
|
56404
|
+
const body = template.body === void 0 ? void 0 : renameDeviceVariable(template.body);
|
|
56405
|
+
if (title === template.title && body === template.body) return null;
|
|
56406
|
+
return {
|
|
56407
|
+
...template,
|
|
56408
|
+
...title !== void 0 ? { title } : {},
|
|
56409
|
+
...body !== void 0 ? { body } : {}
|
|
56410
|
+
};
|
|
56411
|
+
}
|
|
56412
|
+
function renamedRule(data) {
|
|
56413
|
+
const template = renamedTemplate(data.template);
|
|
56414
|
+
return template === null ? null : {
|
|
56415
|
+
...data,
|
|
56416
|
+
template
|
|
56417
|
+
};
|
|
56418
|
+
}
|
|
56419
|
+
function renamedTimelapseRule(data) {
|
|
56420
|
+
const template = renamedTemplate(data.template);
|
|
56421
|
+
const previewText = data.previewText === void 0 ? void 0 : renameDeviceVariable(data.previewText);
|
|
56422
|
+
const previewChanged = previewText !== data.previewText;
|
|
56423
|
+
if (template === null && !previewChanged) return null;
|
|
56424
|
+
return {
|
|
56425
|
+
...data,
|
|
56426
|
+
...template !== null ? { template } : {},
|
|
56427
|
+
...previewChanged && previewText !== void 0 ? { previewText } : {}
|
|
56428
|
+
};
|
|
56429
|
+
}
|
|
56430
|
+
function renamedTexts(data) {
|
|
56431
|
+
let changed = false;
|
|
56432
|
+
const overrides = Object.fromEntries(Object.entries(data.overrides).map(([language, table]) => [language, Object.fromEntries(Object.entries(table).map(([key, text]) => {
|
|
56433
|
+
const next = renameDeviceVariable(text);
|
|
56434
|
+
if (next !== text) changed = true;
|
|
56435
|
+
return [key, next];
|
|
56436
|
+
}))]));
|
|
56437
|
+
return changed ? {
|
|
56438
|
+
...data,
|
|
56439
|
+
overrides
|
|
56440
|
+
} : null;
|
|
56441
|
+
}
|
|
56442
|
+
var PAGE$1 = 500;
|
|
56443
|
+
/** Every row of a kind, page by page. */
|
|
56444
|
+
async function allRows(kind) {
|
|
56445
|
+
const rows = [];
|
|
56446
|
+
let cursor;
|
|
56447
|
+
do {
|
|
56448
|
+
const page = await kind.query({
|
|
56449
|
+
limit: PAGE$1,
|
|
56450
|
+
...cursor !== void 0 ? { cursor } : {}
|
|
56451
|
+
});
|
|
56452
|
+
rows.push(...page.records);
|
|
56453
|
+
cursor = page.nextCursor ?? void 0;
|
|
56454
|
+
} while (cursor !== void 0);
|
|
56455
|
+
return rows;
|
|
56456
|
+
}
|
|
56457
|
+
/** Rewrite the rows of one kind that carry the old name; returns how many were written. */
|
|
56458
|
+
async function rewriteKind(kind, rewrite) {
|
|
56459
|
+
let written = 0;
|
|
56460
|
+
for (const row of await allRows(kind)) {
|
|
56461
|
+
const next = rewrite(row.data);
|
|
56462
|
+
if (next === null) continue;
|
|
56463
|
+
await kind.put({
|
|
56464
|
+
id: row.id,
|
|
56465
|
+
data: next
|
|
56466
|
+
}, { ifRev: row.rev });
|
|
56467
|
+
written += 1;
|
|
56468
|
+
}
|
|
56469
|
+
return written;
|
|
56470
|
+
}
|
|
56471
|
+
async function migrateDeviceTemplateVariable(ports) {
|
|
56472
|
+
const rules = await rewriteKind(ports.rules, renamedRule);
|
|
56473
|
+
const timelapseRules = await rewriteKind(ports.timelapseRules, renamedTimelapseRule);
|
|
56474
|
+
const texts = await rewriteKind(ports.texts, renamedTexts);
|
|
56475
|
+
return {
|
|
56476
|
+
outcome: rules + timelapseRules + texts === 0 ? "already-done" : "done",
|
|
56477
|
+
summary: {
|
|
56478
|
+
rules,
|
|
56479
|
+
timelapseRules,
|
|
56480
|
+
texts
|
|
56481
|
+
}
|
|
56482
|
+
};
|
|
56483
|
+
}
|
|
56484
|
+
//#endregion
|
|
56485
|
+
//#region src/migrations/index.ts
|
|
56486
|
+
/** The `pipeline-analytics` catalogue (D987): offered to the engine at the hub's boot. */
|
|
56487
|
+
var ADDON_MIGRATIONS = [{
|
|
56488
|
+
meta: {
|
|
56489
|
+
id: "pipeline-analytics/2026-11-01-template-camera-to-device",
|
|
56490
|
+
owner: "pipeline-analytics",
|
|
56491
|
+
level: "addon-state",
|
|
56492
|
+
seq: 1,
|
|
56493
|
+
dependsOn: [],
|
|
56494
|
+
store: "settings",
|
|
56495
|
+
scope: "cluster",
|
|
56496
|
+
onFailure: "tolerate",
|
|
56497
|
+
bodyKind: "procedure",
|
|
56498
|
+
description: "Rename the {{camera}} template variable to {{device}} in every stored notification rule, timelapse rule and text override",
|
|
56499
|
+
fixtureMark: "the rule name and the trigger conditions the migration does not know"
|
|
56500
|
+
},
|
|
56501
|
+
procedure: migrateDeviceTemplateVariable
|
|
56502
|
+
}];
|
|
56503
|
+
//#endregion
|
|
56233
56504
|
//#region src/pipeline-analytics/clip-space-plan.ts
|
|
56234
56505
|
/**
|
|
56235
56506
|
* Which CLIP feature space a search reads and a rebuild fills — decided in one
|
|
@@ -67128,7 +67399,7 @@ var CaptureScheduler = class {
|
|
|
67128
67399
|
for (let i = lane.queue.length - 1; i >= 0; i -= 1) {
|
|
67129
67400
|
const entry = lane.queue[i];
|
|
67130
67401
|
if (entry === void 0) continue;
|
|
67131
|
-
if (entry.staleAfterMs === void 0 || nowMs - entry.enqueuedAt
|
|
67402
|
+
if (entry.staleAfterMs === void 0 || nowMs - entry.enqueuedAt < entry.staleAfterMs) continue;
|
|
67132
67403
|
lane.queue.splice(i, 1);
|
|
67133
67404
|
this.bump(deviceId, "droppedStale");
|
|
67134
67405
|
this.noteDropped(deviceId, entry, "stale");
|
|
@@ -67204,7 +67475,7 @@ var CaptureScheduler = class {
|
|
|
67204
67475
|
while (lane.inFlight < this.budget && lane.queue.length > 0) {
|
|
67205
67476
|
const entry = lane.queue.shift();
|
|
67206
67477
|
if (entry === void 0) return;
|
|
67207
|
-
if (entry.staleAfterMs !== void 0 && this.now() - entry.enqueuedAt
|
|
67478
|
+
if (entry.staleAfterMs !== void 0 && this.now() - entry.enqueuedAt >= entry.staleAfterMs) {
|
|
67208
67479
|
this.bump(deviceId, "droppedStale");
|
|
67209
67480
|
this.noteDropped(deviceId, entry, "stale");
|
|
67210
67481
|
entry.drop();
|
|
@@ -82396,147 +82667,6 @@ var NcDeviceMuteStore = class {
|
|
|
82396
82667
|
}
|
|
82397
82668
|
};
|
|
82398
82669
|
//#endregion
|
|
82399
|
-
//#region src/notification-center/record-kinds.ts
|
|
82400
|
-
/**
|
|
82401
|
-
* Record kinds owned by the pipeline-analytics addon (records plan phase 2,
|
|
82402
|
-
* steps 18-24): the notification-center config tables.
|
|
82403
|
-
*
|
|
82404
|
-
* The notification-center module is hosted inside the pipeline-analytics addon.
|
|
82405
|
-
* That addon declares no `execution.group`, so the UDS owner stamp is the addon
|
|
82406
|
-
* id: `pipeline-analytics`. Declared hub-only (agents have no `ctx.records`).
|
|
82407
|
-
* One deploy covers all 7 kinds.
|
|
82408
|
-
*/
|
|
82409
|
-
/**
|
|
82410
|
-
* The hub language and operator-authored per-key overrides.
|
|
82411
|
-
* One row, id `texts`. Losing it degrades to the shipped English catalog.
|
|
82412
|
-
*/
|
|
82413
|
-
var TextsBodySchema = require_clip_model_registry.object({
|
|
82414
|
-
language: NcLanguageSchema,
|
|
82415
|
-
overrides: require_clip_model_registry.partialRecord(NcLanguageSchema, require_clip_model_registry.record(require_clip_model_registry.string(), require_clip_model_registry.string()))
|
|
82416
|
-
});
|
|
82417
|
-
/**
|
|
82418
|
-
* A shared action sequence (without id, which is the record key).
|
|
82419
|
-
* Extends the wire shape with `createdAt` / `updatedAt` (stored by the legacy table).
|
|
82420
|
-
*/
|
|
82421
|
-
var SequenceBodySchema = require_clip_model_registry.NcSharedSequenceSchema.omit({ id: true }).extend({
|
|
82422
|
-
createdAt: require_clip_model_registry.number(),
|
|
82423
|
-
updatedAt: require_clip_model_registry.number()
|
|
82424
|
-
});
|
|
82425
|
-
/**
|
|
82426
|
-
* The AI section of one ordinary rule. Record id = the rule id.
|
|
82427
|
-
*/
|
|
82428
|
-
var RuleAiBodySchema = require_clip_model_registry.object({
|
|
82429
|
-
ai: NcRuleAiSchema,
|
|
82430
|
-
updatedAt: require_clip_model_registry.number()
|
|
82431
|
-
});
|
|
82432
|
-
/** A calendar-events rule (without `id`). */
|
|
82433
|
-
var CalendarEventsRuleBodySchema = NcCalendarEventsRuleSchema.omit({ id: true });
|
|
82434
|
-
/** A multi-camera digest rule (without `id`). */
|
|
82435
|
-
var SummaryRuleBodySchema = NcSummaryRuleSchema.omit({ id: true });
|
|
82436
|
-
/** A timelapse rule (without `id`). */
|
|
82437
|
-
var TimelapseRuleBodySchema = require_clip_model_registry.TimelapseRuleSchema.omit({ id: true });
|
|
82438
|
-
/** A notification rule (without `id`). */
|
|
82439
|
-
var RuleBodySchema = require_clip_model_registry.NcRuleSchema.omit({ id: true });
|
|
82440
|
-
var ncRecordKinds = require_clip_model_registry.defineRecordKinds({
|
|
82441
|
-
/**
|
|
82442
|
-
* The hub language + per-key text overrides. One row (id `texts`).
|
|
82443
|
-
*/
|
|
82444
|
-
texts: {
|
|
82445
|
-
schema: TextsBodySchema,
|
|
82446
|
-
class: "config",
|
|
82447
|
-
writeTrigger: "an operator changes the hub language (setLanguage) or replaces the per-key override set (setOverrides)",
|
|
82448
|
-
device: "none",
|
|
82449
|
-
at: "none",
|
|
82450
|
-
retention: { none: "one row (id `texts`); losing it costs the chosen language and household wording, never a notification — every read degrades to the shipped English catalog" }
|
|
82451
|
-
},
|
|
82452
|
-
/**
|
|
82453
|
-
* Shared action sequences — named, ordered runs of steps with their own throttle.
|
|
82454
|
-
* One row per sequence, id = sequence UUID.
|
|
82455
|
-
*/
|
|
82456
|
-
sequences: {
|
|
82457
|
-
schema: SequenceBodySchema,
|
|
82458
|
-
class: "config",
|
|
82459
|
-
writeTrigger: "an admin creates/edits/deletes a shared sequence via notificationRules.createSequence / updateSequence / deleteSequence, or the one-shot boot migration that materialises legacy inline sequences",
|
|
82460
|
-
device: "none",
|
|
82461
|
-
at: "none",
|
|
82462
|
-
retention: { none: "a row goes only when an admin deletes it; bounded by the sequence set a human is willing to author" }
|
|
82463
|
-
},
|
|
82464
|
-
/**
|
|
82465
|
-
* Per-rule AI section: whether and how a rule narrates itself.
|
|
82466
|
-
* Record id = the rule id it belongs to.
|
|
82467
|
-
*/
|
|
82468
|
-
"rule-ai": {
|
|
82469
|
-
schema: RuleAiBodySchema,
|
|
82470
|
-
class: "config",
|
|
82471
|
-
writeTrigger: "an operator switches the AI section of a notification rule on or off, or edits its prompt / word cap / language",
|
|
82472
|
-
device: "none",
|
|
82473
|
-
at: "none",
|
|
82474
|
-
retention: { none: "a row goes when the operator clears the section or deletes the rule; bounded by the rule set a human is willing to author" }
|
|
82475
|
-
},
|
|
82476
|
-
/**
|
|
82477
|
-
* Calendar-events rules. One row per rule, id = rule UUID.
|
|
82478
|
-
*/
|
|
82479
|
-
"calendar-events-rules": {
|
|
82480
|
-
schema: CalendarEventsRuleBodySchema,
|
|
82481
|
-
class: "config",
|
|
82482
|
-
writeTrigger: "an operator creates or edits a calendar-events rule, or the producer stamps a delivered window via markGenerated",
|
|
82483
|
-
device: "none",
|
|
82484
|
-
at: "none",
|
|
82485
|
-
indexes: [{
|
|
82486
|
-
name: "enabled",
|
|
82487
|
-
fields: ["enabled"]
|
|
82488
|
-
}],
|
|
82489
|
-
retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
|
|
82490
|
-
},
|
|
82491
|
-
/**
|
|
82492
|
-
* Multi-camera digest rules. One row per rule, id = rule UUID.
|
|
82493
|
-
*/
|
|
82494
|
-
"summary-rules": {
|
|
82495
|
-
schema: SummaryRuleBodySchema,
|
|
82496
|
-
class: "config",
|
|
82497
|
-
writeTrigger: "an operator creates or edits a digest rule, or the producer stamps a delivered window via markGenerated",
|
|
82498
|
-
device: "none",
|
|
82499
|
-
at: "none",
|
|
82500
|
-
indexes: [{
|
|
82501
|
-
name: "enabled",
|
|
82502
|
-
fields: ["enabled"]
|
|
82503
|
-
}],
|
|
82504
|
-
retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
|
|
82505
|
-
},
|
|
82506
|
-
/**
|
|
82507
|
-
* Timelapse rules. One row per rule, id = rule UUID.
|
|
82508
|
-
* Three rows on prod (2026-10-18).
|
|
82509
|
-
*/
|
|
82510
|
-
"timelapse-rules": {
|
|
82511
|
-
schema: TimelapseRuleBodySchema,
|
|
82512
|
-
class: "config",
|
|
82513
|
-
writeTrigger: "an operator creates or edits a timelapse rule, or the scheduler stamps a successful per-camera generation via markGenerated",
|
|
82514
|
-
device: "none",
|
|
82515
|
-
at: "none",
|
|
82516
|
-
indexes: [{
|
|
82517
|
-
name: "enabled",
|
|
82518
|
-
fields: ["enabled"]
|
|
82519
|
-
}],
|
|
82520
|
-
retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
|
|
82521
|
-
},
|
|
82522
|
-
/**
|
|
82523
|
-
* Notification rules — the main rule set. One row per rule, id = rule UUID.
|
|
82524
|
-
* 18 rows on prod (2026-10-18); most coupled kind in this owner.
|
|
82525
|
-
*/
|
|
82526
|
-
rules: {
|
|
82527
|
-
schema: RuleBodySchema,
|
|
82528
|
-
class: "config",
|
|
82529
|
-
writeTrigger: "an operator creates, edits, enables/disables a rule or opts a target out of it; nothing writes on the evaluation path",
|
|
82530
|
-
device: "none",
|
|
82531
|
-
at: "none",
|
|
82532
|
-
indexes: [{
|
|
82533
|
-
name: "delivery",
|
|
82534
|
-
fields: ["delivery"]
|
|
82535
|
-
}],
|
|
82536
|
-
retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
|
|
82537
|
-
}
|
|
82538
|
-
});
|
|
82539
|
-
//#endregion
|
|
82540
82670
|
//#region src/notification-center/sequence-store.ts
|
|
82541
82671
|
/**
|
|
82542
82672
|
* Shared action sequences for the notification centre.
|
|
@@ -87621,6 +87751,26 @@ var NcDispatcher = class {
|
|
|
87621
87751
|
renderText(input) {
|
|
87622
87752
|
return renderRowText(this.texts, input);
|
|
87623
87753
|
}
|
|
87754
|
+
/** Rules already warned about a deprecated variable in this process — once each. */
|
|
87755
|
+
deprecatedVarWarned = /* @__PURE__ */ new Set();
|
|
87756
|
+
/**
|
|
87757
|
+
* A rule template written by an older client may still say `{{camera}}`. It
|
|
87758
|
+
* renders (the alias), and the rule is named once so the operator can edit it.
|
|
87759
|
+
*/
|
|
87760
|
+
warnDeprecatedVars(entry) {
|
|
87761
|
+
if (this.deprecatedVarWarned.has(entry.ruleId)) return;
|
|
87762
|
+
const template = entry.payload.template;
|
|
87763
|
+
const used = [...new Set([...deprecatedPlaceholders(template?.title), ...deprecatedPlaceholders(template?.body)])];
|
|
87764
|
+
if (used.length === 0) return;
|
|
87765
|
+
this.deprecatedVarWarned.add(entry.ruleId);
|
|
87766
|
+
this.deps.logger.warn("rule template uses a renamed variable — it still renders, edit it to the new name", {
|
|
87767
|
+
tags: { ruleId: entry.ruleId },
|
|
87768
|
+
meta: {
|
|
87769
|
+
variables: used,
|
|
87770
|
+
replacement: "device"
|
|
87771
|
+
}
|
|
87772
|
+
});
|
|
87773
|
+
}
|
|
87624
87774
|
/**
|
|
87625
87775
|
* Builds the notification AND returns the artefact ids its media resolve
|
|
87626
87776
|
* indexed — see {@link withArtifactUrls}. A tuple rather than a field on
|
|
@@ -87634,6 +87784,7 @@ var NcDispatcher = class {
|
|
|
87634
87784
|
const alarmSource = systemEvent?.alarmSource;
|
|
87635
87785
|
const zoneLabels = systemEvent === void 0 ? await resolveZoneLabels(this.deps.getZoneNames, subject.deviceId, subject.zones) : alarmSource?.zones !== void 0 && alarmSource.zones.length > 0 ? await resolveZoneLabels(this.deps.getZoneNames, alarmSource.deviceId, alarmSource.zones) : [];
|
|
87636
87786
|
const vars = buildTemplateVars(this.texts, entry, deviceName, zoneLabels);
|
|
87787
|
+
this.warnDeprecatedVars(entry);
|
|
87637
87788
|
const systemCount = systemEvent?.textCount !== void 0 ? { count: systemEvent.textCount } : {};
|
|
87638
87789
|
const title = renderRowTitle(this.texts, entry, vars);
|
|
87639
87790
|
const authoredBody = entry.payload.template?.body;
|
|
@@ -88748,15 +88899,15 @@ async function resolveZoneLabels(getZoneNames, deviceId, zoneIds) {
|
|
|
88748
88899
|
function percentOf(confidence) {
|
|
88749
88900
|
return confidence !== void 0 ? `${Math.round(confidence * 100)}%` : "";
|
|
88750
88901
|
}
|
|
88751
|
-
/** What `{{
|
|
88902
|
+
/** What `{{device}}` renders on a system row that names no device. */
|
|
88752
88903
|
var NC_DEVICELESS_CAMERA_NAME = "system";
|
|
88753
88904
|
/**
|
|
88754
|
-
* The name `{{
|
|
88905
|
+
* The name `{{device}}` renders for a row — the ONE derivation, shared by the
|
|
88755
88906
|
* dispatcher and the template-variable parity spec.
|
|
88756
88907
|
*
|
|
88757
88908
|
* A system row that names no device (a node, a backup, an update) renders the
|
|
88758
88909
|
* filler {@link NC_DEVICELESS_CAMERA_NAME}: there is no camera to name, which
|
|
88759
|
-
* is why the catalog offers `{{
|
|
88910
|
+
* is why the catalog offers `{{device}}` on a system rule only for the kinds
|
|
88760
88911
|
* that carry a device (template-vars.ts). Everything else asks the directory,
|
|
88761
88912
|
* and falls back to `camera <id>` when it cannot answer.
|
|
88762
88913
|
*/
|
|
@@ -88809,7 +88960,7 @@ function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone) {
|
|
|
88809
88960
|
const cameraName = deviceName.toLocaleLowerCase(locale);
|
|
88810
88961
|
const zonesLower = zoneLabels.map((z) => z.toLocaleLowerCase(locale));
|
|
88811
88962
|
return {
|
|
88812
|
-
|
|
88963
|
+
device: cameraName,
|
|
88813
88964
|
class: detection?.className ?? (systemEvent !== void 0 ? "" : subject.className),
|
|
88814
88965
|
classLabel: (() => {
|
|
88815
88966
|
const wire = detection?.className ?? (systemEvent !== void 0 ? "" : subject.className);
|
|
@@ -96673,7 +96824,7 @@ function clockOf(atMs) {
|
|
|
96673
96824
|
* catalog, the same way `buildTemplateVars` is asserted for rule rows. */
|
|
96674
96825
|
function timelapseTemplateVars(input) {
|
|
96675
96826
|
return {
|
|
96676
|
-
|
|
96827
|
+
device: input.deviceName ?? `camera ${input.deviceId}`,
|
|
96677
96828
|
rule: input.ruleName,
|
|
96678
96829
|
from: clockOf(input.window.startMs),
|
|
96679
96830
|
to: clockOf(input.window.endMs),
|
|
@@ -96739,7 +96890,7 @@ function paramsFor(target, clickUrl) {
|
|
|
96739
96890
|
*/
|
|
96740
96891
|
function timelapsePreviewText(input) {
|
|
96741
96892
|
const vars = {
|
|
96742
|
-
|
|
96893
|
+
device: input.deviceName ?? `camera ${input.deviceId}`,
|
|
96743
96894
|
...input.ruleName !== void 0 ? { rule: input.ruleName } : {},
|
|
96744
96895
|
...input.window !== void 0 ? {
|
|
96745
96896
|
from: clockOf(input.window.startMs),
|
|
@@ -104271,7 +104422,7 @@ function buildSnoozeDigest(texts, snooze, rows, deviceNames) {
|
|
|
104271
104422
|
key: "snooze.digest.line",
|
|
104272
104423
|
vars: {
|
|
104273
104424
|
rule: r.ruleName,
|
|
104274
|
-
|
|
104425
|
+
device: deviceNames.get(r.deviceId) ?? `camera ${r.deviceId}`,
|
|
104275
104426
|
count: `${r.count}`
|
|
104276
104427
|
}
|
|
104277
104428
|
}));
|
|
@@ -122906,6 +123057,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
122906
123057
|
return out;
|
|
122907
123058
|
};
|
|
122908
123059
|
await step("declareCollections", () => this.declareCollections(api));
|
|
123060
|
+
if (this.localOwnNodeId() === "hub") await step("migrations", () => this.runTemplateMigrations());
|
|
122909
123061
|
this.armAlarmPanelDeclaration(api);
|
|
122910
123062
|
const logger = this.ctx.logger;
|
|
122911
123063
|
const storage = await step("resolveMediaStorage", () => this.resolveMediaStorage(logger));
|
|
@@ -123256,6 +123408,16 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
123256
123408
|
});
|
|
123257
123409
|
return this.collectionsBootInstance;
|
|
123258
123410
|
}
|
|
123411
|
+
/** The addon's migration catalogue (D987), bound to the notification kinds. */
|
|
123412
|
+
async runTemplateMigrations() {
|
|
123413
|
+
const records = this.requireRecords();
|
|
123414
|
+
const ports = {
|
|
123415
|
+
rules: records.kind(ncRecordKinds["rules"]),
|
|
123416
|
+
timelapseRules: records.kind(ncRecordKinds["timelapse-rules"]),
|
|
123417
|
+
texts: records.kind(ncRecordKinds.texts)
|
|
123418
|
+
};
|
|
123419
|
+
await this.runMigrations(require_clip_model_registry.defineAddonMigrations(ADDON_MIGRATIONS, ports));
|
|
123420
|
+
}
|
|
123259
123421
|
/** `declare-collections.ts` — declareCollections. */
|
|
123260
123422
|
async declareCollections(api) {
|
|
123261
123423
|
return this.collectionsBoot.declareCollections(api);
|