@camstack/addon-post-analysis 1.2.154 → 1.2.156
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-Ds6x9Agm.js → dist-CXY233l4.js} +72 -0
- package/dist/{dist-D6iC--Gh.mjs → dist-DqNo8CCq.mjs} +72 -0
- 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-CnB4y-xY.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-rrVQlXbu.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-DdANt7Ia.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-C8CZ3hb-.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit-BqnSOQO7.mjs → hostInit-Bfr4wfpP.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +313 -88
- package/dist/pipeline-analytics/index.mjs +311 -106
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -8
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as audioMetricsCapability, A as NcSnoozeInputSchema, At as errMsg, B as SCENE_DEFAULT_ANCHOR_THRESHOLD, Bt as boolean, C as NC_TAXONOMY, D as NcRuleSchema, Dt as vectorDimFromBase64, E as NcRulePatchSchema, Et as systemEventFilterApplies, F as OpsLogEntrySchema, Ft as isDeviceScopedCap, G as TimelapseRuleInputSchema, Gt as partialRecord, H as SCENE_DIVERGED, Ht as literal, It as nodePin, J as TrackSourceSchema, Jt as unknown, K as TimelapseRulePatchSchema, Kt as record, L as RECORDING_EXPORT_MAX_READ_BYTES, Lt as sleep, M as NcSnoozeSuppressedSchema, Mt as DeviceType, N as NcSystemEventKindSchema, Nt as createEvent, O as NcRuleTargetSchema, Ot as videoclipsCapability, P as NcTaxonomySchema, Pt as hydrateSchema, Q as assertTimelapseCadences, R as RetrainStatusSchema, Rt as _enum, St as readTimelapseGeneratedAt, T as NcRuleInputSchema, Tt as subKindsOf, U as SceneMonitorSchema, Ut as number, V as SCENE_DEFAULT_UNCOVERED_POLICY, Vt as discriminatedUnion, W as TIMELAPSE_DENSE_FLOOR_SEC, Wt as object, X as addonWidgetsSourceCapability, Y as VISIT_MERGE_GAP_MS, Yt as EventCategory, Z as alarmPanelCapability, _ as MACRO_LABELS, a as COCO_TO_MACRO, at as deriveRecordingMode, b as NC_CONDITION_CATALOG, bt as plateGalleryCapability, ct as evaluateSensorEdge, d as DeclaredDevices, et as audioModeOf, f as EVENT_KIND_BY_CAP, ft as isDetectionMacroClass, g as LabelAttributionSchema, gt as notificationRulesCapability, h as FailureCounters, ht as kebabToCamel, i as CLUSTER_MODEL_SCOPED_STEPS, it as defineCustomActions, j as NcSnoozeSchema, jt as BaseAddon, k as NcScheduleSchema, kt as zoneAnalyticsCapability, l as DETECTION_MACRO_CLASSES, lt as faceGalleryCapability, m as FULL_IMAGE_BBOX, mt as isSourceCap, n as AnalyticsGroupRecordSchema, nt as cosineSimilarity$1, o as DEFAULT_EVENT_COLOR, p as EVENT_PAD_MS, pt as isScheduleActive, q as TimelapseRuleSchema, qt as string, r as BaseDevice, rt as customAction, st as encodeVectorBase64, t as AnalyticsGroupDetailSchema, tt as buildEventKindDescriptor, ut as failureContributionCapability, v as MediaFileKindEnum, vt as pickClusterStepModels, w as NcConditionDescriptorSchema, wt as sceneMonitorCapability, x as NC_DEFAULT_SNOOZE_MINUTES, xt as readDeviceStateFrom, y as NC_ALARM_SYSTEM_EVENT_KINDS, yt as pipelineAnalyticsCapability, zt as array } from "../dist-
|
|
1
|
+
import { $ as audioMetricsCapability, A as NcSnoozeInputSchema, At as errMsg, B as SCENE_DEFAULT_ANCHOR_THRESHOLD, Bt as boolean, C as NC_TAXONOMY, D as NcRuleSchema, Dt as vectorDimFromBase64, E as NcRulePatchSchema, Et as systemEventFilterApplies, F as OpsLogEntrySchema, Ft as isDeviceScopedCap, G as TimelapseRuleInputSchema, Gt as partialRecord, H as SCENE_DIVERGED, Ht as literal, It as nodePin, J as TrackSourceSchema, Jt as unknown, K as TimelapseRulePatchSchema, Kt as record, L as RECORDING_EXPORT_MAX_READ_BYTES, Lt as sleep, M as NcSnoozeSuppressedSchema, Mt as DeviceType, N as NcSystemEventKindSchema, Nt as createEvent, O as NcRuleTargetSchema, Ot as videoclipsCapability, P as NcTaxonomySchema, Pt as hydrateSchema, Q as assertTimelapseCadences, R as RetrainStatusSchema, Rt as _enum, St as readTimelapseGeneratedAt, T as NcRuleInputSchema, Tt as subKindsOf, U as SceneMonitorSchema, Ut as number, V as SCENE_DEFAULT_UNCOVERED_POLICY, Vt as discriminatedUnion, W as TIMELAPSE_DENSE_FLOOR_SEC, Wt as object, X as addonWidgetsSourceCapability, Y as VISIT_MERGE_GAP_MS, Yt as EventCategory, Z as alarmPanelCapability, _ as MACRO_LABELS, a as COCO_TO_MACRO, at as deriveRecordingMode, b as NC_CONDITION_CATALOG, bt as plateGalleryCapability, ct as evaluateSensorEdge, d as DeclaredDevices, et as audioModeOf, f as EVENT_KIND_BY_CAP, ft as isDetectionMacroClass, g as LabelAttributionSchema, gt as notificationRulesCapability, h as FailureCounters, ht as kebabToCamel, i as CLUSTER_MODEL_SCOPED_STEPS, it as defineCustomActions, j as NcSnoozeSchema, jt as BaseAddon, k as NcScheduleSchema, kt as zoneAnalyticsCapability, l as DETECTION_MACRO_CLASSES, lt as faceGalleryCapability, m as FULL_IMAGE_BBOX, mt as isSourceCap, n as AnalyticsGroupRecordSchema, nt as cosineSimilarity$1, o as DEFAULT_EVENT_COLOR, p as EVENT_PAD_MS, pt as isScheduleActive, q as TimelapseRuleSchema, qt as string, r as BaseDevice, rt as customAction, st as encodeVectorBase64, t as AnalyticsGroupDetailSchema, tt as buildEventKindDescriptor, ut as failureContributionCapability, v as MediaFileKindEnum, vt as pickClusterStepModels, w as NcConditionDescriptorSchema, wt as sceneMonitorCapability, x as NC_DEFAULT_SNOOZE_MINUTES, xt as readDeviceStateFrom, y as NC_ALARM_SYSTEM_EVENT_KINDS, yt as pipelineAnalyticsCapability, zt as array } from "../dist-DqNo8CCq.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";
|
|
@@ -107,6 +107,7 @@ function actionTtlMs(subject) {
|
|
|
107
107
|
case "alarm": return subject.action === "disarm" ? ALARM_DISARM_TTL_MS : DEFAULT_ACTION_TTL_MS;
|
|
108
108
|
case "sequence": return DEFAULT_ACTION_TTL_MS;
|
|
109
109
|
case "addon-update": return DEFAULT_ACTION_TTL_MS;
|
|
110
|
+
case "server-update": return DEFAULT_ACTION_TTL_MS;
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
113
|
var NcActionTokenRegistry = class {
|
|
@@ -176,9 +177,9 @@ var NcActionTokenRegistry = class {
|
|
|
176
177
|
/**
|
|
177
178
|
* What a spent grant DID, in the operator's own terms.
|
|
178
179
|
*
|
|
179
|
-
* Per `kind`, because the
|
|
180
|
+
* Per `kind`, because the families answer different questions and one
|
|
180
181
|
* generic "Done" would leave a snooze indistinguishable from an open gate on
|
|
181
|
-
* the page a tap lands on. The switch is exhaustive by construction — a
|
|
182
|
+
* the page a tap lands on. The switch is exhaustive by construction — a new
|
|
182
183
|
* grant family will not compile until it has a sentence here.
|
|
183
184
|
*/
|
|
184
185
|
function describeGrant(grant) {
|
|
@@ -187,6 +188,7 @@ function describeGrant(grant) {
|
|
|
187
188
|
case "snooze": return grant.classes === void 0 || grant.classes.length === 0 ? `Silenced for ${grant.minutes} min` : `Silenced for ${grant.minutes} min — ${grant.classes.join(", ")}`;
|
|
188
189
|
case "alarm": return grant.action === "disarm" ? "Alarm disarmed" : `Alarm armed${grant.mode !== void 0 ? ` (${grant.mode})` : ""}`;
|
|
189
190
|
case "addon-update": return `${grant.packages.length} addon update(s) on ${grant.nodeId}`;
|
|
191
|
+
case "server-update": return `Server update on ${grant.nodeId}`;
|
|
190
192
|
}
|
|
191
193
|
}
|
|
192
194
|
/** The audit line's `meta`, per family. A snooze that logged a `sequence` it
|
|
@@ -222,6 +224,10 @@ function grantMeta(grant) {
|
|
|
222
224
|
nodeId: grant.nodeId,
|
|
223
225
|
packages: grant.packages
|
|
224
226
|
};
|
|
227
|
+
case "server-update": return {
|
|
228
|
+
...common,
|
|
229
|
+
nodeId: grant.nodeId
|
|
230
|
+
};
|
|
225
231
|
}
|
|
226
232
|
}
|
|
227
233
|
/** What the human standing in front of the page is told. */
|
|
@@ -5508,6 +5514,37 @@ function buildAddonUpdateButtons(input) {
|
|
|
5508
5514
|
}];
|
|
5509
5515
|
}
|
|
5510
5516
|
//#endregion
|
|
5517
|
+
//#region src/notification-center/server-update-buttons.ts
|
|
5518
|
+
/** The id this button travels under — in the callback and in the audit line. */
|
|
5519
|
+
var SERVER_UPDATE_BUTTON_ID = "server-update-apply";
|
|
5520
|
+
function serverUpdateButtonRefusal(subject, recipient) {
|
|
5521
|
+
if (subject.kind !== "server-update-available") return "not-a-server-update";
|
|
5522
|
+
if (subject.nodeId === void 0) return "several-nodes";
|
|
5523
|
+
if (recipient.ownerUserId === void 0) return "unowned-target";
|
|
5524
|
+
return null;
|
|
5525
|
+
}
|
|
5526
|
+
/**
|
|
5527
|
+
* The button, or none.
|
|
5528
|
+
*
|
|
5529
|
+
* Returns an array rather than a nullable, so the caller composes it with the
|
|
5530
|
+
* other families by spreading — and so "which buttons survived" has one shape.
|
|
5531
|
+
*/
|
|
5532
|
+
function buildServerUpdateButtons(input) {
|
|
5533
|
+
const subject = input.subject;
|
|
5534
|
+
if (serverUpdateButtonRefusal(subject, input.recipient) !== null) return [];
|
|
5535
|
+
const nodeId = subject.nodeId ?? "";
|
|
5536
|
+
return [{
|
|
5537
|
+
id: SERVER_UPDATE_BUTTON_ID,
|
|
5538
|
+
label: input.label({ nodeId }),
|
|
5539
|
+
url: input.mintUrl({
|
|
5540
|
+
actionId: SERVER_UPDATE_BUTTON_ID,
|
|
5541
|
+
nodeId
|
|
5542
|
+
}),
|
|
5543
|
+
destructive: true,
|
|
5544
|
+
mode: "background"
|
|
5545
|
+
}];
|
|
5546
|
+
}
|
|
5547
|
+
//#endregion
|
|
5511
5548
|
//#region src/notification-center/audio-rule-matcher.ts
|
|
5512
5549
|
/**
|
|
5513
5550
|
* AudioWatcher — the Notification Center's matcher for audio rules
|
|
@@ -8589,6 +8626,13 @@ var en_default = {
|
|
|
8589
8626
|
"action.addon-update.partial": "{{nodeId}}: {{applied}} updated, {{skipped}} already current, {{failed}} failed",
|
|
8590
8627
|
"action.addon-update.uptodate": "Already up to date on {{nodeId}} — nothing to do",
|
|
8591
8628
|
"action.addon-update.unknown": "Could not read what needs updating on {{nodeId}} — nothing was changed. Open CamStack.",
|
|
8629
|
+
"button.server-update.apply": "Update server on {{nodeId}}",
|
|
8630
|
+
"action.server-update.title": "Server update",
|
|
8631
|
+
"action.server-update.started": "Updating server on {{nodeId}}…",
|
|
8632
|
+
"action.server-update.restarting": "Server update staged on {{nodeId}} — the node is restarting. You will hear when it comes back.",
|
|
8633
|
+
"action.server-update.uptodate": "Already up to date on {{nodeId}} — nothing to do",
|
|
8634
|
+
"action.server-update.unknown": "Could not read whether {{nodeId}} still needs a server update — nothing was changed. Open CamStack.",
|
|
8635
|
+
"action.server-update.failed": "Server update on {{nodeId}} was refused — nothing was changed. Open CamStack.",
|
|
8592
8636
|
"summary.title": "Summary — {{rule}}",
|
|
8593
8637
|
"summary.body.window": "{{from}}→{{to}}",
|
|
8594
8638
|
"summary.body.events": {
|
|
@@ -8827,6 +8871,13 @@ var it_default = {
|
|
|
8827
8871
|
"action.addon-update.partial": "{{nodeId}}: {{applied}} aggiornati, {{skipped}} già aggiornati, {{failed}} falliti",
|
|
8828
8872
|
"action.addon-update.uptodate": "Già aggiornato su {{nodeId}} — niente da fare",
|
|
8829
8873
|
"action.addon-update.unknown": "Non è stato possibile leggere cosa aggiornare su {{nodeId}} — non è stato cambiato nulla. Apri CamStack.",
|
|
8874
|
+
"button.server-update.apply": "Aggiorna server su {{nodeId}}",
|
|
8875
|
+
"action.server-update.title": "Aggiornamento server",
|
|
8876
|
+
"action.server-update.started": "Aggiornamento server su {{nodeId}}…",
|
|
8877
|
+
"action.server-update.restarting": "Aggiornamento server predisposto su {{nodeId}} — il nodo si riavvia. Ti avviseremo quando torna.",
|
|
8878
|
+
"action.server-update.uptodate": "Già aggiornato su {{nodeId}} — niente da fare",
|
|
8879
|
+
"action.server-update.unknown": "Non è stato possibile leggere se {{nodeId}} ha ancora bisogno di un aggiornamento server — non è stato cambiato nulla. Apri CamStack.",
|
|
8880
|
+
"action.server-update.failed": "L'aggiornamento server su {{nodeId}} è stato rifiutato — non è stato cambiato nulla. Apri CamStack.",
|
|
8830
8881
|
"summary.title": "Riepilogo — {{rule}}",
|
|
8831
8882
|
"summary.body.window": "{{from}}→{{to}}",
|
|
8832
8883
|
"summary.body.events": {
|
|
@@ -20367,6 +20418,41 @@ var NotificationCenter = class NotificationCenter {
|
|
|
20367
20418
|
packages: systemEvent?.packageNames ?? []
|
|
20368
20419
|
}
|
|
20369
20420
|
});
|
|
20421
|
+
const serverRefusal = systemEvent === void 0 ? "not-a-server-update" : serverUpdateButtonRefusal(systemEvent, recipient);
|
|
20422
|
+
const serverButtons = systemEvent === void 0 ? [] : buildServerUpdateButtons({
|
|
20423
|
+
subject: systemEvent,
|
|
20424
|
+
recipient,
|
|
20425
|
+
label: ({ nodeId }) => this.texts.text({
|
|
20426
|
+
key: "button.server-update.apply",
|
|
20427
|
+
vars: { nodeId }
|
|
20428
|
+
}),
|
|
20429
|
+
mintUrl: ({ actionId, nodeId }) => {
|
|
20430
|
+
const id = randomUUID();
|
|
20431
|
+
drafts.push({
|
|
20432
|
+
id,
|
|
20433
|
+
ttlMs: actionTtlMs({ kind: "server-update" }),
|
|
20434
|
+
grant: (expiresAt) => ({
|
|
20435
|
+
kind: "server-update",
|
|
20436
|
+
id,
|
|
20437
|
+
nodeId,
|
|
20438
|
+
deviceId: input.deviceId,
|
|
20439
|
+
actionId,
|
|
20440
|
+
targetId: input.targetId,
|
|
20441
|
+
expiresAt
|
|
20442
|
+
})
|
|
20443
|
+
});
|
|
20444
|
+
return id;
|
|
20445
|
+
}
|
|
20446
|
+
});
|
|
20447
|
+
if (serverRefusal === "several-nodes" || serverRefusal === "unowned-target") this.logger.info("server-update notification carries no apply button", {
|
|
20448
|
+
tags: { deviceId: input.deviceId },
|
|
20449
|
+
meta: {
|
|
20450
|
+
ruleId: input.ruleId,
|
|
20451
|
+
reason: serverRefusal,
|
|
20452
|
+
targetId: input.targetId,
|
|
20453
|
+
nodeIds: systemEvent?.nodeIds ?? []
|
|
20454
|
+
}
|
|
20455
|
+
});
|
|
20370
20456
|
if (alarmKind !== null && panel === null) this.logger.info("alarm notification carries no buttons — this node owns no panel", {
|
|
20371
20457
|
tags: { deviceId: input.deviceId },
|
|
20372
20458
|
meta: {
|
|
@@ -20378,7 +20464,8 @@ var NotificationCenter = class NotificationCenter {
|
|
|
20378
20464
|
...sequenceButtons,
|
|
20379
20465
|
...snoozeButtons,
|
|
20380
20466
|
...alarmButtons,
|
|
20381
|
-
...updateButtons
|
|
20467
|
+
...updateButtons,
|
|
20468
|
+
...serverButtons
|
|
20382
20469
|
];
|
|
20383
20470
|
if (out.length === 0) return [];
|
|
20384
20471
|
const now = this.now();
|
|
@@ -20500,6 +20587,7 @@ var NotificationCenter = class NotificationCenter {
|
|
|
20500
20587
|
case "alarm": return this.applyGrantedAlarm(grant);
|
|
20501
20588
|
case "sequence": return this.runGrantedSequence(grant);
|
|
20502
20589
|
case "addon-update": return this.applyGrantedAddonUpdate(grant);
|
|
20590
|
+
case "server-update": return this.applyGrantedServerUpdate(grant);
|
|
20503
20591
|
}
|
|
20504
20592
|
}
|
|
20505
20593
|
/**
|
|
@@ -20610,6 +20698,80 @@ var NotificationCenter = class NotificationCenter {
|
|
|
20610
20698
|
if (applied.length === 0 && failed.length > 0) throw new Error(`no package updated on ${grant.nodeId} (${failed.join(", ")})`);
|
|
20611
20699
|
}
|
|
20612
20700
|
/**
|
|
20701
|
+
* Stage the server update one notification was about.
|
|
20702
|
+
*
|
|
20703
|
+
* The shape matches the addon-update runner, with one extra constraint: the
|
|
20704
|
+
* **start announcement leaves BEFORE apply**. Applying the hub kills this
|
|
20705
|
+
* process; a tap that went into silence because the process died mid-apply
|
|
20706
|
+
* is worse than no button. The reboot itself is confirmed by the existing
|
|
20707
|
+
* `server-updated` event on reconnect — this method does not wait for it.
|
|
20708
|
+
*
|
|
20709
|
+
* Throws when nothing was staged and something went wrong, so the plane's
|
|
20710
|
+
* line says the run failed rather than reporting a spent token as a success.
|
|
20711
|
+
*/
|
|
20712
|
+
async applyGrantedServerUpdate(grant) {
|
|
20713
|
+
const apply = this.deps.applyServerUpdate;
|
|
20714
|
+
const check = this.deps.checkServerUpdate;
|
|
20715
|
+
if (apply === void 0 || check === void 0) throw new Error("no server updater is wired on this node");
|
|
20716
|
+
let stillAvailable;
|
|
20717
|
+
try {
|
|
20718
|
+
stillAvailable = await check(grant.nodeId);
|
|
20719
|
+
} catch (err) {
|
|
20720
|
+
this.logger.warn("server update button changed nothing — could not read whether an update is still needed", { meta: {
|
|
20721
|
+
nodeId: grant.nodeId,
|
|
20722
|
+
error: String(err)
|
|
20723
|
+
} });
|
|
20724
|
+
await this.announceServerUpdate(grant, "unknown", { nodeId: grant.nodeId });
|
|
20725
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
20726
|
+
}
|
|
20727
|
+
if (!stillAvailable) {
|
|
20728
|
+
this.logger.info("server update button applied nothing — already current", { meta: { nodeId: grant.nodeId } });
|
|
20729
|
+
await this.announceServerUpdate(grant, "uptodate", { nodeId: grant.nodeId });
|
|
20730
|
+
return;
|
|
20731
|
+
}
|
|
20732
|
+
await this.announceServerUpdate(grant, "started", { nodeId: grant.nodeId });
|
|
20733
|
+
let result;
|
|
20734
|
+
try {
|
|
20735
|
+
result = await apply({ nodeId: grant.nodeId });
|
|
20736
|
+
} catch (err) {
|
|
20737
|
+
this.logger.warn("server update from a notification button threw", { meta: {
|
|
20738
|
+
nodeId: grant.nodeId,
|
|
20739
|
+
error: String(err)
|
|
20740
|
+
} });
|
|
20741
|
+
await this.announceServerUpdate(grant, "failed", { nodeId: grant.nodeId });
|
|
20742
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
20743
|
+
}
|
|
20744
|
+
if (!result.ok) {
|
|
20745
|
+
this.logger.warn("server update from a notification button was refused", { meta: {
|
|
20746
|
+
nodeId: grant.nodeId,
|
|
20747
|
+
error: result.error
|
|
20748
|
+
} });
|
|
20749
|
+
await this.announceServerUpdate(grant, "failed", { nodeId: grant.nodeId });
|
|
20750
|
+
throw new Error(result.error);
|
|
20751
|
+
}
|
|
20752
|
+
this.logger.info("server update staged from a notification button", { meta: { nodeId: grant.nodeId } });
|
|
20753
|
+
await this.announceServerUpdate(grant, "restarting", { nodeId: grant.nodeId });
|
|
20754
|
+
}
|
|
20755
|
+
/**
|
|
20756
|
+
* One sentence about a server-update tap, to the target the notification
|
|
20757
|
+
* went to. Best-effort — `deliverAnnouncement` never throws — because an
|
|
20758
|
+
* announcement that failed must not turn a staging that HAPPENED into a run
|
|
20759
|
+
* the plane reports as failed.
|
|
20760
|
+
*/
|
|
20761
|
+
async announceServerUpdate(grant, sentence, vars) {
|
|
20762
|
+
const targetId = grant.targetId;
|
|
20763
|
+
if (targetId === void 0) return;
|
|
20764
|
+
await this.dispatcher.deliverAnnouncement({
|
|
20765
|
+
reason: `server-update-${sentence}`,
|
|
20766
|
+
targetId,
|
|
20767
|
+
title: this.texts.text({ key: "action.server-update.title" }),
|
|
20768
|
+
body: this.texts.text({
|
|
20769
|
+
key: `action.server-update.${sentence}`,
|
|
20770
|
+
vars
|
|
20771
|
+
})
|
|
20772
|
+
});
|
|
20773
|
+
}
|
|
20774
|
+
/**
|
|
20613
20775
|
* One sentence about an update tap, to the target the notification went to.
|
|
20614
20776
|
*
|
|
20615
20777
|
* Best-effort by construction — `deliverAnnouncement` never throws and never
|
|
@@ -26073,7 +26235,7 @@ var DEFAULT_MEDIA_LOCATION = "eventMedia";
|
|
|
26073
26235
|
* whose contract is disposal.** The gallery's row lives on the data volume;
|
|
26074
26236
|
* its blob belongs there too, and now does. See D197.
|
|
26075
26237
|
*/
|
|
26076
|
-
var GALLERY_MEDIA_LOCATION = "
|
|
26238
|
+
var GALLERY_MEDIA_LOCATION = "addon-data";
|
|
26077
26239
|
/**
|
|
26078
26240
|
* The location a new blob is written to, derived from its owner kind.
|
|
26079
26241
|
*
|
|
@@ -26970,7 +27132,7 @@ var MediaStore = class {
|
|
|
26970
27132
|
for (const row of rows) {
|
|
26971
27133
|
const data = row.data;
|
|
26972
27134
|
const from = mediaRowLocation(data);
|
|
26973
|
-
if (from === "
|
|
27135
|
+
if (from === "addon-data") continue;
|
|
26974
27136
|
const relativePath = String(data["path"] ?? "");
|
|
26975
27137
|
const deviceId = Number(data["deviceId"]);
|
|
26976
27138
|
if (relativePath.length === 0) continue;
|
|
@@ -35913,6 +36075,7 @@ async function runMediaReclaim(input) {
|
|
|
35913
36075
|
byteBudget -= report.bytesReclaimed;
|
|
35914
36076
|
nextCursors.set(key, report.stoppedBy === "exhausted" ? 0 : report.nextCursor);
|
|
35915
36077
|
devices.push(report);
|
|
36078
|
+
input.onDevice?.(report);
|
|
35916
36079
|
if (report.examined > 0 || report.refused > 0) input.deps.logger.info("media reclaim window", {
|
|
35917
36080
|
tags: { deviceId },
|
|
35918
36081
|
meta: {
|
|
@@ -35947,33 +36110,10 @@ async function runMediaReclaim(input) {
|
|
|
35947
36110
|
cursors: nextCursors
|
|
35948
36111
|
};
|
|
35949
36112
|
}
|
|
35950
|
-
//#endregion
|
|
35951
|
-
//#region src/pipeline-analytics/retention/media-reclaim-actions.ts
|
|
35952
|
-
/**
|
|
35953
|
-
* The operator button for the debug-media reclaim.
|
|
35954
|
-
*
|
|
35955
|
-
* The mechanism ships armed with nothing: **arming it is the operator's
|
|
35956
|
-
* call**. `mode` omitted means `report` — it counts, logs one line per camera
|
|
35957
|
-
* and returns rows/bytes per device, and deletes nothing. `reclaim` is a word
|
|
35958
|
-
* that has to be typed.
|
|
35959
|
-
*
|
|
35960
|
-
* Everything about the shape is borrowed from `orphan-audit-actions.ts`, which
|
|
35961
|
-
* earned it: dry-run default, explicit pacing, explicit blast radius, and a
|
|
35962
|
-
* report you can read instead of a log line you have to find.
|
|
35963
|
-
*/
|
|
35964
|
-
/**
|
|
35965
|
-
* `report` counts; `reclaim` deletes.
|
|
35966
|
-
*
|
|
35967
|
-
* `.default('report')` is load-bearing: the settings form sends `{}` for a
|
|
35968
|
-
* button with no fields, and the safe reading of an unstated intent is the one
|
|
35969
|
-
* that destroys nothing.
|
|
35970
|
-
*/
|
|
35971
|
-
var MediaReclaimActionModeSchema = _enum(["report", "reclaim"]).default("report");
|
|
35972
|
-
var MediaReclaimScopeSchema = _enum(["motion-still", "track-filmstrip"]);
|
|
35973
36113
|
var MediaReclaimActionInputSchema = object({
|
|
35974
|
-
mode:
|
|
36114
|
+
mode: _enum(["report", "reclaim"]).default("report"),
|
|
35975
36115
|
/** Which populations to walk. Omitted = both. */
|
|
35976
|
-
scopes: array(
|
|
36116
|
+
scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
|
|
35977
36117
|
/** Restrict to these cameras. Omitted = every camera in retention scope. */
|
|
35978
36118
|
deviceIds: array(number().int()).min(1).optional(),
|
|
35979
36119
|
/** Start every device from the top instead of resuming its cursor. */
|
|
@@ -36000,39 +36140,28 @@ var MediaReclaimActionInputSchema = object({
|
|
|
36000
36140
|
*/
|
|
36001
36141
|
graceMinutes: number().int().min(1).max(10080).optional()
|
|
36002
36142
|
});
|
|
36003
|
-
var
|
|
36004
|
-
|
|
36005
|
-
|
|
36006
|
-
examined: number(),
|
|
36007
|
-
eligible: number(),
|
|
36008
|
-
reclaimed: number(),
|
|
36009
|
-
bytesReclaimed: number(),
|
|
36010
|
-
/** Rows kept because the owning track is pinned (`staging`/`favourited`). */
|
|
36011
|
-
held: number(),
|
|
36012
|
-
/** Rows kept because the owner could not be shown to be a motion event. */
|
|
36013
|
-
unresolved: number(),
|
|
36014
|
-
withinGrace: number(),
|
|
36015
|
-
/** Deletes that REFUSED — the blob survived or the volume could not answer.
|
|
36016
|
-
* The row was kept and the cursor did not pass it. */
|
|
36017
|
-
refused: number(),
|
|
36018
|
-
stoppedBy: string(),
|
|
36019
|
-
/** Where the NEXT pass resumes. `0` = this device+scope was walked to the end. */
|
|
36020
|
-
nextCursor: number()
|
|
36143
|
+
var MediaReclaimStartedSchema = object({
|
|
36144
|
+
started: boolean(),
|
|
36145
|
+
alreadyRunning: boolean()
|
|
36021
36146
|
});
|
|
36022
|
-
var
|
|
36023
|
-
|
|
36024
|
-
|
|
36025
|
-
mode: _enum(["report", "reclaim"]),
|
|
36026
|
-
devices: array(MediaReclaimDeviceReportSchema),
|
|
36147
|
+
var MediaReclaimStatusSchema = object({
|
|
36148
|
+
running: boolean(),
|
|
36149
|
+
mode: _enum(["report", "reclaim"]).nullable(),
|
|
36027
36150
|
totalExamined: number(),
|
|
36028
36151
|
totalEligible: number(),
|
|
36029
36152
|
totalReclaimed: number(),
|
|
36030
36153
|
totalBytesReclaimed: number(),
|
|
36031
36154
|
totalRefused: number(),
|
|
36032
|
-
|
|
36033
|
-
|
|
36034
|
-
|
|
36035
|
-
|
|
36155
|
+
devicesDone: number(),
|
|
36156
|
+
complete: boolean().nullable(),
|
|
36157
|
+
startedAtMs: number().nullable(),
|
|
36158
|
+
finishedAtMs: number().nullable(),
|
|
36159
|
+
error: string().nullable()
|
|
36160
|
+
});
|
|
36161
|
+
var mediaReclaimActions = defineCustomActions({
|
|
36162
|
+
"retention.mediaReclaim": customAction(MediaReclaimActionInputSchema, MediaReclaimStartedSchema, { auth: "admin" }),
|
|
36163
|
+
"retention.mediaReclaimStatus": customAction(object({}), MediaReclaimStatusSchema, { auth: "admin" })
|
|
36164
|
+
});
|
|
36036
36165
|
var OrphanAuditActionInputSchema = object({
|
|
36037
36166
|
mode: _enum(["report", "reclaim"]).default("report"),
|
|
36038
36167
|
/**
|
|
@@ -41087,6 +41216,7 @@ function createLocationAwareMediaStorage(deps) {
|
|
|
41087
41216
|
const roots = /* @__PURE__ */ new Map();
|
|
41088
41217
|
let defaultBypassInvalidated = false;
|
|
41089
41218
|
const rootOf = async (locationId) => {
|
|
41219
|
+
if (deps.galleryLocationId !== void 0 && deps.galleryRoot !== void 0 && locationId === deps.galleryLocationId) return deps.galleryRoot;
|
|
41090
41220
|
const cached = roots.get(locationId);
|
|
41091
41221
|
if (cached !== void 0) return cached;
|
|
41092
41222
|
const root = await deps.resolveRoot(locationId);
|
|
@@ -43593,7 +43723,7 @@ var PhotoAnalysisSessions = class {
|
|
|
43593
43723
|
* a magnet that recognises every degraded probe as its owner, and 22 of 50
|
|
43594
43724
|
* live samples were enrolled that way on 2026-08-18;
|
|
43595
43725
|
* 3. both blobs are written under `ownerKind: 'identity'`, which is what puts
|
|
43596
|
-
* them
|
|
43726
|
+
* them in the addon data dir — the location a footage wipe cannot reach (D197);
|
|
43597
43727
|
* 4. the row is written last, and if it does not land BOTH blobs are reclaimed.
|
|
43598
43728
|
* An orphan under `identity` is exempt from every sweep this repo has, so
|
|
43599
43729
|
* it would otherwise live forever.
|
|
@@ -64112,6 +64242,20 @@ var TIMELAPSE_TRACK_QUERY_LIMIT = 2e3;
|
|
|
64112
64242
|
* window rather than a random slice of it.
|
|
64113
64243
|
*/
|
|
64114
64244
|
var TIMELAPSE_EVENT_QUERY_LIMIT = 1e4;
|
|
64245
|
+
var IDLE_MEDIA_RECLAIM = {
|
|
64246
|
+
running: false,
|
|
64247
|
+
mode: null,
|
|
64248
|
+
totalExamined: 0,
|
|
64249
|
+
totalEligible: 0,
|
|
64250
|
+
totalReclaimed: 0,
|
|
64251
|
+
totalBytesReclaimed: 0,
|
|
64252
|
+
totalRefused: 0,
|
|
64253
|
+
devicesDone: 0,
|
|
64254
|
+
complete: null,
|
|
64255
|
+
startedAtMs: null,
|
|
64256
|
+
finishedAtMs: null,
|
|
64257
|
+
error: null
|
|
64258
|
+
};
|
|
64115
64259
|
var PIPELINE_ANALYTICS_ADDON_ID = "pipeline-analytics";
|
|
64116
64260
|
/** Artefact index ⇄ shelf reconcile cadence. Hourly: the two only drift on the
|
|
64117
64261
|
* bounds' own timescale (14 days / 2000 entries / 2 GiB). */
|
|
@@ -65227,9 +65371,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
65227
65371
|
* In RAM like the audit's: the pass is resumable from zero at no cost
|
|
65228
65372
|
* beyond re-walking a head that is already empty. */
|
|
65229
65373
|
mediaReclaimCursors = /* @__PURE__ */ new Map();
|
|
65230
|
-
/**
|
|
65231
|
-
|
|
65232
|
-
mediaReclaimRunning = false;
|
|
65374
|
+
/** Live state of the detached debug-media reclaim — see `reclaimDebugMedia`. */
|
|
65375
|
+
mediaReclaimState = { ...IDLE_MEDIA_RECLAIM };
|
|
65233
65376
|
/**
|
|
65234
65377
|
* The vector-index leg's resume cursor, kept in its OWN field.
|
|
65235
65378
|
*
|
|
@@ -65669,6 +65812,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
65669
65812
|
const routed = createLocationAwareMediaStorage({
|
|
65670
65813
|
base: storage,
|
|
65671
65814
|
defaultLocation: "eventMedia",
|
|
65815
|
+
galleryLocationId: GALLERY_MEDIA_LOCATION,
|
|
65816
|
+
galleryRoot: path.join(this.ctx.dataDir, "gallery"),
|
|
65672
65817
|
resolveRoot: (locationId) => this.ctx.api.storage.resolve.query({
|
|
65673
65818
|
location: locationId,
|
|
65674
65819
|
relativePath: ""
|
|
@@ -66345,6 +66490,26 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
66345
66490
|
};
|
|
66346
66491
|
}
|
|
66347
66492
|
},
|
|
66493
|
+
checkServerUpdate: async (nodeId) => {
|
|
66494
|
+
const result = await this.ctx.api.serverManagement.checkServerUpdate.mutate({ nodeId });
|
|
66495
|
+
if (result.error) throw new Error(result.error);
|
|
66496
|
+
return result.updateAvailable;
|
|
66497
|
+
},
|
|
66498
|
+
applyServerUpdate: async ({ nodeId }) => {
|
|
66499
|
+
try {
|
|
66500
|
+
const result = await this.ctx.api.serverManagement.applyServerUpdate.mutate({ nodeId });
|
|
66501
|
+
if (!result.accepted) return {
|
|
66502
|
+
ok: false,
|
|
66503
|
+
error: result.message
|
|
66504
|
+
};
|
|
66505
|
+
return { ok: true };
|
|
66506
|
+
} catch (err) {
|
|
66507
|
+
return {
|
|
66508
|
+
ok: false,
|
|
66509
|
+
error: errMsg(err)
|
|
66510
|
+
};
|
|
66511
|
+
}
|
|
66512
|
+
},
|
|
66348
66513
|
readDeviceStates: async (ids) => {
|
|
66349
66514
|
const out = /* @__PURE__ */ new Map();
|
|
66350
66515
|
for (const id of ids) try {
|
|
@@ -67277,19 +67442,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
67277
67442
|
async reconcileGalleryMediaLocation() {
|
|
67278
67443
|
const mediaStore = this.mediaStore;
|
|
67279
67444
|
if (!mediaStore) return;
|
|
67280
|
-
try {
|
|
67281
|
-
await this.ctx.api.storage.resolve.query({
|
|
67282
|
-
location: GALLERY_MEDIA_LOCATION,
|
|
67283
|
-
relativePath: ""
|
|
67284
|
-
});
|
|
67285
|
-
} catch (err) {
|
|
67286
|
-
this.ctx.logger.error("gallery media location is NOT available — enrolling a face, a plate or a scene reference will FAIL until it is", { meta: {
|
|
67287
|
-
location: GALLERY_MEDIA_LOCATION,
|
|
67288
|
-
error: errMsg(err),
|
|
67289
|
-
hint: "the declaration is seeded when a storage-provider registers; restart this node so the manifest is re-read (D197). Refusing the write is deliberate — the alternative is the footage volume, which is where the last gallery was lost."
|
|
67290
|
-
} });
|
|
67291
|
-
return;
|
|
67292
|
-
}
|
|
67293
67445
|
try {
|
|
67294
67446
|
const result = await mediaStore.relocateExemptMediaToGallery();
|
|
67295
67447
|
if (result.relocated === 0 && result.missing === 0 && result.failed === 0) return;
|
|
@@ -72358,42 +72510,58 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
72358
72510
|
"embedding.reembedFaces": async (input) => this.runFaceReembedPass(input),
|
|
72359
72511
|
"embedding.reembedFacesStatus": async () => this.getFaceReembedStatus(),
|
|
72360
72512
|
"retention.orphanAudit": async (input) => this.runOrphanAuditNow(input),
|
|
72361
|
-
"retention.mediaReclaim": async (input) => this.
|
|
72513
|
+
"retention.mediaReclaim": async (input) => this.reclaimDebugMedia(input),
|
|
72514
|
+
"retention.mediaReclaimStatus": async () => this.getMediaReclaimStatus()
|
|
72362
72515
|
};
|
|
72363
72516
|
}
|
|
72364
72517
|
/**
|
|
72365
|
-
*
|
|
72366
|
-
* `retention.mediaReclaim`.
|
|
72367
|
-
*
|
|
72368
|
-
* DRY RUN BY DEFAULT. Draining the standing 111 GB means running this with
|
|
72369
|
-
* `mode: 'reclaim'` repeatedly until `complete` is true; each run resumes
|
|
72370
|
-
* from the per-device cursors this method keeps, and a run that is cut short
|
|
72371
|
-
* has still only forgotten rows whose blobs it proved gone.
|
|
72518
|
+
* Start a debug-media reclaim pass. DETACHED.
|
|
72372
72519
|
*
|
|
72373
|
-
*
|
|
72374
|
-
*
|
|
72375
|
-
*
|
|
72520
|
+
* Returns `{ started }` immediately. The walk is tens of minutes at 2 MB/s;
|
|
72521
|
+
* awaiting it is a timeout, not a verdict. Poll {@link getMediaReclaimStatus}.
|
|
72522
|
+
* Dry-run unless `mode: 'reclaim'`. A second call while one is in flight is
|
|
72523
|
+
* `alreadyRunning`, not a second walk on the same spindle.
|
|
72376
72524
|
*/
|
|
72377
|
-
async
|
|
72525
|
+
async reclaimDebugMedia(rawInput) {
|
|
72378
72526
|
const input = MediaReclaimActionInputSchema.parse(rawInput);
|
|
72527
|
+
if (this.mediaReclaimState.running) {
|
|
72528
|
+
this.ctx.logger.info("debug-media reclaim already running — request ignored");
|
|
72529
|
+
return {
|
|
72530
|
+
started: false,
|
|
72531
|
+
alreadyRunning: true
|
|
72532
|
+
};
|
|
72533
|
+
}
|
|
72379
72534
|
const mediaStore = this.mediaStore;
|
|
72380
72535
|
const eventStore = this.eventStore;
|
|
72381
72536
|
const trackStore = this.trackStore;
|
|
72382
|
-
|
|
72383
|
-
|
|
72537
|
+
if (!mediaStore || !eventStore || !trackStore) {
|
|
72538
|
+
this.ctx.logger.warn("reclaimDebugMedia: analytics stores unavailable");
|
|
72539
|
+
return {
|
|
72540
|
+
started: false,
|
|
72541
|
+
alreadyRunning: false
|
|
72542
|
+
};
|
|
72543
|
+
}
|
|
72544
|
+
this.mediaReclaimState = {
|
|
72545
|
+
...IDLE_MEDIA_RECLAIM,
|
|
72546
|
+
running: true,
|
|
72384
72547
|
mode: input.mode,
|
|
72385
|
-
|
|
72386
|
-
|
|
72387
|
-
|
|
72388
|
-
|
|
72389
|
-
|
|
72390
|
-
|
|
72391
|
-
|
|
72392
|
-
|
|
72548
|
+
startedAtMs: Date.now()
|
|
72549
|
+
};
|
|
72550
|
+
this.ctx.logger.info("debug-media reclaim starting", { meta: {
|
|
72551
|
+
mode: input.mode,
|
|
72552
|
+
restart: input.restart === true
|
|
72553
|
+
} });
|
|
72554
|
+
this.executeMediaReclaim(input, mediaStore, eventStore, trackStore);
|
|
72555
|
+
return {
|
|
72556
|
+
started: true,
|
|
72557
|
+
alreadyRunning: false
|
|
72393
72558
|
};
|
|
72394
|
-
|
|
72395
|
-
|
|
72396
|
-
|
|
72559
|
+
}
|
|
72560
|
+
/**
|
|
72561
|
+
* The walk itself. Called DETACHED from {@link reclaimDebugMedia}; never
|
|
72562
|
+
* await this from an RPC.
|
|
72563
|
+
*/
|
|
72564
|
+
async executeMediaReclaim(input, mediaStore, eventStore, trackStore) {
|
|
72397
72565
|
try {
|
|
72398
72566
|
const limits = {
|
|
72399
72567
|
mode: input.mode,
|
|
@@ -72411,6 +72579,19 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
72411
72579
|
scopes,
|
|
72412
72580
|
limits,
|
|
72413
72581
|
cursors: input.restart === true ? /* @__PURE__ */ new Map() : this.mediaReclaimCursors,
|
|
72582
|
+
onDevice: (device) => {
|
|
72583
|
+
const s = this.mediaReclaimState;
|
|
72584
|
+
if (!s.running) return;
|
|
72585
|
+
this.mediaReclaimState = {
|
|
72586
|
+
...s,
|
|
72587
|
+
totalExamined: s.totalExamined + device.examined,
|
|
72588
|
+
totalEligible: s.totalEligible + device.eligible,
|
|
72589
|
+
totalReclaimed: s.totalReclaimed + device.reclaimed,
|
|
72590
|
+
totalBytesReclaimed: s.totalBytesReclaimed + device.bytesReclaimed,
|
|
72591
|
+
totalRefused: s.totalRefused + device.refused,
|
|
72592
|
+
devicesDone: s.devicesDone + 1
|
|
72593
|
+
};
|
|
72594
|
+
},
|
|
72414
72595
|
deps: {
|
|
72415
72596
|
page: ({ deviceId, scope, afterMs, limit }) => mediaStore.pageByKindForDevice({
|
|
72416
72597
|
deviceId,
|
|
@@ -72437,15 +72618,39 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
72437
72618
|
bytesReclaimed: report.totalBytesReclaimed,
|
|
72438
72619
|
detail: `debug-media reclaim (${scopes.join("+")}): ${report.totalReclaimed} rows, ${report.totalRefused} refused, complete=${report.complete}`
|
|
72439
72620
|
});
|
|
72440
|
-
|
|
72441
|
-
|
|
72442
|
-
|
|
72443
|
-
|
|
72621
|
+
this.mediaReclaimState = {
|
|
72622
|
+
...this.mediaReclaimState,
|
|
72623
|
+
running: false,
|
|
72624
|
+
totalExamined: report.totalExamined,
|
|
72625
|
+
totalEligible: report.totalEligible,
|
|
72626
|
+
totalReclaimed: report.totalReclaimed,
|
|
72627
|
+
totalBytesReclaimed: report.totalBytesReclaimed,
|
|
72628
|
+
totalRefused: report.totalRefused,
|
|
72629
|
+
complete: report.complete,
|
|
72630
|
+
finishedAtMs: Date.now()
|
|
72444
72631
|
};
|
|
72445
|
-
|
|
72446
|
-
|
|
72632
|
+
this.ctx.logger.info("debug-media reclaim finished", { meta: {
|
|
72633
|
+
mode: report.mode,
|
|
72634
|
+
examined: report.totalExamined,
|
|
72635
|
+
reclaimed: report.totalReclaimed,
|
|
72636
|
+
bytesReclaimed: report.totalBytesReclaimed,
|
|
72637
|
+
complete: report.complete
|
|
72638
|
+
} });
|
|
72639
|
+
} catch (err) {
|
|
72640
|
+
this.mediaReclaimState = {
|
|
72641
|
+
...this.mediaReclaimState,
|
|
72642
|
+
running: false,
|
|
72643
|
+
finishedAtMs: Date.now(),
|
|
72644
|
+
error: err instanceof Error ? err.message : String(err),
|
|
72645
|
+
complete: false
|
|
72646
|
+
};
|
|
72647
|
+
this.ctx.logger.warn("debug-media reclaim failed", { meta: { error: String(err) } });
|
|
72447
72648
|
}
|
|
72448
72649
|
}
|
|
72650
|
+
/** Progress of the detached reclaim — the only way to observe a long pass. */
|
|
72651
|
+
async getMediaReclaimStatus() {
|
|
72652
|
+
return { ...this.mediaReclaimState };
|
|
72653
|
+
}
|
|
72449
72654
|
/** Progress of the detached rebuild — the only way to observe a long pass. */
|
|
72450
72655
|
async getObjectEmbeddingRebuildStatus() {
|
|
72451
72656
|
const state = this.rebuildState;
|
|
@@ -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-rrVQlXbu.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.156",
|
|
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",
|
|
@@ -110,13 +110,6 @@
|
|
|
110
110
|
"cardinality": "multi",
|
|
111
111
|
"description": "Detection snapshots, thumbnails and crops.",
|
|
112
112
|
"defaultsTo": "recordings"
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
"id": "galleryMedia",
|
|
116
|
-
"displayName": "Enrolled Gallery",
|
|
117
|
-
"cardinality": "single",
|
|
118
|
-
"description": "Enrolled face and plate reference crops, and scene reference photographs. Deliberately NOT on the recordings volume: these images survive every retention sweep, so they must also survive a footage wipe, a quota eviction and a recordings disk swap. Small — a few KB per enrolled sample.",
|
|
119
|
-
"defaultRoot": "data"
|
|
120
113
|
}
|
|
121
114
|
],
|
|
122
115
|
"icon": "assets/icon.svg",
|