@camstack/addon-post-analysis 1.2.640 → 1.2.642
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/{clip-model-registry-B1HxbGuP.mjs → clip-model-registry-CoH7ORtC.mjs} +218 -30
- package/dist/{clip-model-registry-C8xdbChV.js → clip-model-registry-Dj3lrvL2.js} +218 -30
- 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-CO5qrMxU.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-Dhan9FH4.mjs} +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-_TUthUZh.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-DIbQ2FJ4.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CuNXxDlT.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit-BN2pgAbz.mjs → hostInit-j1gxJvS7.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +470 -27
- package/dist/pipeline-analytics/index.mjs +470 -27
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CENVyAAQ.mjs +0 -26
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as MediaFileKindEnum, $i as CAP_ADVANCE_FIELDS, $n as cloneAlarmSettings, $r as paintHex, $t as OpsLogEntrySchema, A as DeclaredDevices, Aa as EventCategory, Ai as subKindsOf, An as TrackSideSchema, Ar as isAudioClassName, At as NcAlarmSensorsInputSchema, B as FULL_IMAGE_BBOX, Bi as templateCarriesSummary, Bn as applyModeDelaysEdits, Br as isRootSuppliableStepId, Bt as NcRuleUpdateInputSchema, C as ClearTrackRelationResultSchema, Ca as record, Ci as ruleKindOf, Cn as TimelapseRuleInputSchema, Cr as failureContributionCapability, Ct as NC_SNOOZE_MAX_MINUTES, D as DEFAULT_TIMELAPSE_PREVIEW_TEXT, Da as tuple, Di as servedTemplateVars, Dn as TrackPuritySchema, Dr as isAlertEpisodeOpen, Dt as NC_TEMPLATE_VARS, E as DEFAULT_EVENT_COLOR, Ea as templateLiteral, Ei as sensorRaisedOf, En as TrackPlacementSchema, Er as isAlarmControllableCap, Et as NC_TAXONOMY, F as EVENT_DENSITY_BATCH_MAX, Fi as systemEventDeliveryProblems, Fn as alarmPanelCapability, Fr as isEventOwnerType, Ft as NcRuleActionSchema, G as ListTrackRelationsResultSchema, Gi as zoneAnalyticsCapability, Gn as audioMetricsCapability, Gr as kebabToCamel, Gt as NcSnoozeSuppressedSchema, H as LABEL_SUPPLY_MACRO, Hi as vectorDimFromBase64, Hn as applySecureOnArmEdit, Hr as isSystemDelivery, Ht as NcSharedSequenceSchema, I as EVENT_KIND_BY_CAP, Ii as systemEventFamilyProblems, In as alertEpisodeId, Ir as isKnownTemplateVar, It as NcRuleInputSchema, J as MAX_BIRTH_DECISION_RECORDS, Ji as evaluateSensorEdge, Jn as buildSubjectPath, Jr as memoizePinnedFfmpeg, Jt as NcTaxonomySchema, K as LockStateSchema, Ki as DEFAULT_FIRST_SIGHTING_FRESHNESS_MS, Kn as audioModeOf, Kr as linkedStepOf, Kt as NcStateSummarySectionIdSchema, L as EVENT_OWNER_TYPES, Li as systemEventFilterApplies, Ln as alertEpisodeKind, Lr as isLabelSupplyStepId, Lt as NcRulePatchSchema, M as EMBEDDING_MODEL_FAILED, Mi as subjectPathSvgPoints, Mn as activateAlarmProfile, Mr as isDetectionMacroClass, Mt as NcAlarmSettingsPatchSchema, N as EMBEDDING_REQUIREMENTS_FAILED, Ni as summarizeReview, Nn as addonWidgetsSourceCapability, Nr as isDrawableSubjectPath, Nt as NcConditionDescriptorSchema, O as DETECTION_MACRO_CLASSES, Oa as union, Oi as startMoverUnderClearance, On as TrackRelationKindSchema, Or as isAlertEpisodeSeverity, Ot as NcAlarmConfigSchema, P as EMBEDDING_REQUIREMENTS_UNAVAILABLE, Pi as syncActiveAlarmProfileSettings, Pn as alarmDeviceSecureOnArm, Pr as isEventKindLabelKey, Pt as NcConditionsSchema, Q as MOTION_CLOSE_AFTER_MS, Qi as BaseAddon, Qn as classifyRetrainReview, Qr as occupancyScope, Qt as OPS_LOG_DEFAULT_LIMIT, R as EXPORT_DENSE_MAX_RANGES, Ri as systemEventSummaryScheduleOf, Rn as alertEpisodePayload, Rr as isMediaOwnerType, Rt as NcRuleSchema, S as ClearTrackRelationInputSchema, Sa as partialRecord, Si as ruleGroupingApplies, Sn as TRACK_RELATIONS_DEFAULT_LIMIT, Sr as faceGalleryCapability, St as NC_SENSOR_TRANSITION_KINDS, T as DEFAULT_DETAIL_CROP_CONVENTION, Ta as string, Ti as secureActuationFor, Tn as TimelapseRuleSchema, Tr as gifFromMp4Args, Tt as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, U as LabelAttributionSchema, Ui as viewerLinkBaseOf, Un as armRemovalReconcile, Ur as isSystemEventSummaryRule, Ut as NcSnoozeInputSchema, V as FailureCounters, Vi as templateVarsFor, Vn as applyNonBlockingEdit, Vr as isScheduleActive, Vt as NcScheduleSchema, W as ListTrackRelationsInputSchema, Wi as withSystemEventClasses, Wn as assertTimelapseCadences, Wr as isVectorIndexDimConflict, Wt as NcSnoozeSchema, X as MAX_TRACK_PURITIES, Xi as DeviceType, Xn as bundledCamstack, Xr as normalizeNcMediaPolicy, Xt as NcTemplatePreviewSchema, Y as MAX_BIRTH_LATENCY_PROXY_MS, Yi as isSourceCap, Yn as buildViewerLink, Yr as ncMediaPolicyError, Yt as NcTemplatePreviewInputSchema, Z as MAX_TRACK_RELATIONS, Zi as errMsg, Zn as calendarEventLocalSpan, Zr as notificationRulesCapability, Zt as NcTemplateVarDescriptorSchema, _ as BIRTH_DECISION_DEFAULT_LIMIT, _a as discriminatedUnion, _i as retrainRevisionConflictMessage, _n as SceneMonitorSchema, _t as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, a as CLIP_EMBEDDING_STEP_ID, aa as customAction, ai as plateGalleryCapability, an as RetrainDismissedSchema, ar as deleteAlarmProfile, at as NC_ALARM_NOTIFY_FIELDS, b as BucketAccumulator, ba as number, bi as ruleBelongsToAlarm$1, bn as SetTrackRelationInputSchema, br as eventKindLabel, bt as NC_GROUPING_MAX_SEC, c as readClusterModelPinStrict, ca as hydrateSchema, ci as readMotionTriggeredBy, cn as SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX, cr as deriveRecordingMode, ct as NC_AUDIO_CONFIRM_HITS_DEFAULT, d as APPEARANCE_BLOCKS, di as resolveClipSearchMinScore, dn as SCENE_DEFAULT_CHECK_INTERVAL_SEC, dr as deviceAttentionKind, dt as NC_CLIP_PRE_ROLL_DEFAULT_SEC, ea as CamProfileSchema, ei as paletteResultSchema, en as PackageLlmVerdictSchema, er as consumableFormOf, et as MotionTriggeredBySchema, f as APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT, fa as sleep$1, fi as resolveLocationMode, fn as SCENE_DEFAULT_OBSERVATION_SPACING_SEC, fr as deviceRemovalHookCapability, ft as NC_CLIP_RETENTION_MAX_SEC, g as ArchivedDebugNoteSchema, ga as boolean, gn as SCENE_RESET_RECAPTURES, gt as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, h as AlarmArmModeSchema, ha as array, hi as resolveNcMediaImage, hn as SCENE_DIVERGED, hr as embeddingRefusalOf, ht as NC_CONFIRM_DEFAULT_TIMEOUT_MS, ia as createEvent, ii as pipelineAnalyticsCapability, in as RebuildObjectEmbeddingsRefusalSchema, ir as createAlarmProfile, it as NC_ALARM_DELAY_MAX_SEC, j as DetectionSourceSchema, ji as subjectPathSampleFromBox, jn as TrackSourceSchema, jr as isDefaultAlarmProfileId, jt as NcAlarmSensorsSchema, k as DETECTION_PIPELINE_CAP_NAME, ka as unknown, ki as storageOccupancyCapability, kn as TrackRelationSchema, kr as isAppLinkBase, kt as NcAlarmPostTriggeredActionSchema, l as resolveClusterModelPin, la as isDeviceScopedCap, li as readTimelapseGeneratedAt, ln as SCENE_CONFIRM_DEFAULT_TIMEOUT_MS, lr as describeSystemEventDeliveryProblem, lt as NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT, m as AUDIO_MACRO_LABELS, ma as _enum, mi as resolveModeNotifyPolicy, mn as SCENE_DEFAULT_UNCOVERED_POLICY, mt as NC_CONFIRM_DEFAULT_MAX_IMAGE_PX, na as RECORD_MAX_DATA_BYTES, nn as RECORDING_EXPORT_MAX_READ_BYTES, nr as coreBlockIdFromAddonId, nt as NC_ALARM_DEFAULT_TRIGGERED_DURATION_SEC, o as CLUSTER_MODEL_OWNER_ADDON_ID, oa as defineCustomActions, oi as projectSubjectPath, on as RetrainOriginSchema, or as deriveDetailCropRect, ot as NC_ALARM_SYSTEM_EVENT_KINDS, p as ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT, pa as ZodObject, pi as resolveModeDelays, pn as SCENE_DEFAULT_QUIET_SECONDS, pt as NC_CONDITION_CATALOG, q as MAX_ARCHIVED_DEBUG_NOTES, qi as canonicalHash, qn as buildEventKindDescriptor, qr as mayWriteToLocation, qt as NcSystemEventKindSchema, ra as RECORD_QUERY_MAX, ri as pickFaceRescoreIntervalMinutes, rn as RECORDING_SOURCE_CAMSTACK_ADDON, rr as cosineSimilarity$1, rt as NC_ALARM_DELAY_FIELDS, s as FACE_EMBEDDING_STEP_ID, sa as defineRecordKinds, si as readDeviceStateFrom, sn as RetrainStatusSchema, sr as derivePostTriggeredAction, st as NC_ALARM_TRIGGERED_MAX_SEC, t as BUILTIN_CLIP_MODELS, ta as InvalidRecordError, ti as parseAlertEpisodePayload, tr as consumableLowPercentOf, tt as NC_ALARM_DEFAULT_PROFILE_ID, u as APPEARANCE_BIN_COUNT, ua as nodePin, ui as renameAlarmProfile, un as SCENE_DEFAULT_ANCHOR_THRESHOLD, ur as describeSystemEventFamilyProblem, ut as NC_CLIP_POST_ROLL_DEFAULT_SEC, v as BaseDevice, va as literal, vi as rootSupplyServes, vn as SetTrackPurityInputSchema, vr as encodeVectorBase64, vt as NC_DEFAULT_SNOOZE_MINUTES, w as CoverStateSchema, wa as strictObject, wi as sceneMonitorCapability, wn as TimelapseRulePatchSchema, wr as gifDeriveSpeed, wt as NC_STATE_SUMMARY_SECTION_IDS, x as COCO_TO_MACRO, xa as object, xi as ruleDeviceCompatCapQueries, xn as TIMELAPSE_DENSE_FLOOR_SEC, xr as evictionPolicyOfLocation, xt as NC_GROUPING_MIN_SEC, y as BirthDecisionRecordSchema, ya as looseObject, yi as ruleAutoTriggersAlarm, yn as SetTrackPurityResultSchema, yr as ensureAlarmProfiles, yt as NC_GROUPING_DEFAULT_SEC, z as FIRST_LEVEL_MACRO_CLASSES, zi as systemEventSummarySplitOf, zn as applyBlockingEdit, zr as isRetiredClearKey, zt as NcRuleTargetSchema } from "../clip-model-registry-
|
|
1
|
+
import { $ as MediaFileKindEnum, $i as CAP_ADVANCE_FIELDS, $n as cloneAlarmSettings, $r as paintHex, $t as OpsLogEntrySchema, A as DeclaredDevices, Aa as EventCategory, Ai as subKindsOf, An as TrackSideSchema, Ar as isAudioClassName, At as NcAlarmSensorsInputSchema, B as FULL_IMAGE_BBOX, Bi as templateCarriesSummary, Bn as applyModeDelaysEdits, Br as isRootSuppliableStepId, Bt as NcRuleUpdateInputSchema, C as ClearTrackRelationResultSchema, Ca as record, Ci as ruleKindOf, Cn as TimelapseRuleInputSchema, Cr as failureContributionCapability, Ct as NC_SNOOZE_MAX_MINUTES, D as DEFAULT_TIMELAPSE_PREVIEW_TEXT, Da as tuple, Di as servedTemplateVars, Dn as TrackPuritySchema, Dr as isAlertEpisodeOpen, Dt as NC_TEMPLATE_VARS, E as DEFAULT_EVENT_COLOR, Ea as templateLiteral, Ei as sensorRaisedOf, En as TrackPlacementSchema, Er as isAlarmControllableCap, Et as NC_TAXONOMY, F as EVENT_DENSITY_BATCH_MAX, Fi as systemEventDeliveryProblems, Fn as alarmPanelCapability, Fr as isEventOwnerType, Ft as NcRuleActionSchema, G as ListTrackRelationsResultSchema, Gi as zoneAnalyticsCapability, Gn as audioMetricsCapability, Gr as kebabToCamel, Gt as NcSnoozeSuppressedSchema, H as LABEL_SUPPLY_MACRO, Hi as vectorDimFromBase64, Hn as applySecureOnArmEdit, Hr as isSystemDelivery, Ht as NcSharedSequenceSchema, I as EVENT_KIND_BY_CAP, Ii as systemEventFamilyProblems, In as alertEpisodeId, Ir as isKnownTemplateVar, It as NcRuleInputSchema, J as MAX_BIRTH_DECISION_RECORDS, Ji as evaluateSensorEdge, Jn as buildSubjectPath, Jr as memoizePinnedFfmpeg, Jt as NcTaxonomySchema, K as LockStateSchema, Ki as DEFAULT_FIRST_SIGHTING_FRESHNESS_MS, Kn as audioModeOf, Kr as linkedStepOf, Kt as NcStateSummarySectionIdSchema, L as EVENT_OWNER_TYPES, Li as systemEventFilterApplies, Ln as alertEpisodeKind, Lr as isLabelSupplyStepId, Lt as NcRulePatchSchema, M as EMBEDDING_MODEL_FAILED, Mi as subjectPathSvgPoints, Mn as activateAlarmProfile, Mr as isDetectionMacroClass, Mt as NcAlarmSettingsPatchSchema, N as EMBEDDING_REQUIREMENTS_FAILED, Ni as summarizeReview, Nn as addonWidgetsSourceCapability, Nr as isDrawableSubjectPath, Nt as NcConditionDescriptorSchema, O as DETECTION_MACRO_CLASSES, Oa as union, Oi as startMoverUnderClearance, On as TrackRelationKindSchema, Or as isAlertEpisodeSeverity, Ot as NcAlarmConfigSchema, P as EMBEDDING_REQUIREMENTS_UNAVAILABLE, Pi as syncActiveAlarmProfileSettings, Pn as alarmDeviceSecureOnArm, Pr as isEventKindLabelKey, Pt as NcConditionsSchema, Q as MOTION_CLOSE_AFTER_MS, Qi as BaseAddon, Qn as classifyRetrainReview, Qr as occupancyScope, Qt as OPS_LOG_DEFAULT_LIMIT, R as EXPORT_DENSE_MAX_RANGES, Ri as systemEventSummaryScheduleOf, Rn as alertEpisodePayload, Rr as isMediaOwnerType, Rt as NcRuleSchema, S as ClearTrackRelationInputSchema, Sa as partialRecord, Si as ruleGroupingApplies, Sn as TRACK_RELATIONS_DEFAULT_LIMIT, Sr as faceGalleryCapability, St as NC_SENSOR_TRANSITION_KINDS, T as DEFAULT_DETAIL_CROP_CONVENTION, Ta as string, Ti as secureActuationFor, Tn as TimelapseRuleSchema, Tr as gifFromMp4Args, Tt as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, U as LabelAttributionSchema, Ui as viewerLinkBaseOf, Un as armRemovalReconcile, Ur as isSystemEventSummaryRule, Ut as NcSnoozeInputSchema, V as FailureCounters, Vi as templateVarsFor, Vn as applyNonBlockingEdit, Vr as isScheduleActive, Vt as NcScheduleSchema, W as ListTrackRelationsInputSchema, Wi as withSystemEventClasses, Wn as assertTimelapseCadences, Wr as isVectorIndexDimConflict, Wt as NcSnoozeSchema, X as MAX_TRACK_PURITIES, Xi as DeviceType, Xn as bundledCamstack, Xr as normalizeNcMediaPolicy, Xt as NcTemplatePreviewSchema, Y as MAX_BIRTH_LATENCY_PROXY_MS, Yi as isSourceCap, Yn as buildViewerLink, Yr as ncMediaPolicyError, Yt as NcTemplatePreviewInputSchema, Z as MAX_TRACK_RELATIONS, Zi as errMsg, Zn as calendarEventLocalSpan, Zr as notificationRulesCapability, Zt as NcTemplateVarDescriptorSchema, _ as BIRTH_DECISION_DEFAULT_LIMIT, _a as discriminatedUnion, _i as retrainRevisionConflictMessage, _n as SceneMonitorSchema, _t as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, a as CLIP_EMBEDDING_STEP_ID, aa as customAction, ai as plateGalleryCapability, an as RetrainDismissedSchema, ar as deleteAlarmProfile, at as NC_ALARM_NOTIFY_FIELDS, b as BucketAccumulator, ba as number, bi as ruleBelongsToAlarm$1, bn as SetTrackRelationInputSchema, br as eventKindLabel, bt as NC_GROUPING_MAX_SEC, c as readClusterModelPinStrict, ca as hydrateSchema, ci as readMotionTriggeredBy, cn as SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX, cr as deriveRecordingMode, ct as NC_AUDIO_CONFIRM_HITS_DEFAULT, d as APPEARANCE_BLOCKS, di as resolveClipSearchMinScore, dn as SCENE_DEFAULT_CHECK_INTERVAL_SEC, dr as deviceAttentionKind, dt as NC_CLIP_PRE_ROLL_DEFAULT_SEC, ea as CamProfileSchema, ei as paletteResultSchema, en as PackageLlmVerdictSchema, er as consumableFormOf, et as MotionTriggeredBySchema, f as APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT, fa as sleep$1, fi as resolveLocationMode, fn as SCENE_DEFAULT_OBSERVATION_SPACING_SEC, fr as deviceRemovalHookCapability, ft as NC_CLIP_RETENTION_MAX_SEC, g as ArchivedDebugNoteSchema, ga as boolean, gn as SCENE_RESET_RECAPTURES, gt as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, h as AlarmArmModeSchema, ha as array, hi as resolveNcMediaImage, hn as SCENE_DIVERGED, hr as embeddingRefusalOf, ht as NC_CONFIRM_DEFAULT_TIMEOUT_MS, ia as createEvent, ii as pipelineAnalyticsCapability, in as RebuildObjectEmbeddingsRefusalSchema, ir as createAlarmProfile, it as NC_ALARM_DELAY_MAX_SEC, j as DetectionSourceSchema, ji as subjectPathSampleFromBox, jn as TrackSourceSchema, jr as isDefaultAlarmProfileId, jt as NcAlarmSensorsSchema, k as DETECTION_PIPELINE_CAP_NAME, ka as unknown, ki as storageOccupancyCapability, kn as TrackRelationSchema, kr as isAppLinkBase, kt as NcAlarmPostTriggeredActionSchema, l as resolveClusterModelPin, la as isDeviceScopedCap, li as readTimelapseGeneratedAt, ln as SCENE_CONFIRM_DEFAULT_TIMEOUT_MS, lr as describeSystemEventDeliveryProblem, lt as NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT, m as AUDIO_MACRO_LABELS, ma as _enum, mi as resolveModeNotifyPolicy, mn as SCENE_DEFAULT_UNCOVERED_POLICY, mt as NC_CONFIRM_DEFAULT_MAX_IMAGE_PX, na as RECORD_MAX_DATA_BYTES, nn as RECORDING_EXPORT_MAX_READ_BYTES, nr as coreBlockIdFromAddonId, nt as NC_ALARM_DEFAULT_TRIGGERED_DURATION_SEC, o as CLUSTER_MODEL_OWNER_ADDON_ID, oa as defineCustomActions, oi as projectSubjectPath, on as RetrainOriginSchema, or as deriveDetailCropRect, ot as NC_ALARM_SYSTEM_EVENT_KINDS, p as ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT, pa as ZodObject, pi as resolveModeDelays, pn as SCENE_DEFAULT_QUIET_SECONDS, pt as NC_CONDITION_CATALOG, q as MAX_ARCHIVED_DEBUG_NOTES, qi as canonicalHash, qn as buildEventKindDescriptor, qr as mayWriteToLocation, qt as NcSystemEventKindSchema, ra as RECORD_QUERY_MAX, ri as pickFaceRescoreIntervalMinutes, rn as RECORDING_SOURCE_CAMSTACK_ADDON, rr as cosineSimilarity$1, rt as NC_ALARM_DELAY_FIELDS, s as FACE_EMBEDDING_STEP_ID, sa as defineRecordKinds, si as readDeviceStateFrom, sn as RetrainStatusSchema, sr as derivePostTriggeredAction, st as NC_ALARM_TRIGGERED_MAX_SEC, t as BUILTIN_CLIP_MODELS, ta as InvalidRecordError, ti as parseAlertEpisodePayload, tr as consumableLowPercentOf, tt as NC_ALARM_DEFAULT_PROFILE_ID, u as APPEARANCE_BIN_COUNT, ua as nodePin, ui as renameAlarmProfile, un as SCENE_DEFAULT_ANCHOR_THRESHOLD, ur as describeSystemEventFamilyProblem, ut as NC_CLIP_POST_ROLL_DEFAULT_SEC, v as BaseDevice, va as literal, vi as rootSupplyServes, vn as SetTrackPurityInputSchema, vr as encodeVectorBase64, vt as NC_DEFAULT_SNOOZE_MINUTES, w as CoverStateSchema, wa as strictObject, wi as sceneMonitorCapability, wn as TimelapseRulePatchSchema, wr as gifDeriveSpeed, wt as NC_STATE_SUMMARY_SECTION_IDS, x as COCO_TO_MACRO, xa as object, xi as ruleDeviceCompatCapQueries, xn as TIMELAPSE_DENSE_FLOOR_SEC, xr as evictionPolicyOfLocation, xt as NC_GROUPING_MIN_SEC, y as BirthDecisionRecordSchema, ya as looseObject, yi as ruleAutoTriggersAlarm, yn as SetTrackPurityResultSchema, yr as ensureAlarmProfiles, yt as NC_GROUPING_DEFAULT_SEC, z as FIRST_LEVEL_MACRO_CLASSES, zi as systemEventSummarySplitOf, zn as applyBlockingEdit, zr as isRetiredClearKey, zt as NcRuleTargetSchema } from "../clip-model-registry-CoH7ORtC.mjs";
|
|
2
2
|
import { t as __exportAll } from "../embedding-encoder/index.mjs";
|
|
3
3
|
import * as fs$1 from "node:fs";
|
|
4
4
|
import { createWriteStream, promises } from "node:fs";
|
|
@@ -750,6 +750,8 @@ var DEFAULT_ACTION_TTL_MS = 15 * 6e4;
|
|
|
750
750
|
* notification that is not fresh.
|
|
751
751
|
*/
|
|
752
752
|
var SNOOZE_ACTION_TTL_MS = 60 * 6e4;
|
|
753
|
+
/** How long a Correct / Wrong button stays live: one day. */
|
|
754
|
+
var VERDICT_ACTION_TTL_MS = 1440 * 6e4;
|
|
753
755
|
/**
|
|
754
756
|
* How long a DISARM button stays live: five minutes.
|
|
755
757
|
*
|
|
@@ -777,6 +779,7 @@ function actionTtlMs(subject) {
|
|
|
777
779
|
case "sequence": return DEFAULT_ACTION_TTL_MS;
|
|
778
780
|
case "addon-update": return DEFAULT_ACTION_TTL_MS;
|
|
779
781
|
case "server-update": return DEFAULT_ACTION_TTL_MS;
|
|
782
|
+
case "detection-verdict": return VERDICT_ACTION_TTL_MS;
|
|
780
783
|
}
|
|
781
784
|
}
|
|
782
785
|
var NcActionTokenRegistry = class {
|
|
@@ -820,6 +823,30 @@ var NcActionTokenRegistry = class {
|
|
|
820
823
|
grant: found.grant
|
|
821
824
|
};
|
|
822
825
|
}
|
|
826
|
+
/**
|
|
827
|
+
* Read the grant without spending it. ONLY the confirm page of a link-only
|
|
828
|
+
* channel may call this: it exists so a link preview that opens the URL
|
|
829
|
+
* cannot spend the token, and it never runs anything.
|
|
830
|
+
*/
|
|
831
|
+
peek(id) {
|
|
832
|
+
const found = this.grants.get(id);
|
|
833
|
+
if (found === void 0) return {
|
|
834
|
+
ok: false,
|
|
835
|
+
reason: "unknown"
|
|
836
|
+
};
|
|
837
|
+
if (found.used) return {
|
|
838
|
+
ok: false,
|
|
839
|
+
reason: "already-used"
|
|
840
|
+
};
|
|
841
|
+
if (found.grant.expiresAt <= this.now()) return {
|
|
842
|
+
ok: false,
|
|
843
|
+
reason: "expired"
|
|
844
|
+
};
|
|
845
|
+
return {
|
|
846
|
+
ok: true,
|
|
847
|
+
grant: found.grant
|
|
848
|
+
};
|
|
849
|
+
}
|
|
823
850
|
/** Drop records that can no longer be claimed. Memory hygiene only — an
|
|
824
851
|
* expired record is already refused by {@link claim}, so a sweep that never
|
|
825
852
|
* ran would cost bytes, never safety. */
|
|
@@ -861,6 +888,7 @@ function describeGrant(grant) {
|
|
|
861
888
|
return grant.action === "disarm" ? "Alarm disarmed" : `Alarm armed${grant.mode !== void 0 ? ` (${grant.mode})` : ""}`;
|
|
862
889
|
case "addon-update": return `${grant.packages.length} addon update(s) on ${grant.nodeId}`;
|
|
863
890
|
case "server-update": return `Server update on ${grant.nodeId}`;
|
|
891
|
+
case "detection-verdict": return grant.verdict === "correct" ? "Marked correct — kept for retraining" : grant.suggestedClass === void 0 ? "Marked wrong — kept for retraining" : `Marked wrong, it was a ${grant.suggestedClass} — kept for retraining`;
|
|
864
892
|
}
|
|
865
893
|
}
|
|
866
894
|
/** The audit line's `meta`, per family. A snooze that logged a `sequence` it
|
|
@@ -914,6 +942,15 @@ function grantMeta(grant) {
|
|
|
914
942
|
...common,
|
|
915
943
|
nodeId: grant.nodeId
|
|
916
944
|
};
|
|
945
|
+
case "detection-verdict": return {
|
|
946
|
+
...common,
|
|
947
|
+
ruleId: grant.ruleId,
|
|
948
|
+
rule: grant.ruleName,
|
|
949
|
+
userId: grant.userId,
|
|
950
|
+
trackId: grant.trackId,
|
|
951
|
+
verdict: grant.verdict,
|
|
952
|
+
suggestedClass: grant.suggestedClass ?? null
|
|
953
|
+
};
|
|
917
954
|
}
|
|
918
955
|
}
|
|
919
956
|
/** What the human standing in front of the page is told. */
|
|
@@ -922,6 +959,11 @@ var FAILURE_COPY = {
|
|
|
922
959
|
expired: "This button has expired. Open CamStack to do it there.",
|
|
923
960
|
"already-used": "Already done — this button had been used."
|
|
924
961
|
};
|
|
962
|
+
/** The question a link-only channel lands on, answered by a POST form. */
|
|
963
|
+
function confirmPage(url, grant) {
|
|
964
|
+
const action = `${url.pathname}?exp=${encodeURIComponent(url.searchParams.get("exp") ?? "")}&sig=${encodeURIComponent(url.searchParams.get("sig") ?? "")}&confirm=1`;
|
|
965
|
+
return `<!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Confirm</title><style>body{font:16px/1.5 system-ui,sans-serif;margin:0;min-height:100vh;display:grid;place-items:center;background:#111;color:#eee}main{max-width:24rem;padding:2rem;text-align:center}h1{font-size:1.25rem;margin:0 0 1rem}button{font:inherit;padding:.6rem 1.4rem;border-radius:.5rem;border:0;background:#3b82f6;color:#fff}</style><main><h1>Confirm</h1><p>${grant.verdict === "correct" ? "Mark this detection as correct?" : grant.suggestedClass === void 0 ? "Mark this detection as wrong?" : `Mark this detection as wrong, it was a ${grant.suggestedClass}?`}</p><form method="post" action="${action}"><button type="submit">Confirm</button></form></main>`;
|
|
966
|
+
}
|
|
925
967
|
function page(title, detail) {
|
|
926
968
|
return `<!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${title}</title><style>body{font:16px/1.5 system-ui,sans-serif;margin:0;min-height:100vh;display:grid;place-items:center;background:#111;color:#eee}main{max-width:24rem;padding:2rem;text-align:center}h1{font-size:1.25rem;margin:0 0 .5rem}p{margin:0;color:#aaa}</style><main><h1>${title}</h1><p>${detail}</p></main>`;
|
|
927
969
|
}
|
|
@@ -935,7 +977,8 @@ var NcActionPlane = class {
|
|
|
935
977
|
handler = async (req, res) => {
|
|
936
978
|
const url = new URL(req.url ?? "/", "http://placeholder");
|
|
937
979
|
const id = decodeURIComponent(url.pathname.split("/").filter(Boolean).pop() ?? "");
|
|
938
|
-
const
|
|
980
|
+
const posted = (req.method ?? "GET").toUpperCase() === "POST";
|
|
981
|
+
const background = posted && url.searchParams.get("confirm") !== "1";
|
|
939
982
|
if (!verifyActionSignature({
|
|
940
983
|
secret: this.deps.secret,
|
|
941
984
|
id,
|
|
@@ -947,6 +990,13 @@ var NcActionPlane = class {
|
|
|
947
990
|
this.reply(res, background, 404, "Not found", "This link is not valid.");
|
|
948
991
|
return;
|
|
949
992
|
}
|
|
993
|
+
if (!posted) {
|
|
994
|
+
const peeked = this.deps.registry.peek(id);
|
|
995
|
+
if (peeked.ok && peeked.grant.kind === "detection-verdict" && peeked.grant.viaLink === true) {
|
|
996
|
+
this.sendBody(res, 200, "text/html; charset=utf-8", confirmPage(url, peeked.grant));
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
950
1000
|
const claim = this.deps.registry.claim(id);
|
|
951
1001
|
if (!claim.ok) {
|
|
952
1002
|
this.deps.logger.info("nc action callback not claimable", { meta: {
|
|
@@ -1092,6 +1142,7 @@ var ncDurableRecordKinds = defineRecordKinds({
|
|
|
1092
1142
|
"state-summary-stamps": ledger("digest delivery stamps per rule kind"),
|
|
1093
1143
|
meta: ledger("outbox boot-reconcile watermark and other single-key meta rows", "settings", { none: "one fixed key (watermark), overwritten in place, never deleted" }),
|
|
1094
1144
|
snoozes: ledger("operator opens or closes a snooze window (write-through)"),
|
|
1145
|
+
"detection-verdicts": ledger("operator taps Correct or Wrong on a detection notification; one row per track"),
|
|
1095
1146
|
"snooze-suppressed": {
|
|
1096
1147
|
...QUERIED,
|
|
1097
1148
|
class: "ledger",
|
|
@@ -9226,6 +9277,9 @@ var en_default = {
|
|
|
9226
9277
|
"action.addon-update.uptodate": "Already up to date on {{nodeId}} — nothing to do",
|
|
9227
9278
|
"action.addon-update.unknown": "Could not read what needs updating on {{nodeId}} — nothing was changed. Open CamStack.",
|
|
9228
9279
|
"button.server-update.apply": "Update server on {{nodeId}}",
|
|
9280
|
+
"button.verdict.correct": "Correct",
|
|
9281
|
+
"button.verdict.wrong": "Wrong",
|
|
9282
|
+
"button.verdict.wrongAs": "Wrong: it is {{class}}",
|
|
9229
9283
|
"action.server-update.title": "Server update",
|
|
9230
9284
|
"action.server-update.started": "Updating server on {{nodeId}}…",
|
|
9231
9285
|
"action.server-update.restarting": "Server update staged on {{nodeId}} — the node is restarting. You will hear when it comes back.",
|
|
@@ -9379,7 +9433,7 @@ var it_default = {
|
|
|
9379
9433
|
"occupancy.op.free": "libera",
|
|
9380
9434
|
"system.device-online.title": "Di nuovo online — {{deviceName}}",
|
|
9381
9435
|
"system.device-online.body": "Il dispositivo {{deviceName}} è di nuovo online",
|
|
9382
|
-
"system.device-offline.title": "
|
|
9436
|
+
"system.device-offline.title": "Non raggiungibile — {{deviceName}}",
|
|
9383
9437
|
"system.device-offline.body": "Il dispositivo {{deviceName}} è offline",
|
|
9384
9438
|
"system.device-disabled.title": "Spento — {{deviceName}}",
|
|
9385
9439
|
"system.device-disabled.body": "Il dispositivo {{deviceName}} è stato spento",
|
|
@@ -9395,7 +9449,7 @@ var it_default = {
|
|
|
9395
9449
|
"system.stream-online.title": "Stream di nuovo online — {{deviceName}}",
|
|
9396
9450
|
"system.stream-online.body": "Lo stream della camera {{deviceName}} è di nuovo online",
|
|
9397
9451
|
"system.stream-online.body.profiled": "Lo stream {{profile}} della camera {{deviceName}} è di nuovo online",
|
|
9398
|
-
"system.stream-offline.title": "
|
|
9452
|
+
"system.stream-offline.title": "Flusso interrotto — {{deviceName}}",
|
|
9399
9453
|
"system.stream-offline.body": "Lo stream della camera {{deviceName}} è offline",
|
|
9400
9454
|
"system.stream-offline.body.profiled": "Lo stream {{profile}} della camera {{deviceName}} è offline",
|
|
9401
9455
|
"system.node-online.title": "Nodo di nuovo online — {{nodeName}}",
|
|
@@ -9426,7 +9480,7 @@ var it_default = {
|
|
|
9426
9480
|
"system.storage-location-low.body": "La location {{locationName}} è al {{freePercent}}% libero ({{reason}})",
|
|
9427
9481
|
"system.storage-location-critical.title": "Storage critico — {{locationName}}",
|
|
9428
9482
|
"system.storage-location-critical.body": "La location {{locationName}} è critica: {{reason}}",
|
|
9429
|
-
"system.storage-location-ok.title": "
|
|
9483
|
+
"system.storage-location-ok.title": "Archivio a posto — {{locationName}}",
|
|
9430
9484
|
"system.storage-location-ok.body": "La location {{locationName}} ha recuperato spazio libero",
|
|
9431
9485
|
"system.detection-blind.title": "Senza detection — {{deviceName}}",
|
|
9432
9486
|
"system.detection-blind.body": {
|
|
@@ -9507,8 +9561,8 @@ var it_default = {
|
|
|
9507
9561
|
"system.camera-disabled.body": "Il dispositivo {{deviceName}} è stato spento",
|
|
9508
9562
|
"system.camera-enabled.title": "Dispositivo riacceso",
|
|
9509
9563
|
"system.camera-enabled.body": "Il dispositivo {{deviceName}} è stato riacceso",
|
|
9510
|
-
"test.prefix": "[
|
|
9511
|
-
"timelapse.title": "
|
|
9564
|
+
"test.prefix": "[PROVA] {{body}}",
|
|
9565
|
+
"timelapse.title": "Sequenza — {{camera}}",
|
|
9512
9566
|
"timelapse.body.window": "{{rule}} — {{from}}→{{to}}",
|
|
9513
9567
|
"timelapse.body.tooManyEvents": "troppi eventi per il campionamento denso: video a cadenza uniforme",
|
|
9514
9568
|
"timelapse.body.denseRanges": {
|
|
@@ -9627,6 +9681,9 @@ var it_default = {
|
|
|
9627
9681
|
"action.addon-update.uptodate": "Già aggiornato su {{nodeId}} — niente da fare",
|
|
9628
9682
|
"action.addon-update.unknown": "Non è stato possibile leggere cosa aggiornare su {{nodeId}} — non è stato cambiato nulla. Apri CamStack.",
|
|
9629
9683
|
"button.server-update.apply": "Aggiorna server su {{nodeId}}",
|
|
9684
|
+
"button.verdict.correct": "Corretto",
|
|
9685
|
+
"button.verdict.wrong": "Sbagliato",
|
|
9686
|
+
"button.verdict.wrongAs": "Sbagliato: è {{class}}",
|
|
9630
9687
|
"action.server-update.title": "Aggiornamento server",
|
|
9631
9688
|
"action.server-update.started": "Aggiornamento server su {{nodeId}}…",
|
|
9632
9689
|
"action.server-update.restarting": "Aggiornamento server predisposto su {{nodeId}} — il nodo si riavvia. Ti avviseremo quando torna.",
|
|
@@ -9670,7 +9727,7 @@ var it_default = {
|
|
|
9670
9727
|
"summary.line.node-inference-unavailable": "{{nodeId}} — non può eseguire l'inferenza (da {{since}})",
|
|
9671
9728
|
"summary.line.addon-crash-loop": "{{nodeId}} — {{addons}} fermati dopo crash ripetuti (da {{since}})",
|
|
9672
9729
|
"summary.line.update": "{{packageName}} {{currentVersion}} → {{latestVersion}}",
|
|
9673
|
-
"summary.line.wrapper-update": "
|
|
9730
|
+
"summary.line.wrapper-update": "Contenitore {{currentVersion}} → {{latestVersion}}",
|
|
9674
9731
|
"summary.line.consumable.part": "{{item}} {{percentage}}%",
|
|
9675
9732
|
"summary.line.consumable.part.unmeasured": "{{item}} da sostituire",
|
|
9676
9733
|
"summary.line.consumable.tank": "{{item}} quasi vuota",
|
|
@@ -59702,6 +59759,25 @@ var TrackAlreadyTrainedError = class extends Error {
|
|
|
59702
59759
|
}
|
|
59703
59760
|
};
|
|
59704
59761
|
/**
|
|
59762
|
+
* Classify a failed `setTrackFlags` as one of the lifecycle's DELIBERATE
|
|
59763
|
+
* refusals, or `null` when it is a failure of the call itself.
|
|
59764
|
+
*
|
|
59765
|
+
* Needed because the caller that matters (a notification button) reaches the
|
|
59766
|
+
* method over `ctx.api`, where the error class does not survive the hop and
|
|
59767
|
+
* only the message does. The class check comes first; the message check is the
|
|
59768
|
+
* transported form, and `track-flags.spec.ts` pins it against the real
|
|
59769
|
+
* messages so a reworded refusal cannot silently turn into a generic failure.
|
|
59770
|
+
*/
|
|
59771
|
+
function retrainRefusalReason(err) {
|
|
59772
|
+
if (err instanceof TrackAlreadyTrainedError) return "trained";
|
|
59773
|
+
if (err instanceof StagingBudgetExceededError) return "budget";
|
|
59774
|
+
const message = err instanceof Error ? err.message : "";
|
|
59775
|
+
if (message.includes("is already trained")) return "trained";
|
|
59776
|
+
if (message.includes("in retrain staging")) return "budget";
|
|
59777
|
+
if (message.includes("unknown track")) return "unknown-track";
|
|
59778
|
+
return null;
|
|
59779
|
+
}
|
|
59780
|
+
/**
|
|
59705
59781
|
* The `debugNote` half of the patch — `{}` when nothing should be written.
|
|
59706
59782
|
*
|
|
59707
59783
|
* The note is not an independent column: it is the operator's sentence about
|
|
@@ -81843,6 +81919,10 @@ var NC_DEVICE_ROW_KINDS = [
|
|
|
81843
81919
|
{
|
|
81844
81920
|
key: "nc-artifacts",
|
|
81845
81921
|
kind: ncDurableRecordKinds.artifacts
|
|
81922
|
+
},
|
|
81923
|
+
{
|
|
81924
|
+
key: "nc-detection-verdicts",
|
|
81925
|
+
kind: ncDurableRecordKinds["detection-verdicts"]
|
|
81846
81926
|
}
|
|
81847
81927
|
];
|
|
81848
81928
|
/**
|
|
@@ -82262,6 +82342,82 @@ var NcSequenceStore = class NcSequenceStore {
|
|
|
82262
82342
|
}
|
|
82263
82343
|
};
|
|
82264
82344
|
//#endregion
|
|
82345
|
+
//#region src/notification-center/detection-verdict-ledger.ts
|
|
82346
|
+
/**
|
|
82347
|
+
* @durable class=ledger owner=notification-center
|
|
82348
|
+
* write="the operator taps Correct / Wrong on a detection notification; one row per track, the latest answer replaces the earlier one"
|
|
82349
|
+
* retention="none — bounded by the tracks a rule asked about; deleted with the camera by the device-removal hook"
|
|
82350
|
+
*/
|
|
82351
|
+
var NC_DETECTION_VERDICTS_COLLECTION = "notification-center:detection-verdicts";
|
|
82352
|
+
var NC_DETECTION_VERDICTS_COLUMNS = [
|
|
82353
|
+
{
|
|
82354
|
+
name: "id",
|
|
82355
|
+
type: "TEXT",
|
|
82356
|
+
primaryKey: true,
|
|
82357
|
+
notNull: true
|
|
82358
|
+
},
|
|
82359
|
+
{
|
|
82360
|
+
name: "ruleId",
|
|
82361
|
+
type: "TEXT",
|
|
82362
|
+
notNull: true
|
|
82363
|
+
},
|
|
82364
|
+
{
|
|
82365
|
+
name: "at",
|
|
82366
|
+
type: "INTEGER",
|
|
82367
|
+
notNull: true
|
|
82368
|
+
},
|
|
82369
|
+
(
|
|
82370
|
+
/** The FULL row (Zod-validated on read). */
|
|
82371
|
+
{
|
|
82372
|
+
name: "row",
|
|
82373
|
+
type: "JSON",
|
|
82374
|
+
notNull: true
|
|
82375
|
+
})
|
|
82376
|
+
];
|
|
82377
|
+
var NcDetectionVerdictRecordSchema = object({
|
|
82378
|
+
trackId: string().min(1),
|
|
82379
|
+
deviceId: number().int(),
|
|
82380
|
+
verdict: _enum(["correct", "wrong"]),
|
|
82381
|
+
/** The class the operator said it really was. Only with `wrong`. */
|
|
82382
|
+
suggestedClass: string().min(1).optional(),
|
|
82383
|
+
/** Whose notification it was (the token's frozen owner), NOT who tapped. */
|
|
82384
|
+
userId: string().min(1),
|
|
82385
|
+
ruleId: string().min(1),
|
|
82386
|
+
ruleName: string(),
|
|
82387
|
+
at: number()
|
|
82388
|
+
});
|
|
82389
|
+
var NC_DETECTION_VERDICTS_SPEC = {
|
|
82390
|
+
collection: NC_DETECTION_VERDICTS_COLLECTION,
|
|
82391
|
+
columns: [...NC_DETECTION_VERDICTS_COLUMNS],
|
|
82392
|
+
writeMode: "write-through",
|
|
82393
|
+
keyOf: (row) => row.trackId,
|
|
82394
|
+
toValue: (row) => ({
|
|
82395
|
+
ruleId: row.ruleId,
|
|
82396
|
+
at: row.at,
|
|
82397
|
+
row
|
|
82398
|
+
}),
|
|
82399
|
+
fromRecord: (_key, data) => {
|
|
82400
|
+
const parsed = NcDetectionVerdictRecordSchema.safeParse(data["row"]);
|
|
82401
|
+
return parsed.success ? parsed.data : null;
|
|
82402
|
+
},
|
|
82403
|
+
deviceIdOf: (row) => row.deviceId,
|
|
82404
|
+
loadLimit: 1e4
|
|
82405
|
+
};
|
|
82406
|
+
var NcDetectionVerdictLedger = class {
|
|
82407
|
+
ledger;
|
|
82408
|
+
constructor(deps) {
|
|
82409
|
+
this.ledger = new DurableLedger({
|
|
82410
|
+
spec: NC_DETECTION_VERDICTS_SPEC,
|
|
82411
|
+
kind: deps.records.kind(ncDurableRecordKinds["detection-verdicts"]),
|
|
82412
|
+
logger: deps.logger
|
|
82413
|
+
});
|
|
82414
|
+
}
|
|
82415
|
+
/** Write-through; throws on a failed write so the tap reports it. */
|
|
82416
|
+
async record(row) {
|
|
82417
|
+
await this.ledger.put(NcDetectionVerdictRecordSchema.parse(row));
|
|
82418
|
+
}
|
|
82419
|
+
};
|
|
82420
|
+
//#endregion
|
|
82265
82421
|
//#region src/notification-center/device-prune.ts
|
|
82266
82422
|
function judgeList(list, isDead) {
|
|
82267
82423
|
const dead = list.filter(isDead);
|
|
@@ -83216,6 +83372,64 @@ var NcDeviceStateCache = class {
|
|
|
83216
83372
|
}
|
|
83217
83373
|
};
|
|
83218
83374
|
//#endregion
|
|
83375
|
+
//#region src/notification-center/detection-verdict-buttons.ts
|
|
83376
|
+
var VERDICT_CORRECT_ID = "detection-verdict-correct";
|
|
83377
|
+
var VERDICT_WRONG_ID = "detection-verdict-wrong";
|
|
83378
|
+
/** `detection-verdict-wrong:<class>` — Wrong, and what it really was. */
|
|
83379
|
+
var VERDICT_WRONG_AS_PREFIX = "detection-verdict-wrong:";
|
|
83380
|
+
/** The macro classes offered as "it was really…". Short on purpose. */
|
|
83381
|
+
var VERDICT_CLASS_CHOICES = [
|
|
83382
|
+
"person",
|
|
83383
|
+
"vehicle",
|
|
83384
|
+
"animal"
|
|
83385
|
+
];
|
|
83386
|
+
/** At most this many class buttons, and only when the channel has room. */
|
|
83387
|
+
var VERDICT_MAX_CLASS_CHOICES = 2;
|
|
83388
|
+
function verdictButtonRefusal(subject) {
|
|
83389
|
+
if (!subject.askVerdict) return "rule-off";
|
|
83390
|
+
if (subject.trackId === void 0) return "no-track";
|
|
83391
|
+
if (subject.ownerUserId === void 0) return "unowned-target";
|
|
83392
|
+
return null;
|
|
83393
|
+
}
|
|
83394
|
+
/** The classes worth offering for a subject of `className`. */
|
|
83395
|
+
function verdictClassChoices(className) {
|
|
83396
|
+
return VERDICT_CLASS_CHOICES.filter((c) => c !== className).slice(0, VERDICT_MAX_CLASS_CHOICES);
|
|
83397
|
+
}
|
|
83398
|
+
/**
|
|
83399
|
+
* Which buttons a channel with `maxActions` slots gets. `maxActions` absent
|
|
83400
|
+
* (the kind catalog did not answer) is treated as the common floor of three:
|
|
83401
|
+
* the two plain choices, never the class ones. The class choices appear only
|
|
83402
|
+
* when the channel declared room for a fourth button.
|
|
83403
|
+
*/
|
|
83404
|
+
function verdictButtonSpecs(input) {
|
|
83405
|
+
const base = [{
|
|
83406
|
+
actionId: VERDICT_CORRECT_ID,
|
|
83407
|
+
verdict: "correct"
|
|
83408
|
+
}, {
|
|
83409
|
+
actionId: VERDICT_WRONG_ID,
|
|
83410
|
+
verdict: "wrong"
|
|
83411
|
+
}];
|
|
83412
|
+
if (input.maxActions === void 0 || input.maxActions <= 3) return base;
|
|
83413
|
+
const room = input.maxActions - base.length;
|
|
83414
|
+
const extra = verdictClassChoices(input.className).slice(0, room).map((suggestedClass) => ({
|
|
83415
|
+
actionId: `${VERDICT_WRONG_AS_PREFIX}${suggestedClass}`,
|
|
83416
|
+
verdict: "wrong",
|
|
83417
|
+
suggestedClass
|
|
83418
|
+
}));
|
|
83419
|
+
return [...base, ...extra];
|
|
83420
|
+
}
|
|
83421
|
+
/** Is this one of our buttons (so a link-only channel can lift it into the body)? */
|
|
83422
|
+
function isVerdictAction(action) {
|
|
83423
|
+
return action.id === VERDICT_CORRECT_ID || action.id.startsWith(VERDICT_WRONG_ID);
|
|
83424
|
+
}
|
|
83425
|
+
/**
|
|
83426
|
+
* The body lines that carry the verdict choices on a channel with no buttons.
|
|
83427
|
+
* A bare URL per line: every chat and mail client linkifies it.
|
|
83428
|
+
*/
|
|
83429
|
+
function verdictLinkLines(actions) {
|
|
83430
|
+
return actions.filter(isVerdictAction).map((a) => `${a.label}: ${a.url ?? ""}`).join("\n");
|
|
83431
|
+
}
|
|
83432
|
+
//#endregion
|
|
83219
83433
|
//#region src/notification-center/state-summary/state-model.ts
|
|
83220
83434
|
/**
|
|
83221
83435
|
* The STATE SUMMARY's vocabulary (D647): its sections, and what one reported
|
|
@@ -83937,6 +84151,53 @@ function withTimeout$3(promise, ms) {
|
|
|
83937
84151
|
}
|
|
83938
84152
|
//#endregion
|
|
83939
84153
|
//#region src/notification-center/summary/mosaic-renderer.ts
|
|
84154
|
+
/**
|
|
84155
|
+
* The mosaic: N stored best-shots laid out as one image.
|
|
84156
|
+
*
|
|
84157
|
+
* ## The mosaic is ALWAYS CLEAN
|
|
84158
|
+
*
|
|
84159
|
+
* No detection boxes. Not "boxes off by default" — the media ladder in
|
|
84160
|
+
* `summary-selection.ts` refuses `fullFrameBoxed` outright and nothing here
|
|
84161
|
+
* draws a rectangle. The operator asked for it in those words, and there are
|
|
84162
|
+
* two further reasons worth writing down so nobody adds them back as a
|
|
84163
|
+
* convenience:
|
|
84164
|
+
*
|
|
84165
|
+
* - a burnt-in box is permanent. A tile is a durable artefact and a mosaic is
|
|
84166
|
+
* a thing an operator re-reads days later; a box drawn from a detection that
|
|
84167
|
+
* was wrong cannot be un-drawn;
|
|
84168
|
+
* - for P2 it is an attack surface. Anything rendered INTO the pixels is text
|
|
84169
|
+
* the model reads as scene content, and D121 is this repo's record of a
|
|
84170
|
+
* model treating in-frame text as instruction. Boxes stay DATA — the
|
|
84171
|
+
* artefact can carry them, the picture never does.
|
|
84172
|
+
*
|
|
84173
|
+
* ## …and by default it carries NO TEXT EITHER
|
|
84174
|
+
*
|
|
84175
|
+
* {@link mosaicTextPlan} is the ONE place that decides whether a word is drawn,
|
|
84176
|
+
* and with no `captions` on the rule the answer is "none": tile labels off,
|
|
84177
|
+
* title band off. Same argument as the boxes, one step further — the operator
|
|
84178
|
+
* asked for a wall of pictures, a burnt-in caption cannot be un-drawn, and the
|
|
84179
|
+
* label strip covers 14% of every tile with the part of the frame nearest the
|
|
84180
|
+
* ground, which is where feet, wheels and parcels are.
|
|
84181
|
+
*
|
|
84182
|
+
* Nothing is lost by it. The AI pass never sees this image:
|
|
84183
|
+
* `summary-producer.ts#analyse` hands the model the RAW per-track JPEGs and
|
|
84184
|
+
* `summary-ai.ts#buildSummaryAiPrompt` supplies camera name, class and time as
|
|
84185
|
+
* PROMPT TEXT. The tile rectangles stay exported (see {@link mosaicLayout}) so
|
|
84186
|
+
* a viewer can still say what a tile is without a word being burnt into it.
|
|
84187
|
+
*
|
|
84188
|
+
* ## sharp, and never ffmpeg `drawtext`
|
|
84189
|
+
*
|
|
84190
|
+
* Labels, WHEN ASKED FOR, are composited as SVG through sharp. D118 forbids `drawtext` because
|
|
84191
|
+
* the hub carries two ffmpeg builds that disagree about libfreetype, so a
|
|
84192
|
+
* caption would appear or vanish depending on which node rendered it. sharp is
|
|
84193
|
+
* a direct dependency of this addon and its text path is librsvg, which is the
|
|
84194
|
+
* same one the timelapse preview already uses.
|
|
84195
|
+
*
|
|
84196
|
+
* The geometry helpers are pure and exported: {@link mosaicGrid} and
|
|
84197
|
+
* {@link mosaicLayout} answer the tile rectangles WITHOUT touching a pixel,
|
|
84198
|
+
* which is what lets the layout be tested and what a future clickable-tile
|
|
84199
|
+
* viewer will read.
|
|
84200
|
+
*/
|
|
83940
84201
|
/** Widest mosaic produced. 1920 keeps a 3×3 grid at 640 px a tile — readable
|
|
83941
84202
|
* on a phone, and a ~200 KB JPEG that every notifier accepts. */
|
|
83942
84203
|
var MOSAIC_MAX_WIDTH = 1920;
|
|
@@ -84130,6 +84391,13 @@ function fitLine(text, usablePx, basePx, minPx) {
|
|
|
84130
84391
|
fontPx
|
|
84131
84392
|
};
|
|
84132
84393
|
}
|
|
84394
|
+
/**
|
|
84395
|
+
* The words drawn on a summary tile: camera, clock, and the class vocabulary
|
|
84396
|
+
* in the hub language. A class the vocabulary does not know stays the wire id.
|
|
84397
|
+
*/
|
|
84398
|
+
function summaryTileCaption(camera, clock, className, locale) {
|
|
84399
|
+
return `${camera} · ${clock} · ${eventKindLabel(className, locale)}`;
|
|
84400
|
+
}
|
|
84133
84401
|
/** The label strip drawn across the bottom of one tile. */
|
|
84134
84402
|
function tileLabelSvg(width, height, label) {
|
|
84135
84403
|
const bandHeight = Math.max(18, Math.round(height * LABEL_BAND_FRACTION));
|
|
@@ -85204,8 +85472,9 @@ function selectTriggerZoneCandidates(input) {
|
|
|
85204
85472
|
*
|
|
85205
85473
|
* - `scene-boxes` — the whole scene; the subject outlined THICK in the accent
|
|
85206
85474
|
* colour, every OTHER track visible at that instant outlined THIN, each
|
|
85207
|
-
* captioned with class
|
|
85208
|
-
* reads on any background. Boxes are the tracks'
|
|
85475
|
+
* captioned with the class vocabulary in the hub language ("Cane 87%") on a
|
|
85476
|
+
* dark plate that reads on any background. Boxes are the tracks' trail
|
|
85477
|
+
* boxes AT
|
|
85209
85478
|
* the source frame's timestamp — never the track envelope — scaled to the
|
|
85210
85479
|
* JPEG.
|
|
85211
85480
|
* - `crop` — the subject cut out of the clean frame around its box at that
|
|
@@ -85226,10 +85495,11 @@ function selectTriggerZoneCandidates(input) {
|
|
|
85226
85495
|
* ## Once per record
|
|
85227
85496
|
*
|
|
85228
85497
|
* N targets and every outbox retry resolve the same row; the render is
|
|
85229
|
-
* memoised per record + subject + mode + zone flag + camera
|
|
85230
|
-
* {@link NC_IMAGE_MEMO_TTL_MS}, so a 4K decode happens once, not once per
|
|
85231
|
-
* phone.
|
|
85232
|
-
*
|
|
85498
|
+
* memoised per record + subject + mode + zone flag + camera + hub language
|
|
85499
|
+
* for {@link NC_IMAGE_MEMO_TTL_MS}, so a 4K decode happens once, not once per
|
|
85500
|
+
* phone. The hub has one notification language, so every recipient of a row
|
|
85501
|
+
* shares that picture. It runs in the process that hosts the Notification
|
|
85502
|
+
* Center (the pipeline-analytics runner), which already carries `sharp`.
|
|
85233
85503
|
*/
|
|
85234
85504
|
/** How long a rendered picture is reused for the same record (N targets, retries). */
|
|
85235
85505
|
var NC_IMAGE_MEMO_TTL_MS = 15 * 6e4;
|
|
@@ -85261,12 +85531,37 @@ var SAME_SUBJECT_IOU = .8;
|
|
|
85261
85531
|
*/
|
|
85262
85532
|
var NC_IMAGE_SAME_CLASS_SUBJECT_IOU = .5;
|
|
85263
85533
|
var JPEG_QUALITY = 85;
|
|
85264
|
-
/** The caption of a box:
|
|
85534
|
+
/** The caption of a box: a name and confidence — "Cane 87%". The name is already chosen. */
|
|
85265
85535
|
function imageLabelText(name, confidence) {
|
|
85266
85536
|
if (confidence === void 0 || !Number.isFinite(confidence)) return name;
|
|
85267
85537
|
const pct = Math.round(Math.min(1, Math.max(0, confidence)) * 100);
|
|
85268
85538
|
return `${name} ${String(pct)}%`;
|
|
85269
85539
|
}
|
|
85540
|
+
/**
|
|
85541
|
+
* The name drawn on a box, in the hub language.
|
|
85542
|
+
*
|
|
85543
|
+
* A class id — the coarse class, or a finer one riding `label` — is
|
|
85544
|
+
* {@link eventKindLabel}. An identity or a plate stays the word the
|
|
85545
|
+
* recogniser produced. A class the vocabulary does not know stays the wire
|
|
85546
|
+
* id, which is what `eventKindLabel` already returns.
|
|
85547
|
+
*/
|
|
85548
|
+
function imageBoxName(input, locale) {
|
|
85549
|
+
if (input.labelKind === "identity" || input.labelKind === "plate") {
|
|
85550
|
+
const written = input.label?.trim();
|
|
85551
|
+
if (written !== void 0 && written.length > 0) return written;
|
|
85552
|
+
}
|
|
85553
|
+
const label = input.label?.trim();
|
|
85554
|
+
if (label !== void 0 && label.length > 0) {
|
|
85555
|
+
const key = label.toLowerCase();
|
|
85556
|
+
if (isEventKindLabelKey(key)) return eventKindLabel(key, locale);
|
|
85557
|
+
if (key !== input.className.trim().toLowerCase()) return label;
|
|
85558
|
+
}
|
|
85559
|
+
return eventKindLabel(input.className, locale);
|
|
85560
|
+
}
|
|
85561
|
+
/** Class vocabulary in `locale`, plus confidence — the string the overlay draws. */
|
|
85562
|
+
function imageBoxCaption(input, locale) {
|
|
85563
|
+
return imageLabelText(imageBoxName(input, locale), input.confidence);
|
|
85564
|
+
}
|
|
85270
85565
|
/** Stroke widths for an output of this size: the subject's is the thick one. */
|
|
85271
85566
|
function overlayStrokes(width, height) {
|
|
85272
85567
|
const subject = Math.max(4, Math.round(Math.min(width, height) / 110));
|
|
@@ -85565,8 +85860,8 @@ var NcNotificationImageStage = class {
|
|
|
85565
85860
|
}
|
|
85566
85861
|
/**
|
|
85567
85862
|
* The picture for this row from this source, rendered once per record +
|
|
85568
|
-
* subject + mode + zone flag + camera and reused for every
|
|
85569
|
-
* Null when the row ships no still.
|
|
85863
|
+
* subject + mode + zone flag + camera + language and reused for every
|
|
85864
|
+
* target and retry. Null when the row ships no still.
|
|
85570
85865
|
*/
|
|
85571
85866
|
async notificationImage(input) {
|
|
85572
85867
|
const payload = input.entry.payload;
|
|
@@ -85574,13 +85869,15 @@ var NcNotificationImageStage = class {
|
|
|
85574
85869
|
if (mode === "none") return null;
|
|
85575
85870
|
const zoneIds = input.withSubject ? payloadZoneOverlayIds(payload) : [];
|
|
85576
85871
|
const withPath = input.withSubject && payloadShowsSubjectPath(payload);
|
|
85872
|
+
const locale = input.locale ?? "en";
|
|
85577
85873
|
const key = [
|
|
85578
85874
|
input.entry.recordId,
|
|
85579
85875
|
payload.subject.trackId ?? "",
|
|
85580
85876
|
mode,
|
|
85581
85877
|
zoneIds.length > 0 ? "z" : "-",
|
|
85582
85878
|
withPath ? "p" : "-",
|
|
85583
|
-
String(input.camera)
|
|
85879
|
+
String(input.camera),
|
|
85880
|
+
locale
|
|
85584
85881
|
].join("|");
|
|
85585
85882
|
const now = this.deps.now();
|
|
85586
85883
|
const hit = this.memo.get(key);
|
|
@@ -85618,9 +85915,15 @@ var NcNotificationImageStage = class {
|
|
|
85618
85915
|
const trail = hasTrackedSubject(subjectTrackId) ? await this.subjectTrail(camera, subjectTrackId) : [];
|
|
85619
85916
|
const subjectBox = trailAt(trail, source.timestamp, NC_IMAGE_BOX_MATCH_MAX_MS)?.bbox ?? null;
|
|
85620
85917
|
const path = withPath ? this.subjectPath(input, trail) : [];
|
|
85918
|
+
const locale = input.locale ?? "en";
|
|
85621
85919
|
const subject = subjectBox !== null ? {
|
|
85622
85920
|
...subjectBox,
|
|
85623
|
-
label:
|
|
85921
|
+
label: imageBoxCaption({
|
|
85922
|
+
className: payload.subject.className,
|
|
85923
|
+
...payload.subject.label !== void 0 ? { label: payload.subject.label } : {},
|
|
85924
|
+
...payload.subject.labelKind !== void 0 ? { labelKind: payload.subject.labelKind } : {},
|
|
85925
|
+
...payload.subject.confidence !== void 0 ? { confidence: payload.subject.confidence } : {}
|
|
85926
|
+
}, locale),
|
|
85624
85927
|
className: payload.subject.className
|
|
85625
85928
|
} : null;
|
|
85626
85929
|
const othersDrawn = mode === "scene-boxes" ? otherTracks(await this.tracksAt(camera, source.timestamp), subjectTrackId, subjectBox, payload.subject.className) : [];
|
|
@@ -85635,7 +85938,11 @@ var NcNotificationImageStage = class {
|
|
|
85635
85938
|
});
|
|
85636
85939
|
const others = mode === "scene-boxes" ? othersDrawn.map((t) => ({
|
|
85637
85940
|
...t.bbox,
|
|
85638
|
-
label:
|
|
85941
|
+
label: imageBoxCaption({
|
|
85942
|
+
className: t.className,
|
|
85943
|
+
...t.label !== void 0 ? { label: t.label } : {},
|
|
85944
|
+
...t.confidence !== void 0 ? { confidence: t.confidence } : {}
|
|
85945
|
+
}, locale),
|
|
85639
85946
|
className: t.className,
|
|
85640
85947
|
...t.confidence !== void 0 ? { confidence: t.confidence } : {}
|
|
85641
85948
|
})) : [];
|
|
@@ -86831,6 +87138,8 @@ var NcDispatcher = class {
|
|
|
86831
87138
|
targetId: target.id,
|
|
86832
87139
|
...target.ownerUserId !== void 0 ? { ownerUserId: target.ownerUserId } : {},
|
|
86833
87140
|
...entry.payload.subject.className !== void 0 ? { className: entry.payload.subject.className } : {},
|
|
87141
|
+
...entry.payload.subject.trackId !== void 0 ? { trackId: entry.payload.subject.trackId } : {},
|
|
87142
|
+
...target.maxActions !== void 0 ? { maxActions: target.maxActions } : {},
|
|
86834
87143
|
...entry.payload.subject.systemEvent !== void 0 ? { systemEvent: entry.payload.subject.systemEvent } : {}
|
|
86835
87144
|
});
|
|
86836
87145
|
} catch (err) {
|
|
@@ -86926,9 +87235,13 @@ var NcDispatcher = class {
|
|
|
86926
87235
|
...summarySectionOf(systemEvent.data)
|
|
86927
87236
|
} } : {}
|
|
86928
87237
|
}) : null;
|
|
86929
|
-
const
|
|
87238
|
+
const minted = await this.mintActions(entry, target);
|
|
87239
|
+
const linkOnly = target.maxActions === 0;
|
|
87240
|
+
const verdictLinks = linkOnly ? verdictLinkLines(minted) : "";
|
|
87241
|
+
const actions = linkOnly ? minted.filter((a) => !isVerdictAction(a)) : minted;
|
|
87242
|
+
const withLinks = (text) => verdictLinks === "" ? text : `${text}\n\n${verdictLinks}`;
|
|
86930
87243
|
const notification = {
|
|
86931
|
-
body,
|
|
87244
|
+
body: withLinks(body),
|
|
86932
87245
|
title,
|
|
86933
87246
|
priority: clampPriority(paramNumber(entry.payload.params, "priority") ?? entry.payload.priority),
|
|
86934
87247
|
...params,
|
|
@@ -86950,7 +87263,7 @@ var NcDispatcher = class {
|
|
|
86950
87263
|
grouped: resolved.grouped,
|
|
86951
87264
|
withAiText: (aiText) => ({
|
|
86952
87265
|
...notification,
|
|
86953
|
-
body: renderBody(aiText)
|
|
87266
|
+
body: withLinks(renderBody(aiText))
|
|
86954
87267
|
})
|
|
86955
87268
|
};
|
|
86956
87269
|
}
|
|
@@ -87145,7 +87458,8 @@ var NcDispatcher = class {
|
|
|
87145
87458
|
annotated: isAnnotatedSceneKind(file.kind)
|
|
87146
87459
|
},
|
|
87147
87460
|
camera: cameraId,
|
|
87148
|
-
withSubject: false
|
|
87461
|
+
withSubject: false,
|
|
87462
|
+
locale: this.texts.language
|
|
87149
87463
|
});
|
|
87150
87464
|
if (image === null) continue;
|
|
87151
87465
|
out.push({
|
|
@@ -87934,7 +88248,8 @@ var NcDispatcher = class {
|
|
|
87934
88248
|
annotated: isAnnotatedSceneKind(file.kind)
|
|
87935
88249
|
},
|
|
87936
88250
|
camera: primaryImageCamera(entry.payload),
|
|
87937
|
-
withSubject: true
|
|
88251
|
+
withSubject: true,
|
|
88252
|
+
locale: this.texts.language
|
|
87938
88253
|
});
|
|
87939
88254
|
if (image === null) return null;
|
|
87940
88255
|
return {
|
|
@@ -94654,7 +94969,7 @@ var NcSummaryProducer = class {
|
|
|
94654
94969
|
purpose: "summary",
|
|
94655
94970
|
tiles: tiles.map(({ candidate, jpeg }) => ({
|
|
94656
94971
|
jpeg,
|
|
94657
|
-
...plan.tileLabels ? { label:
|
|
94972
|
+
...plan.tileLabels ? { label: summaryTileCaption(names.get(candidate.deviceId) ?? String(candidate.deviceId), clockOf$2(candidate.firstSeen), candidate.className, this.texts.language) } : {},
|
|
94658
94973
|
deviceId: candidate.deviceId,
|
|
94659
94974
|
sourceId: candidate.trackId
|
|
94660
94975
|
})),
|
|
@@ -99561,6 +99876,55 @@ async function mintButtons(host, input) {
|
|
|
99561
99876
|
const announcement = stored === null && input.ruleId === NC_ALARM_ANNOUNCE_RULE_ID;
|
|
99562
99877
|
if (stored === null && !announcement) return [];
|
|
99563
99878
|
const drafts = [];
|
|
99879
|
+
const verdictRefusal = verdictButtonRefusal({
|
|
99880
|
+
askVerdict: stored?.askVerdict === true,
|
|
99881
|
+
...input.trackId !== void 0 ? { trackId: input.trackId } : {},
|
|
99882
|
+
...input.ownerUserId !== void 0 ? { ownerUserId: input.ownerUserId } : {}
|
|
99883
|
+
});
|
|
99884
|
+
if (stored?.askVerdict === true && verdictRefusal !== null) host.logger.info("detection notification carries no verdict buttons", {
|
|
99885
|
+
tags: { deviceId: input.deviceId },
|
|
99886
|
+
meta: {
|
|
99887
|
+
ruleId: input.ruleId,
|
|
99888
|
+
reason: verdictRefusal,
|
|
99889
|
+
targetId: input.targetId
|
|
99890
|
+
}
|
|
99891
|
+
});
|
|
99892
|
+
const verdictTrackId = input.trackId;
|
|
99893
|
+
const verdictOwner = input.ownerUserId;
|
|
99894
|
+
const verdictButtons = verdictRefusal !== null || verdictTrackId === void 0 || verdictOwner === void 0 ? [] : verdictButtonSpecs({
|
|
99895
|
+
className: input.className,
|
|
99896
|
+
maxActions: input.maxActions
|
|
99897
|
+
}).map((spec) => {
|
|
99898
|
+
const id = randomUUID();
|
|
99899
|
+
drafts.push({
|
|
99900
|
+
id,
|
|
99901
|
+
ttlMs: actionTtlMs({ kind: "detection-verdict" }),
|
|
99902
|
+
grant: (expiresAt) => ({
|
|
99903
|
+
kind: "detection-verdict",
|
|
99904
|
+
id,
|
|
99905
|
+
verdict: spec.verdict,
|
|
99906
|
+
trackId: verdictTrackId,
|
|
99907
|
+
ruleId: input.ruleId,
|
|
99908
|
+
ruleName: input.ruleName,
|
|
99909
|
+
userId: verdictOwner,
|
|
99910
|
+
...spec.suggestedClass !== void 0 ? { suggestedClass: spec.suggestedClass } : {},
|
|
99911
|
+
...input.maxActions === 0 ? { viaLink: true } : {},
|
|
99912
|
+
deviceId: input.deviceId,
|
|
99913
|
+
actionId: spec.actionId,
|
|
99914
|
+
targetId: input.targetId,
|
|
99915
|
+
expiresAt
|
|
99916
|
+
})
|
|
99917
|
+
});
|
|
99918
|
+
return {
|
|
99919
|
+
id: spec.actionId,
|
|
99920
|
+
label: spec.suggestedClass === void 0 ? host.texts.text({ key: spec.verdict === "correct" ? "button.verdict.correct" : "button.verdict.wrong" }) : host.texts.text({
|
|
99921
|
+
key: "button.verdict.wrongAs",
|
|
99922
|
+
vars: { class: spec.suggestedClass }
|
|
99923
|
+
}),
|
|
99924
|
+
url: id,
|
|
99925
|
+
mode: "background"
|
|
99926
|
+
};
|
|
99927
|
+
});
|
|
99564
99928
|
const sequenceButtons = host.actionRunner === null || stored === null ? [] : buildActionButtons({
|
|
99565
99929
|
actions: stored.actions,
|
|
99566
99930
|
knownSequenceIds: host.sequences.knownIds(),
|
|
@@ -99773,6 +100137,7 @@ async function mintButtons(host, input) {
|
|
|
99773
100137
|
}
|
|
99774
100138
|
});
|
|
99775
100139
|
const out = [
|
|
100140
|
+
...verdictButtons,
|
|
99776
100141
|
...sequenceButtons,
|
|
99777
100142
|
...snoozeButtons,
|
|
99778
100143
|
...alarmButtons,
|
|
@@ -99996,7 +100361,52 @@ async function runGrantedAction(host, grant) {
|
|
|
99996
100361
|
case "sequence": return host.runGrantedSequence(grant);
|
|
99997
100362
|
case "addon-update": return host.applyGrantedAddonUpdate(grant);
|
|
99998
100363
|
case "server-update": return host.applyGrantedServerUpdate(grant);
|
|
100364
|
+
case "detection-verdict": return host.applyGrantedDetectionVerdict(grant);
|
|
100365
|
+
}
|
|
100366
|
+
}
|
|
100367
|
+
/** What a person is told when the retrain lifecycle refuses the mark. */
|
|
100368
|
+
var VERDICT_REFUSAL_COPY = {
|
|
100369
|
+
trained: "This track was already used for training — nothing was changed",
|
|
100370
|
+
budget: "This camera already holds as many tracks for retraining as it may — nothing was changed",
|
|
100371
|
+
"unknown-track": "This track is no longer available — nothing was changed"
|
|
100372
|
+
};
|
|
100373
|
+
/**
|
|
100374
|
+
* Mark the track one notification reported for retraining, and record who was
|
|
100375
|
+
* asked and when.
|
|
100376
|
+
*
|
|
100377
|
+
* Order matters. The MARK goes first and is the authority: a refusal ("already
|
|
100378
|
+
* trained", "budget full") means the operator's answer changed nothing, so no
|
|
100379
|
+
* ledger row is written for an answer that was not applied. The token is
|
|
100380
|
+
* already spent (D47) — the refusal is shown as the reason, not a retry.
|
|
100381
|
+
*/
|
|
100382
|
+
async function applyGrantedDetectionVerdict(host, grant) {
|
|
100383
|
+
const mark = host.deps.markTrackForRetrain;
|
|
100384
|
+
if (mark === void 0) throw new Error("no retrain mark is wired on this node");
|
|
100385
|
+
const outcome = await mark({
|
|
100386
|
+
deviceId: grant.deviceId,
|
|
100387
|
+
trackId: grant.trackId
|
|
100388
|
+
});
|
|
100389
|
+
if (!outcome.ok) {
|
|
100390
|
+
host.logger.info("detection verdict changed nothing — the retrain flow refused the mark", {
|
|
100391
|
+
tags: { deviceId: grant.deviceId },
|
|
100392
|
+
meta: {
|
|
100393
|
+
trackId: grant.trackId,
|
|
100394
|
+
reason: outcome.reason,
|
|
100395
|
+
verdict: grant.verdict
|
|
100396
|
+
}
|
|
100397
|
+
});
|
|
100398
|
+
throw new NcActionRefusedError(VERDICT_REFUSAL_COPY[outcome.reason]);
|
|
99999
100399
|
}
|
|
100400
|
+
await host.verdicts.record({
|
|
100401
|
+
trackId: grant.trackId,
|
|
100402
|
+
deviceId: grant.deviceId,
|
|
100403
|
+
verdict: grant.verdict,
|
|
100404
|
+
...grant.suggestedClass !== void 0 ? { suggestedClass: grant.suggestedClass } : {},
|
|
100405
|
+
userId: grant.userId,
|
|
100406
|
+
ruleId: grant.ruleId,
|
|
100407
|
+
ruleName: grant.ruleName,
|
|
100408
|
+
at: host.now()
|
|
100409
|
+
});
|
|
100000
100410
|
}
|
|
100001
100411
|
/**
|
|
100002
100412
|
* Apply the addon updates one notification was about.
|
|
@@ -105174,6 +105584,7 @@ var NotificationCenter = class {
|
|
|
105174
105584
|
* evaluating one.
|
|
105175
105585
|
*/
|
|
105176
105586
|
pruneLedger;
|
|
105587
|
+
verdicts;
|
|
105177
105588
|
/** D905: which rules cannot work, announced once per distinct reason. */
|
|
105178
105589
|
ruleHealth = new NcRuleHealthMonitor();
|
|
105179
105590
|
/** The prune itself — see `device-prune-runner.ts`. */
|
|
@@ -105402,6 +105813,11 @@ var NotificationCenter = class {
|
|
|
105402
105813
|
applyGrantedArmAnyway: (panel, grant) => self.applyGrantedArmAnyway(panel, grant),
|
|
105403
105814
|
applyGrantedBypassInput: (panel, grant) => self.applyGrantedBypassInput(panel, grant),
|
|
105404
105815
|
applyGrantedServerUpdate: (grant) => self.applyGrantedServerUpdate(grant),
|
|
105816
|
+
applyGrantedDetectionVerdict: (grant) => self.applyGrantedDetectionVerdict(grant),
|
|
105817
|
+
now: self.now,
|
|
105818
|
+
get verdicts() {
|
|
105819
|
+
return self.verdicts;
|
|
105820
|
+
},
|
|
105405
105821
|
applyGrantedSnooze: (grant) => self.applyGrantedSnooze(grant),
|
|
105406
105822
|
get deps() {
|
|
105407
105823
|
return self.deps;
|
|
@@ -105452,6 +105868,9 @@ var NotificationCenter = class {
|
|
|
105452
105868
|
async applyGrantedAddonUpdate(grant) {
|
|
105453
105869
|
return applyGrantedAddonUpdate(this.grantedActionsHost(), grant);
|
|
105454
105870
|
}
|
|
105871
|
+
async applyGrantedDetectionVerdict(grant) {
|
|
105872
|
+
return applyGrantedDetectionVerdict(this.grantedActionsHost(), grant);
|
|
105873
|
+
}
|
|
105455
105874
|
async applyGrantedServerUpdate(grant) {
|
|
105456
105875
|
return applyGrantedServerUpdate(this.grantedActionsHost(), grant);
|
|
105457
105876
|
}
|
|
@@ -105691,6 +106110,10 @@ var NotificationCenter = class {
|
|
|
105691
106110
|
}
|
|
105692
106111
|
this.summaryRules = NcSummaryStore.of(deps.records, this.logger.child("summary"), deps.now);
|
|
105693
106112
|
this.calendarEventsRules = NcCalendarEventsStore.of(deps.records, this.logger.child("calendar-events"), deps.now);
|
|
106113
|
+
this.verdicts = new NcDetectionVerdictLedger({
|
|
106114
|
+
...durableStore,
|
|
106115
|
+
logger: this.logger.child("detection-verdicts")
|
|
106116
|
+
});
|
|
105694
106117
|
this.pruneLedger = new NcPruneLedger({
|
|
105695
106118
|
...durableStore,
|
|
105696
106119
|
logger: this.logger.child("device-prune")
|
|
@@ -108298,6 +108721,23 @@ var BuildNotificationCenter = class extends BuildNotificationCenterBase {
|
|
|
108298
108721
|
};
|
|
108299
108722
|
}
|
|
108300
108723
|
},
|
|
108724
|
+
markTrackForRetrain: async ({ deviceId, trackId }) => {
|
|
108725
|
+
try {
|
|
108726
|
+
await this.ctx.api.pipelineAnalytics.setTrackFlags.mutate({
|
|
108727
|
+
deviceId,
|
|
108728
|
+
trackId,
|
|
108729
|
+
flags: { markForTrain: true }
|
|
108730
|
+
});
|
|
108731
|
+
return { ok: true };
|
|
108732
|
+
} catch (err) {
|
|
108733
|
+
const reason = retrainRefusalReason(err);
|
|
108734
|
+
if (reason === null) throw err;
|
|
108735
|
+
return {
|
|
108736
|
+
ok: false,
|
|
108737
|
+
reason
|
|
108738
|
+
};
|
|
108739
|
+
}
|
|
108740
|
+
},
|
|
108301
108741
|
readDeviceStates: async (ids) => {
|
|
108302
108742
|
const out = /* @__PURE__ */ new Map();
|
|
108303
108743
|
for (const id of ids) try {
|
|
@@ -108369,9 +108809,11 @@ var BuildNotificationCenter = class extends BuildNotificationCenterBase {
|
|
|
108369
108809
|
const targets = await api.notificationOutput.listTargets.query({});
|
|
108370
108810
|
const kinds = await api.notificationOutput.listTargetKinds.query({}).catch(() => []);
|
|
108371
108811
|
const bodyMaxLenByKind = new Map(kinds.map((k) => [k.kind, k.caps.bodyMaxLen]));
|
|
108812
|
+
const maxActionsByKind = new Map(kinds.map((k) => [k.kind, k.caps.actions]));
|
|
108372
108813
|
return targets.map((t) => {
|
|
108373
108814
|
const owner = t.config["ownerUserId"];
|
|
108374
108815
|
const bodyMaxLen = bodyMaxLenByKind.get(t.kind);
|
|
108816
|
+
const maxActions = maxActionsByKind.get(t.kind);
|
|
108375
108817
|
return {
|
|
108376
108818
|
id: t.id,
|
|
108377
108819
|
addonId: t.addonId,
|
|
@@ -108379,7 +108821,8 @@ var BuildNotificationCenter = class extends BuildNotificationCenterBase {
|
|
|
108379
108821
|
kind: t.kind,
|
|
108380
108822
|
enabled: t.enabled,
|
|
108381
108823
|
...typeof owner === "string" && owner.length > 0 ? { ownerUserId: owner } : {},
|
|
108382
|
-
...typeof bodyMaxLen === "number" ? { bodyMaxLen } : {}
|
|
108824
|
+
...typeof bodyMaxLen === "number" ? { bodyMaxLen } : {},
|
|
108825
|
+
...typeof maxActions === "number" ? { maxActions } : {}
|
|
108383
108826
|
};
|
|
108384
108827
|
});
|
|
108385
108828
|
},
|