@camstack/addon-smtp-nodemailer 1.2.46 → 1.2.48
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/smtp.addon.js +290 -4
- package/dist/smtp.addon.mjs +290 -4
- package/package.json +1 -1
package/dist/smtp.addon.js
CHANGED
|
@@ -8039,6 +8039,20 @@ var RelocateJobSchema = object({
|
|
|
8039
8039
|
* in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
|
|
8040
8040
|
*/
|
|
8041
8041
|
rowsReconciled: number().int().nonnegative().optional(),
|
|
8042
|
+
/**
|
|
8043
|
+
* Rows this run FORGOT because the file they name is not on disk.
|
|
8044
|
+
*
|
|
8045
|
+
* The mover derived the path from the row's own fields and `stat`ed it; an
|
|
8046
|
+
* ENOENT there is a per-path confirmation that the segment is gone (D296),
|
|
8047
|
+
* and the durable row is dropped through the same channel eviction uses. It
|
|
8048
|
+
* is reported for the same reason `rowsReconciled` is: this is a durable
|
|
8049
|
+
* mutation nobody asked for, and a migration that quietly erases hour rows is
|
|
8050
|
+
* the same failure as one that quietly skips them (D295).
|
|
8051
|
+
*
|
|
8052
|
+
* The production drain of 2026-08-30 would have reported 11 074 here — the
|
|
8053
|
+
* ledger claimed 5.65 GB of footage that no longer existed.
|
|
8054
|
+
*/
|
|
8055
|
+
rowsForgotten: number().int().nonnegative().optional(),
|
|
8042
8056
|
startedAt: number(),
|
|
8043
8057
|
finishedAt: number().nullable(),
|
|
8044
8058
|
error: string().nullable()
|
|
@@ -8402,6 +8416,91 @@ var RelocateResidueSchema = object({
|
|
|
8402
8416
|
segments: number().int().nonnegative(),
|
|
8403
8417
|
bytes: number().int().nonnegative()
|
|
8404
8418
|
}).nullable();
|
|
8419
|
+
/**
|
|
8420
|
+
* Ask one location whether its durable hour rows describe the disk — the walk
|
|
8421
|
+
* (D319).
|
|
8422
|
+
*
|
|
8423
|
+
* `apply` DEFAULTS TO FALSE and that default is the product: the operator's
|
|
8424
|
+
* missing tool is the question, and the dry run is how they sanity-check the
|
|
8425
|
+
* destructive run before authorising it.
|
|
8426
|
+
*/
|
|
8427
|
+
var LedgerWalkInputSchema = object({
|
|
8428
|
+
locationId: string().min(1),
|
|
8429
|
+
/** Forget the confirmed-absent rows, rather than only counting them. */
|
|
8430
|
+
apply: boolean().optional(),
|
|
8431
|
+
/** Narrow to one camera. */
|
|
8432
|
+
deviceId: number().int().positive().optional(),
|
|
8433
|
+
/** Narrow to these recording profiles; empty/absent = every profile. */
|
|
8434
|
+
profiles: array(string().min(1)).optional()
|
|
8435
|
+
});
|
|
8436
|
+
/** Why a whole walk did nothing. Every one leaves the ledger untouched. */
|
|
8437
|
+
var LedgerWalkRefusalSchema = _enum([
|
|
8438
|
+
"location-unknown",
|
|
8439
|
+
"source-writable",
|
|
8440
|
+
"no-ledger",
|
|
8441
|
+
"archive-unreadable",
|
|
8442
|
+
"anchor-absent",
|
|
8443
|
+
"anchor-unreadable",
|
|
8444
|
+
"anchor-moved"
|
|
8445
|
+
]);
|
|
8446
|
+
_enum([
|
|
8447
|
+
"live-tail",
|
|
8448
|
+
"listing-error",
|
|
8449
|
+
"path-mismatch",
|
|
8450
|
+
"durable-refused"
|
|
8451
|
+
]);
|
|
8452
|
+
/** Every skip reason, always present, always a number — so a reason that never
|
|
8453
|
+
* fired reports as zero rather than absent and the report shape is constant
|
|
8454
|
+
* between passes. Spelled out rather than `z.record` for exactly that. */
|
|
8455
|
+
var LedgerWalkSkipCountsSchema = object({
|
|
8456
|
+
"live-tail": number().int().nonnegative(),
|
|
8457
|
+
"listing-error": number().int().nonnegative(),
|
|
8458
|
+
"path-mismatch": number().int().nonnegative(),
|
|
8459
|
+
"durable-refused": number().int().nonnegative()
|
|
8460
|
+
});
|
|
8461
|
+
/** One camera's share of a walk, so a report names cameras and not rows. */
|
|
8462
|
+
var LedgerWalkDeviceReportSchema = object({
|
|
8463
|
+
deviceId: number().int(),
|
|
8464
|
+
hoursWalked: number().int().nonnegative(),
|
|
8465
|
+
hoursMissing: number().int().nonnegative(),
|
|
8466
|
+
ghostSegments: number().int().nonnegative(),
|
|
8467
|
+
ghostBytes: number().int().nonnegative(),
|
|
8468
|
+
forgottenSegments: number().int().nonnegative(),
|
|
8469
|
+
orphanFiles: number().int().nonnegative()
|
|
8470
|
+
});
|
|
8471
|
+
/**
|
|
8472
|
+
* What one walk claimed, listed, found and (only when armed) forgot.
|
|
8473
|
+
*
|
|
8474
|
+
* `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
|
|
8475
|
+
* walk that saw a fraction of the location is visible in its own report rather
|
|
8476
|
+
* than in the absence of one.
|
|
8477
|
+
*/
|
|
8478
|
+
var LedgerWalkReportSchema = object({
|
|
8479
|
+
locationId: string(),
|
|
8480
|
+
applied: boolean(),
|
|
8481
|
+
refused: LedgerWalkRefusalSchema.nullable(),
|
|
8482
|
+
archiveSegments: number().int().nonnegative().nullable(),
|
|
8483
|
+
archiveBytes: number().int().nonnegative().nullable(),
|
|
8484
|
+
hoursClaimed: number().int().nonnegative(),
|
|
8485
|
+
hoursWalked: number().int().nonnegative(),
|
|
8486
|
+
hoursMissing: number().int().nonnegative(),
|
|
8487
|
+
/** `readdir` calls issued — the cost, stated in the unit that is paid. */
|
|
8488
|
+
listings: number().int().nonnegative(),
|
|
8489
|
+
segmentsClaimed: number().int().nonnegative(),
|
|
8490
|
+
ghostSegments: number().int().nonnegative(),
|
|
8491
|
+
ghostBytes: number().int().nonnegative(),
|
|
8492
|
+
ghostHoursWhole: number().int().nonnegative(),
|
|
8493
|
+
forgottenSegments: number().int().nonnegative(),
|
|
8494
|
+
forgottenBytes: number().int().nonnegative(),
|
|
8495
|
+
/** Files under a claimed hour that no durable row names. Never deleted. */
|
|
8496
|
+
orphanFiles: number().int().nonnegative(),
|
|
8497
|
+
orphanSample: array(string()).readonly(),
|
|
8498
|
+
hoursSkipped: number().int().nonnegative(),
|
|
8499
|
+
skippedByReason: LedgerWalkSkipCountsSchema,
|
|
8500
|
+
/** The walk stopped at its per-pass hour bound with claims unwalked. */
|
|
8501
|
+
bounded: boolean(),
|
|
8502
|
+
byDevice: array(LedgerWalkDeviceReportSchema).readonly()
|
|
8503
|
+
});
|
|
8405
8504
|
/** How many rows a media pass would still act on against a given target — the
|
|
8406
8505
|
* media lane's denominator AND its residue, from ONE derivation so the two can
|
|
8407
8506
|
* never disagree. `null` = the count could not be taken. */
|
|
@@ -18675,13 +18774,15 @@ var ListGroupsPageSchema = object({
|
|
|
18675
18774
|
groups: array(AnalyticsGroupRecordSchema).readonly(),
|
|
18676
18775
|
nextCursor: string().nullable()
|
|
18677
18776
|
});
|
|
18777
|
+
var KEY_EVENTS_DEFAULT_LIMIT = 50;
|
|
18778
|
+
var KEY_EVENTS_MAX_LIMIT = 200;
|
|
18678
18779
|
var KeyEventQueryInput = object({
|
|
18679
18780
|
deviceId: number(),
|
|
18680
18781
|
/** Window lower bound (track firstSeen ≥ since). */
|
|
18681
18782
|
since: number(),
|
|
18682
18783
|
/** Window upper bound (track firstSeen ≤ until). */
|
|
18683
18784
|
until: number(),
|
|
18684
|
-
limit: number().int().min(1).max(
|
|
18785
|
+
limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
|
|
18685
18786
|
/** Drop tracks scoring below this importance. */
|
|
18686
18787
|
minImportance: number().min(0).max(1).optional(),
|
|
18687
18788
|
/** Restrict to a single class (e.g. 'person'). */
|
|
@@ -18703,6 +18804,32 @@ var KeyEventSchema = object({
|
|
|
18703
18804
|
...TrackFlagFields,
|
|
18704
18805
|
...TrackRetrainFields
|
|
18705
18806
|
});
|
|
18807
|
+
/** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
|
|
18808
|
+
var KeyEventBatchQueryInput = object({
|
|
18809
|
+
deviceIds: array(number()).min(1).max(200),
|
|
18810
|
+
since: number(),
|
|
18811
|
+
until: number(),
|
|
18812
|
+
/** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
|
|
18813
|
+
* across the set, which would let a busy camera starve a quiet one of its
|
|
18814
|
+
* rows and change what the merged feed contains. */
|
|
18815
|
+
limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
|
|
18816
|
+
minImportance: number().min(0).max(1).optional(),
|
|
18817
|
+
classFilter: string().optional()
|
|
18818
|
+
});
|
|
18819
|
+
/**
|
|
18820
|
+
* One camera's key events in a batch answer.
|
|
18821
|
+
*
|
|
18822
|
+
* The row exists for every requested id. `getKeyEvents` degrades to `[]` on
|
|
18823
|
+
* error rather than throwing, so a camera whose store read failed and one with
|
|
18824
|
+
* no events in the window were ALREADY indistinguishable per camera — the
|
|
18825
|
+
* batch does not make that worse, and the row keeps the deviceId the single
|
|
18826
|
+
* method's output never carried (the caller used to stamp it from the fan-out
|
|
18827
|
+
* key, which only worked because there was one query per camera).
|
|
18828
|
+
*/
|
|
18829
|
+
var KeyEventsForDeviceSchema = object({
|
|
18830
|
+
deviceId: number(),
|
|
18831
|
+
events: array(KeyEventSchema).readonly()
|
|
18832
|
+
});
|
|
18706
18833
|
object({
|
|
18707
18834
|
trackId: string(),
|
|
18708
18835
|
className: string(),
|
|
@@ -18774,6 +18901,50 @@ var EventStoreFootprintSchema = object({
|
|
|
18774
18901
|
totalBytes: number().int(),
|
|
18775
18902
|
devices: array(EventStoreDeviceFootprintSchema).readonly()
|
|
18776
18903
|
});
|
|
18904
|
+
/** Event-media footprint for one {@link MediaFileKind}. */
|
|
18905
|
+
var EventMediaKindFootprintSchema = object({
|
|
18906
|
+
kind: MediaFileKindEnum,
|
|
18907
|
+
/** Media rows of this kind. */
|
|
18908
|
+
rows: number().int(),
|
|
18909
|
+
/** Bytes on disk held by those rows. */
|
|
18910
|
+
bytes: number().int()
|
|
18911
|
+
});
|
|
18912
|
+
/**
|
|
18913
|
+
* The media footprint broken down by KIND — the axis a deletion decision
|
|
18914
|
+
* actually turns on.
|
|
18915
|
+
*
|
|
18916
|
+
* A byte total says how much there is; it cannot say what is safe to remove.
|
|
18917
|
+
* The deletable set (the periodic `snapshot` filmstrip, the surplus per-edge
|
|
18918
|
+
* motion stills) and the keep set (`firstFrame`, rolling `lastFrame`,
|
|
18919
|
+
* `thumbnail`/`thumbnailSmall`, `keyFrame`/`keyFrameSmall`, the face/plate
|
|
18920
|
+
* buffers, gallery media, the CLIP `crop`) are distinguished by `kind` and by
|
|
18921
|
+
* nothing else, so sizing a deletion means summing per kind.
|
|
18922
|
+
*
|
|
18923
|
+
* ## Why `unaccounted*` exists
|
|
18924
|
+
*
|
|
18925
|
+
* `kinds` is enumerated from {@link MediaFileKindEnum} — the closed set the
|
|
18926
|
+
* writers use — and summed one kind at a time. `totalRows` / `totalBytes` come
|
|
18927
|
+
* from a SEPARATE unfiltered aggregate over the same rows, never from adding
|
|
18928
|
+
* `kinds` up. A row whose stored `kind` is not in the enum (written by a
|
|
18929
|
+
* retired code path, or by a version that knew a kind this one does not) would
|
|
18930
|
+
* otherwise vanish from the total silently, and an operator would delete
|
|
18931
|
+
* against a denominator smaller than the disk.
|
|
18932
|
+
*
|
|
18933
|
+
* `unaccountedRows` / `unaccountedBytes` are the difference. They are normally
|
|
18934
|
+
* zero; a non-zero value is a real finding and must be shown, not rounded away.
|
|
18935
|
+
*/
|
|
18936
|
+
var EventMediaKindBreakdownSchema = object({
|
|
18937
|
+
/** Every media row in scope, from one unfiltered aggregate. */
|
|
18938
|
+
totalRows: number().int(),
|
|
18939
|
+
/** Every media byte in scope, from that same aggregate. */
|
|
18940
|
+
totalBytes: number().int(),
|
|
18941
|
+
/** Per-kind footprint, ordered by bytes descending. */
|
|
18942
|
+
kinds: array(EventMediaKindFootprintSchema).readonly(),
|
|
18943
|
+
/** `totalRows` minus the summed `kinds` rows — see the schema note. */
|
|
18944
|
+
unaccountedRows: number().int(),
|
|
18945
|
+
/** `totalBytes` minus the summed `kinds` bytes — see the schema note. */
|
|
18946
|
+
unaccountedBytes: number().int()
|
|
18947
|
+
});
|
|
18777
18948
|
/** Per-kind counts returned by the event-prune / device-delete mutations. */
|
|
18778
18949
|
var EventPruneCountsSchema = object({
|
|
18779
18950
|
motion: number().int(),
|
|
@@ -18928,7 +19099,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
18928
19099
|
until: number().optional(),
|
|
18929
19100
|
kinds: array(string()).optional(),
|
|
18930
19101
|
limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
|
|
18931
|
-
}), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
|
|
19102
|
+
}), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
|
|
18932
19103
|
deviceId: number(),
|
|
18933
19104
|
since: number(),
|
|
18934
19105
|
until: number(),
|
|
@@ -18977,6 +19148,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
18977
19148
|
}), TrackFlagsSchema, { kind: "mutation" }), method(object({}), EventStoreFootprintSchema, {
|
|
18978
19149
|
kind: "query",
|
|
18979
19150
|
auth: "admin"
|
|
19151
|
+
}), method(object({ deviceId: number().int().optional() }), EventMediaKindBreakdownSchema, {
|
|
19152
|
+
kind: "query",
|
|
19153
|
+
auth: "admin"
|
|
18980
19154
|
}), method(object({
|
|
18981
19155
|
olderThanMs: number(),
|
|
18982
19156
|
reason: OpsLogReasonSchema.optional()
|
|
@@ -20941,6 +21115,20 @@ method(object({
|
|
|
20941
21115
|
error: string().optional()
|
|
20942
21116
|
}), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
|
|
20943
21117
|
providerId: string(),
|
|
21118
|
+
/**
|
|
21119
|
+
* The location this config is an UNSAVED edit of, when there is one.
|
|
21120
|
+
*
|
|
21121
|
+
* `listLocations` replaces every declared secret with the redaction
|
|
21122
|
+
* sentinel, so the edit modal's form state holds the sentinel for any
|
|
21123
|
+
* credential the operator did not retype — and posting that here
|
|
21124
|
+
* without a way to resolve it makes the provider try to authenticate
|
|
21125
|
+
* as `__camstack_redacted__` and report the operator's own working
|
|
21126
|
+
* password as wrong. Given this id, the orchestrator restores each
|
|
21127
|
+
* sentinel from the stored config (same rule as `upsertLocation`)
|
|
21128
|
+
* before dispatching. Omitted by the "Add location" wizard, where
|
|
21129
|
+
* every value was typed just now and nothing is stored yet.
|
|
21130
|
+
*/
|
|
21131
|
+
locationId: string().optional(),
|
|
20944
21132
|
config: record(string(), unknown())
|
|
20945
21133
|
}), object({
|
|
20946
21134
|
ok: boolean(),
|
|
@@ -26576,6 +26764,9 @@ method(object({
|
|
|
26576
26764
|
}), method(RelocateResidueInputSchema, RelocateResidueSchema, {
|
|
26577
26765
|
kind: "query",
|
|
26578
26766
|
auth: "admin"
|
|
26767
|
+
}), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
|
|
26768
|
+
kind: "mutation",
|
|
26769
|
+
auth: "admin"
|
|
26579
26770
|
}), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
26580
26771
|
kind: "mutation",
|
|
26581
26772
|
auth: "admin"
|
|
@@ -26967,7 +27158,26 @@ var SceneMonitorStatusSchema = object({
|
|
|
26967
27158
|
monitors: array(SceneMonitorSchema),
|
|
26968
27159
|
lastFetchedAt: number()
|
|
26969
27160
|
});
|
|
26970
|
-
|
|
27161
|
+
/**
|
|
27162
|
+
* One camera's row in a `listScenesBatch` answer.
|
|
27163
|
+
*
|
|
27164
|
+
* `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
|
|
27165
|
+
* is a `defaultActive` wrapper, so every camera is asked; a camera whose
|
|
27166
|
+
* provider is absent (pipeline-analytics not deployed, the post-processing node
|
|
27167
|
+
* down) cannot answer, and that is not the same fact as a camera with no scenes
|
|
27168
|
+
* configured. Fanned out per camera the difference was visible — one query
|
|
27169
|
+
* errored while the others resolved — and a batch that returned only the rows
|
|
27170
|
+
* it managed would have destroyed it, silently, by making an unreachable camera
|
|
27171
|
+
* indistinguishable from one that answered `monitors: []`.
|
|
27172
|
+
*
|
|
27173
|
+
* So: EVERY requested deviceId gets a row. `status: null` means "this camera
|
|
27174
|
+
* could not be read"; `status.monitors: []` means "read, and it has none".
|
|
27175
|
+
*/
|
|
27176
|
+
var SceneMonitorStatusForDeviceSchema = object({
|
|
27177
|
+
deviceId: number(),
|
|
27178
|
+
status: SceneMonitorStatusSchema.nullable()
|
|
27179
|
+
});
|
|
27180
|
+
DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
|
|
26971
27181
|
deviceId: number(),
|
|
26972
27182
|
label: string(),
|
|
26973
27183
|
roi: MaskRectShapeSchema,
|
|
@@ -28291,6 +28501,27 @@ var CameraOccupancySnapshotSchema = object({
|
|
|
28291
28501
|
stationaryObjects: array(StationaryObjectSchema).readonly().optional()
|
|
28292
28502
|
});
|
|
28293
28503
|
/**
|
|
28504
|
+
* One camera's row in a `getCurrentSnapshotBatch` answer.
|
|
28505
|
+
*
|
|
28506
|
+
* THREE outcomes, and the single-camera method could only express two of them
|
|
28507
|
+
* because `snapshot: null` was already spoken for:
|
|
28508
|
+
*
|
|
28509
|
+
* - `read: 'read'`, `snapshot` present — the live occupancy reading.
|
|
28510
|
+
* - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
|
|
28511
|
+
* yet: no frame since boot, and no parked-object registry to hydrate from.
|
|
28512
|
+
* - `read: 'unreadable'` — the owner could not answer for this
|
|
28513
|
+
* camera. `snapshot` is null, and it does NOT mean "nothing parked here".
|
|
28514
|
+
*
|
|
28515
|
+
* Collapsing the last two is the failure this field exists to prevent: a
|
|
28516
|
+
* hydration that threw would otherwise render as an empty Stationary section,
|
|
28517
|
+
* which is a definite claim about a camera nobody could read.
|
|
28518
|
+
*/
|
|
28519
|
+
var CameraOccupancySnapshotForDeviceSchema = object({
|
|
28520
|
+
deviceId: number(),
|
|
28521
|
+
read: _enum(["read", "unreadable"]),
|
|
28522
|
+
snapshot: CameraOccupancySnapshotSchema.nullable()
|
|
28523
|
+
});
|
|
28524
|
+
/**
|
|
28294
28525
|
* Time-series resolution. The history methods return one bucket per
|
|
28295
28526
|
* step over the requested range. Smaller resolutions cost more
|
|
28296
28527
|
* memory + bandwidth; bound to discrete steps so caller cannot ask
|
|
@@ -28314,7 +28545,7 @@ var HistoryPointSchema = object({
|
|
|
28314
28545
|
/** Object count averaged over the bucket (rounded to nearest integer). */
|
|
28315
28546
|
count: number().int().nonnegative()
|
|
28316
28547
|
});
|
|
28317
|
-
DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
|
|
28548
|
+
DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
|
|
28318
28549
|
deviceId: number(),
|
|
28319
28550
|
zoneId: string(),
|
|
28320
28551
|
className: string().optional()
|
|
@@ -31595,6 +31826,12 @@ Object.freeze({
|
|
|
31595
31826
|
addonId: null,
|
|
31596
31827
|
access: "view"
|
|
31597
31828
|
},
|
|
31829
|
+
"pipelineAnalytics.getEventMediaFootprintByKind": {
|
|
31830
|
+
capName: "pipeline-analytics",
|
|
31831
|
+
capScope: "device",
|
|
31832
|
+
addonId: null,
|
|
31833
|
+
access: "view"
|
|
31834
|
+
},
|
|
31598
31835
|
"pipelineAnalytics.getEventStoreFootprint": {
|
|
31599
31836
|
capName: "pipeline-analytics",
|
|
31600
31837
|
capScope: "device",
|
|
@@ -31613,6 +31850,12 @@ Object.freeze({
|
|
|
31613
31850
|
addonId: null,
|
|
31614
31851
|
access: "view"
|
|
31615
31852
|
},
|
|
31853
|
+
"pipelineAnalytics.getKeyEventsBatch": {
|
|
31854
|
+
capName: "pipeline-analytics",
|
|
31855
|
+
capScope: "device",
|
|
31856
|
+
addonId: null,
|
|
31857
|
+
access: "view"
|
|
31858
|
+
},
|
|
31616
31859
|
"pipelineAnalytics.getMotionEvents": {
|
|
31617
31860
|
capName: "pipeline-analytics",
|
|
31618
31861
|
capScope: "device",
|
|
@@ -32789,6 +33032,12 @@ Object.freeze({
|
|
|
32789
33032
|
addonId: null,
|
|
32790
33033
|
access: "view"
|
|
32791
33034
|
},
|
|
33035
|
+
"recording.reconcileLedgerAgainstDisk": {
|
|
33036
|
+
capName: "recording",
|
|
33037
|
+
capScope: "system",
|
|
33038
|
+
addonId: null,
|
|
33039
|
+
access: "create"
|
|
33040
|
+
},
|
|
32792
33041
|
"recording.refreshStorageLocationsForMigration": {
|
|
32793
33042
|
capName: "recording",
|
|
32794
33043
|
capScope: "system",
|
|
@@ -32915,6 +33164,12 @@ Object.freeze({
|
|
|
32915
33164
|
addonId: null,
|
|
32916
33165
|
access: "view"
|
|
32917
33166
|
},
|
|
33167
|
+
"sceneMonitor.listScenesBatch": {
|
|
33168
|
+
capName: "scene-monitor",
|
|
33169
|
+
capScope: "device",
|
|
33170
|
+
addonId: null,
|
|
33171
|
+
access: "view"
|
|
33172
|
+
},
|
|
32918
33173
|
"sceneMonitor.recheckNow": {
|
|
32919
33174
|
capName: "scene-monitor",
|
|
32920
33175
|
capScope: "device",
|
|
@@ -34271,6 +34526,12 @@ Object.freeze({
|
|
|
34271
34526
|
addonId: null,
|
|
34272
34527
|
access: "view"
|
|
34273
34528
|
},
|
|
34529
|
+
"zoneAnalytics.getCurrentSnapshotBatch": {
|
|
34530
|
+
capName: "zone-analytics",
|
|
34531
|
+
capScope: "device",
|
|
34532
|
+
addonId: null,
|
|
34533
|
+
access: "view"
|
|
34534
|
+
},
|
|
34274
34535
|
"zoneAnalytics.getUnzonedHistory": {
|
|
34275
34536
|
capName: "zone-analytics",
|
|
34276
34537
|
capScope: "device",
|
|
@@ -35260,6 +35521,11 @@ Object.freeze({
|
|
|
35260
35521
|
form: "single",
|
|
35261
35522
|
optional: false
|
|
35262
35523
|
}],
|
|
35524
|
+
"pipelineAnalytics.getEventMediaFootprintByKind": [{
|
|
35525
|
+
name: "deviceId",
|
|
35526
|
+
form: "single",
|
|
35527
|
+
optional: true
|
|
35528
|
+
}],
|
|
35263
35529
|
"pipelineAnalytics.getGroup": [{
|
|
35264
35530
|
name: "deviceId",
|
|
35265
35531
|
form: "single",
|
|
@@ -35270,6 +35536,11 @@ Object.freeze({
|
|
|
35270
35536
|
form: "single",
|
|
35271
35537
|
optional: false
|
|
35272
35538
|
}],
|
|
35539
|
+
"pipelineAnalytics.getKeyEventsBatch": [{
|
|
35540
|
+
name: "deviceIds",
|
|
35541
|
+
form: "array",
|
|
35542
|
+
optional: false
|
|
35543
|
+
}],
|
|
35273
35544
|
"pipelineAnalytics.getMotionEvents": [{
|
|
35274
35545
|
name: "deviceId",
|
|
35275
35546
|
form: "single",
|
|
@@ -35710,6 +35981,11 @@ Object.freeze({
|
|
|
35710
35981
|
form: "single",
|
|
35711
35982
|
optional: false
|
|
35712
35983
|
}],
|
|
35984
|
+
"recording.reconcileLedgerAgainstDisk": [{
|
|
35985
|
+
name: "deviceId",
|
|
35986
|
+
form: "single",
|
|
35987
|
+
optional: true
|
|
35988
|
+
}],
|
|
35713
35989
|
"recording.relocateFootage": [{
|
|
35714
35990
|
name: "deviceId",
|
|
35715
35991
|
form: "single",
|
|
@@ -35775,6 +36051,11 @@ Object.freeze({
|
|
|
35775
36051
|
form: "single",
|
|
35776
36052
|
optional: false
|
|
35777
36053
|
}],
|
|
36054
|
+
"sceneMonitor.listScenesBatch": [{
|
|
36055
|
+
name: "deviceIds",
|
|
36056
|
+
form: "array",
|
|
36057
|
+
optional: false
|
|
36058
|
+
}],
|
|
35778
36059
|
"sceneMonitor.recheckNow": [{
|
|
35779
36060
|
name: "deviceId",
|
|
35780
36061
|
form: "single",
|
|
@@ -36036,6 +36317,11 @@ Object.freeze({
|
|
|
36036
36317
|
form: "single",
|
|
36037
36318
|
optional: false
|
|
36038
36319
|
}],
|
|
36320
|
+
"zoneAnalytics.getCurrentSnapshotBatch": [{
|
|
36321
|
+
name: "deviceIds",
|
|
36322
|
+
form: "array",
|
|
36323
|
+
optional: false
|
|
36324
|
+
}],
|
|
36039
36325
|
"zoneAnalytics.getUnzonedHistory": [{
|
|
36040
36326
|
name: "deviceId",
|
|
36041
36327
|
form: "single",
|
package/dist/smtp.addon.mjs
CHANGED
|
@@ -8037,6 +8037,20 @@ var RelocateJobSchema = object({
|
|
|
8037
8037
|
* in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
|
|
8038
8038
|
*/
|
|
8039
8039
|
rowsReconciled: number().int().nonnegative().optional(),
|
|
8040
|
+
/**
|
|
8041
|
+
* Rows this run FORGOT because the file they name is not on disk.
|
|
8042
|
+
*
|
|
8043
|
+
* The mover derived the path from the row's own fields and `stat`ed it; an
|
|
8044
|
+
* ENOENT there is a per-path confirmation that the segment is gone (D296),
|
|
8045
|
+
* and the durable row is dropped through the same channel eviction uses. It
|
|
8046
|
+
* is reported for the same reason `rowsReconciled` is: this is a durable
|
|
8047
|
+
* mutation nobody asked for, and a migration that quietly erases hour rows is
|
|
8048
|
+
* the same failure as one that quietly skips them (D295).
|
|
8049
|
+
*
|
|
8050
|
+
* The production drain of 2026-08-30 would have reported 11 074 here — the
|
|
8051
|
+
* ledger claimed 5.65 GB of footage that no longer existed.
|
|
8052
|
+
*/
|
|
8053
|
+
rowsForgotten: number().int().nonnegative().optional(),
|
|
8040
8054
|
startedAt: number(),
|
|
8041
8055
|
finishedAt: number().nullable(),
|
|
8042
8056
|
error: string().nullable()
|
|
@@ -8400,6 +8414,91 @@ var RelocateResidueSchema = object({
|
|
|
8400
8414
|
segments: number().int().nonnegative(),
|
|
8401
8415
|
bytes: number().int().nonnegative()
|
|
8402
8416
|
}).nullable();
|
|
8417
|
+
/**
|
|
8418
|
+
* Ask one location whether its durable hour rows describe the disk — the walk
|
|
8419
|
+
* (D319).
|
|
8420
|
+
*
|
|
8421
|
+
* `apply` DEFAULTS TO FALSE and that default is the product: the operator's
|
|
8422
|
+
* missing tool is the question, and the dry run is how they sanity-check the
|
|
8423
|
+
* destructive run before authorising it.
|
|
8424
|
+
*/
|
|
8425
|
+
var LedgerWalkInputSchema = object({
|
|
8426
|
+
locationId: string().min(1),
|
|
8427
|
+
/** Forget the confirmed-absent rows, rather than only counting them. */
|
|
8428
|
+
apply: boolean().optional(),
|
|
8429
|
+
/** Narrow to one camera. */
|
|
8430
|
+
deviceId: number().int().positive().optional(),
|
|
8431
|
+
/** Narrow to these recording profiles; empty/absent = every profile. */
|
|
8432
|
+
profiles: array(string().min(1)).optional()
|
|
8433
|
+
});
|
|
8434
|
+
/** Why a whole walk did nothing. Every one leaves the ledger untouched. */
|
|
8435
|
+
var LedgerWalkRefusalSchema = _enum([
|
|
8436
|
+
"location-unknown",
|
|
8437
|
+
"source-writable",
|
|
8438
|
+
"no-ledger",
|
|
8439
|
+
"archive-unreadable",
|
|
8440
|
+
"anchor-absent",
|
|
8441
|
+
"anchor-unreadable",
|
|
8442
|
+
"anchor-moved"
|
|
8443
|
+
]);
|
|
8444
|
+
_enum([
|
|
8445
|
+
"live-tail",
|
|
8446
|
+
"listing-error",
|
|
8447
|
+
"path-mismatch",
|
|
8448
|
+
"durable-refused"
|
|
8449
|
+
]);
|
|
8450
|
+
/** Every skip reason, always present, always a number — so a reason that never
|
|
8451
|
+
* fired reports as zero rather than absent and the report shape is constant
|
|
8452
|
+
* between passes. Spelled out rather than `z.record` for exactly that. */
|
|
8453
|
+
var LedgerWalkSkipCountsSchema = object({
|
|
8454
|
+
"live-tail": number().int().nonnegative(),
|
|
8455
|
+
"listing-error": number().int().nonnegative(),
|
|
8456
|
+
"path-mismatch": number().int().nonnegative(),
|
|
8457
|
+
"durable-refused": number().int().nonnegative()
|
|
8458
|
+
});
|
|
8459
|
+
/** One camera's share of a walk, so a report names cameras and not rows. */
|
|
8460
|
+
var LedgerWalkDeviceReportSchema = object({
|
|
8461
|
+
deviceId: number().int(),
|
|
8462
|
+
hoursWalked: number().int().nonnegative(),
|
|
8463
|
+
hoursMissing: number().int().nonnegative(),
|
|
8464
|
+
ghostSegments: number().int().nonnegative(),
|
|
8465
|
+
ghostBytes: number().int().nonnegative(),
|
|
8466
|
+
forgottenSegments: number().int().nonnegative(),
|
|
8467
|
+
orphanFiles: number().int().nonnegative()
|
|
8468
|
+
});
|
|
8469
|
+
/**
|
|
8470
|
+
* What one walk claimed, listed, found and (only when armed) forgot.
|
|
8471
|
+
*
|
|
8472
|
+
* `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
|
|
8473
|
+
* walk that saw a fraction of the location is visible in its own report rather
|
|
8474
|
+
* than in the absence of one.
|
|
8475
|
+
*/
|
|
8476
|
+
var LedgerWalkReportSchema = object({
|
|
8477
|
+
locationId: string(),
|
|
8478
|
+
applied: boolean(),
|
|
8479
|
+
refused: LedgerWalkRefusalSchema.nullable(),
|
|
8480
|
+
archiveSegments: number().int().nonnegative().nullable(),
|
|
8481
|
+
archiveBytes: number().int().nonnegative().nullable(),
|
|
8482
|
+
hoursClaimed: number().int().nonnegative(),
|
|
8483
|
+
hoursWalked: number().int().nonnegative(),
|
|
8484
|
+
hoursMissing: number().int().nonnegative(),
|
|
8485
|
+
/** `readdir` calls issued — the cost, stated in the unit that is paid. */
|
|
8486
|
+
listings: number().int().nonnegative(),
|
|
8487
|
+
segmentsClaimed: number().int().nonnegative(),
|
|
8488
|
+
ghostSegments: number().int().nonnegative(),
|
|
8489
|
+
ghostBytes: number().int().nonnegative(),
|
|
8490
|
+
ghostHoursWhole: number().int().nonnegative(),
|
|
8491
|
+
forgottenSegments: number().int().nonnegative(),
|
|
8492
|
+
forgottenBytes: number().int().nonnegative(),
|
|
8493
|
+
/** Files under a claimed hour that no durable row names. Never deleted. */
|
|
8494
|
+
orphanFiles: number().int().nonnegative(),
|
|
8495
|
+
orphanSample: array(string()).readonly(),
|
|
8496
|
+
hoursSkipped: number().int().nonnegative(),
|
|
8497
|
+
skippedByReason: LedgerWalkSkipCountsSchema,
|
|
8498
|
+
/** The walk stopped at its per-pass hour bound with claims unwalked. */
|
|
8499
|
+
bounded: boolean(),
|
|
8500
|
+
byDevice: array(LedgerWalkDeviceReportSchema).readonly()
|
|
8501
|
+
});
|
|
8403
8502
|
/** How many rows a media pass would still act on against a given target — the
|
|
8404
8503
|
* media lane's denominator AND its residue, from ONE derivation so the two can
|
|
8405
8504
|
* never disagree. `null` = the count could not be taken. */
|
|
@@ -18673,13 +18772,15 @@ var ListGroupsPageSchema = object({
|
|
|
18673
18772
|
groups: array(AnalyticsGroupRecordSchema).readonly(),
|
|
18674
18773
|
nextCursor: string().nullable()
|
|
18675
18774
|
});
|
|
18775
|
+
var KEY_EVENTS_DEFAULT_LIMIT = 50;
|
|
18776
|
+
var KEY_EVENTS_MAX_LIMIT = 200;
|
|
18676
18777
|
var KeyEventQueryInput = object({
|
|
18677
18778
|
deviceId: number(),
|
|
18678
18779
|
/** Window lower bound (track firstSeen ≥ since). */
|
|
18679
18780
|
since: number(),
|
|
18680
18781
|
/** Window upper bound (track firstSeen ≤ until). */
|
|
18681
18782
|
until: number(),
|
|
18682
|
-
limit: number().int().min(1).max(
|
|
18783
|
+
limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
|
|
18683
18784
|
/** Drop tracks scoring below this importance. */
|
|
18684
18785
|
minImportance: number().min(0).max(1).optional(),
|
|
18685
18786
|
/** Restrict to a single class (e.g. 'person'). */
|
|
@@ -18701,6 +18802,32 @@ var KeyEventSchema = object({
|
|
|
18701
18802
|
...TrackFlagFields,
|
|
18702
18803
|
...TrackRetrainFields
|
|
18703
18804
|
});
|
|
18805
|
+
/** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
|
|
18806
|
+
var KeyEventBatchQueryInput = object({
|
|
18807
|
+
deviceIds: array(number()).min(1).max(200),
|
|
18808
|
+
since: number(),
|
|
18809
|
+
until: number(),
|
|
18810
|
+
/** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
|
|
18811
|
+
* across the set, which would let a busy camera starve a quiet one of its
|
|
18812
|
+
* rows and change what the merged feed contains. */
|
|
18813
|
+
limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
|
|
18814
|
+
minImportance: number().min(0).max(1).optional(),
|
|
18815
|
+
classFilter: string().optional()
|
|
18816
|
+
});
|
|
18817
|
+
/**
|
|
18818
|
+
* One camera's key events in a batch answer.
|
|
18819
|
+
*
|
|
18820
|
+
* The row exists for every requested id. `getKeyEvents` degrades to `[]` on
|
|
18821
|
+
* error rather than throwing, so a camera whose store read failed and one with
|
|
18822
|
+
* no events in the window were ALREADY indistinguishable per camera — the
|
|
18823
|
+
* batch does not make that worse, and the row keeps the deviceId the single
|
|
18824
|
+
* method's output never carried (the caller used to stamp it from the fan-out
|
|
18825
|
+
* key, which only worked because there was one query per camera).
|
|
18826
|
+
*/
|
|
18827
|
+
var KeyEventsForDeviceSchema = object({
|
|
18828
|
+
deviceId: number(),
|
|
18829
|
+
events: array(KeyEventSchema).readonly()
|
|
18830
|
+
});
|
|
18704
18831
|
object({
|
|
18705
18832
|
trackId: string(),
|
|
18706
18833
|
className: string(),
|
|
@@ -18772,6 +18899,50 @@ var EventStoreFootprintSchema = object({
|
|
|
18772
18899
|
totalBytes: number().int(),
|
|
18773
18900
|
devices: array(EventStoreDeviceFootprintSchema).readonly()
|
|
18774
18901
|
});
|
|
18902
|
+
/** Event-media footprint for one {@link MediaFileKind}. */
|
|
18903
|
+
var EventMediaKindFootprintSchema = object({
|
|
18904
|
+
kind: MediaFileKindEnum,
|
|
18905
|
+
/** Media rows of this kind. */
|
|
18906
|
+
rows: number().int(),
|
|
18907
|
+
/** Bytes on disk held by those rows. */
|
|
18908
|
+
bytes: number().int()
|
|
18909
|
+
});
|
|
18910
|
+
/**
|
|
18911
|
+
* The media footprint broken down by KIND — the axis a deletion decision
|
|
18912
|
+
* actually turns on.
|
|
18913
|
+
*
|
|
18914
|
+
* A byte total says how much there is; it cannot say what is safe to remove.
|
|
18915
|
+
* The deletable set (the periodic `snapshot` filmstrip, the surplus per-edge
|
|
18916
|
+
* motion stills) and the keep set (`firstFrame`, rolling `lastFrame`,
|
|
18917
|
+
* `thumbnail`/`thumbnailSmall`, `keyFrame`/`keyFrameSmall`, the face/plate
|
|
18918
|
+
* buffers, gallery media, the CLIP `crop`) are distinguished by `kind` and by
|
|
18919
|
+
* nothing else, so sizing a deletion means summing per kind.
|
|
18920
|
+
*
|
|
18921
|
+
* ## Why `unaccounted*` exists
|
|
18922
|
+
*
|
|
18923
|
+
* `kinds` is enumerated from {@link MediaFileKindEnum} — the closed set the
|
|
18924
|
+
* writers use — and summed one kind at a time. `totalRows` / `totalBytes` come
|
|
18925
|
+
* from a SEPARATE unfiltered aggregate over the same rows, never from adding
|
|
18926
|
+
* `kinds` up. A row whose stored `kind` is not in the enum (written by a
|
|
18927
|
+
* retired code path, or by a version that knew a kind this one does not) would
|
|
18928
|
+
* otherwise vanish from the total silently, and an operator would delete
|
|
18929
|
+
* against a denominator smaller than the disk.
|
|
18930
|
+
*
|
|
18931
|
+
* `unaccountedRows` / `unaccountedBytes` are the difference. They are normally
|
|
18932
|
+
* zero; a non-zero value is a real finding and must be shown, not rounded away.
|
|
18933
|
+
*/
|
|
18934
|
+
var EventMediaKindBreakdownSchema = object({
|
|
18935
|
+
/** Every media row in scope, from one unfiltered aggregate. */
|
|
18936
|
+
totalRows: number().int(),
|
|
18937
|
+
/** Every media byte in scope, from that same aggregate. */
|
|
18938
|
+
totalBytes: number().int(),
|
|
18939
|
+
/** Per-kind footprint, ordered by bytes descending. */
|
|
18940
|
+
kinds: array(EventMediaKindFootprintSchema).readonly(),
|
|
18941
|
+
/** `totalRows` minus the summed `kinds` rows — see the schema note. */
|
|
18942
|
+
unaccountedRows: number().int(),
|
|
18943
|
+
/** `totalBytes` minus the summed `kinds` bytes — see the schema note. */
|
|
18944
|
+
unaccountedBytes: number().int()
|
|
18945
|
+
});
|
|
18775
18946
|
/** Per-kind counts returned by the event-prune / device-delete mutations. */
|
|
18776
18947
|
var EventPruneCountsSchema = object({
|
|
18777
18948
|
motion: number().int(),
|
|
@@ -18926,7 +19097,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
18926
19097
|
until: number().optional(),
|
|
18927
19098
|
kinds: array(string()).optional(),
|
|
18928
19099
|
limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
|
|
18929
|
-
}), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
|
|
19100
|
+
}), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
|
|
18930
19101
|
deviceId: number(),
|
|
18931
19102
|
since: number(),
|
|
18932
19103
|
until: number(),
|
|
@@ -18975,6 +19146,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
18975
19146
|
}), TrackFlagsSchema, { kind: "mutation" }), method(object({}), EventStoreFootprintSchema, {
|
|
18976
19147
|
kind: "query",
|
|
18977
19148
|
auth: "admin"
|
|
19149
|
+
}), method(object({ deviceId: number().int().optional() }), EventMediaKindBreakdownSchema, {
|
|
19150
|
+
kind: "query",
|
|
19151
|
+
auth: "admin"
|
|
18978
19152
|
}), method(object({
|
|
18979
19153
|
olderThanMs: number(),
|
|
18980
19154
|
reason: OpsLogReasonSchema.optional()
|
|
@@ -20939,6 +21113,20 @@ method(object({
|
|
|
20939
21113
|
error: string().optional()
|
|
20940
21114
|
}), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
|
|
20941
21115
|
providerId: string(),
|
|
21116
|
+
/**
|
|
21117
|
+
* The location this config is an UNSAVED edit of, when there is one.
|
|
21118
|
+
*
|
|
21119
|
+
* `listLocations` replaces every declared secret with the redaction
|
|
21120
|
+
* sentinel, so the edit modal's form state holds the sentinel for any
|
|
21121
|
+
* credential the operator did not retype — and posting that here
|
|
21122
|
+
* without a way to resolve it makes the provider try to authenticate
|
|
21123
|
+
* as `__camstack_redacted__` and report the operator's own working
|
|
21124
|
+
* password as wrong. Given this id, the orchestrator restores each
|
|
21125
|
+
* sentinel from the stored config (same rule as `upsertLocation`)
|
|
21126
|
+
* before dispatching. Omitted by the "Add location" wizard, where
|
|
21127
|
+
* every value was typed just now and nothing is stored yet.
|
|
21128
|
+
*/
|
|
21129
|
+
locationId: string().optional(),
|
|
20942
21130
|
config: record(string(), unknown())
|
|
20943
21131
|
}), object({
|
|
20944
21132
|
ok: boolean(),
|
|
@@ -26574,6 +26762,9 @@ method(object({
|
|
|
26574
26762
|
}), method(RelocateResidueInputSchema, RelocateResidueSchema, {
|
|
26575
26763
|
kind: "query",
|
|
26576
26764
|
auth: "admin"
|
|
26765
|
+
}), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
|
|
26766
|
+
kind: "mutation",
|
|
26767
|
+
auth: "admin"
|
|
26577
26768
|
}), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
26578
26769
|
kind: "mutation",
|
|
26579
26770
|
auth: "admin"
|
|
@@ -26965,7 +27156,26 @@ var SceneMonitorStatusSchema = object({
|
|
|
26965
27156
|
monitors: array(SceneMonitorSchema),
|
|
26966
27157
|
lastFetchedAt: number()
|
|
26967
27158
|
});
|
|
26968
|
-
|
|
27159
|
+
/**
|
|
27160
|
+
* One camera's row in a `listScenesBatch` answer.
|
|
27161
|
+
*
|
|
27162
|
+
* `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
|
|
27163
|
+
* is a `defaultActive` wrapper, so every camera is asked; a camera whose
|
|
27164
|
+
* provider is absent (pipeline-analytics not deployed, the post-processing node
|
|
27165
|
+
* down) cannot answer, and that is not the same fact as a camera with no scenes
|
|
27166
|
+
* configured. Fanned out per camera the difference was visible — one query
|
|
27167
|
+
* errored while the others resolved — and a batch that returned only the rows
|
|
27168
|
+
* it managed would have destroyed it, silently, by making an unreachable camera
|
|
27169
|
+
* indistinguishable from one that answered `monitors: []`.
|
|
27170
|
+
*
|
|
27171
|
+
* So: EVERY requested deviceId gets a row. `status: null` means "this camera
|
|
27172
|
+
* could not be read"; `status.monitors: []` means "read, and it has none".
|
|
27173
|
+
*/
|
|
27174
|
+
var SceneMonitorStatusForDeviceSchema = object({
|
|
27175
|
+
deviceId: number(),
|
|
27176
|
+
status: SceneMonitorStatusSchema.nullable()
|
|
27177
|
+
});
|
|
27178
|
+
DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
|
|
26969
27179
|
deviceId: number(),
|
|
26970
27180
|
label: string(),
|
|
26971
27181
|
roi: MaskRectShapeSchema,
|
|
@@ -28289,6 +28499,27 @@ var CameraOccupancySnapshotSchema = object({
|
|
|
28289
28499
|
stationaryObjects: array(StationaryObjectSchema).readonly().optional()
|
|
28290
28500
|
});
|
|
28291
28501
|
/**
|
|
28502
|
+
* One camera's row in a `getCurrentSnapshotBatch` answer.
|
|
28503
|
+
*
|
|
28504
|
+
* THREE outcomes, and the single-camera method could only express two of them
|
|
28505
|
+
* because `snapshot: null` was already spoken for:
|
|
28506
|
+
*
|
|
28507
|
+
* - `read: 'read'`, `snapshot` present — the live occupancy reading.
|
|
28508
|
+
* - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
|
|
28509
|
+
* yet: no frame since boot, and no parked-object registry to hydrate from.
|
|
28510
|
+
* - `read: 'unreadable'` — the owner could not answer for this
|
|
28511
|
+
* camera. `snapshot` is null, and it does NOT mean "nothing parked here".
|
|
28512
|
+
*
|
|
28513
|
+
* Collapsing the last two is the failure this field exists to prevent: a
|
|
28514
|
+
* hydration that threw would otherwise render as an empty Stationary section,
|
|
28515
|
+
* which is a definite claim about a camera nobody could read.
|
|
28516
|
+
*/
|
|
28517
|
+
var CameraOccupancySnapshotForDeviceSchema = object({
|
|
28518
|
+
deviceId: number(),
|
|
28519
|
+
read: _enum(["read", "unreadable"]),
|
|
28520
|
+
snapshot: CameraOccupancySnapshotSchema.nullable()
|
|
28521
|
+
});
|
|
28522
|
+
/**
|
|
28292
28523
|
* Time-series resolution. The history methods return one bucket per
|
|
28293
28524
|
* step over the requested range. Smaller resolutions cost more
|
|
28294
28525
|
* memory + bandwidth; bound to discrete steps so caller cannot ask
|
|
@@ -28312,7 +28543,7 @@ var HistoryPointSchema = object({
|
|
|
28312
28543
|
/** Object count averaged over the bucket (rounded to nearest integer). */
|
|
28313
28544
|
count: number().int().nonnegative()
|
|
28314
28545
|
});
|
|
28315
|
-
DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
|
|
28546
|
+
DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
|
|
28316
28547
|
deviceId: number(),
|
|
28317
28548
|
zoneId: string(),
|
|
28318
28549
|
className: string().optional()
|
|
@@ -31593,6 +31824,12 @@ Object.freeze({
|
|
|
31593
31824
|
addonId: null,
|
|
31594
31825
|
access: "view"
|
|
31595
31826
|
},
|
|
31827
|
+
"pipelineAnalytics.getEventMediaFootprintByKind": {
|
|
31828
|
+
capName: "pipeline-analytics",
|
|
31829
|
+
capScope: "device",
|
|
31830
|
+
addonId: null,
|
|
31831
|
+
access: "view"
|
|
31832
|
+
},
|
|
31596
31833
|
"pipelineAnalytics.getEventStoreFootprint": {
|
|
31597
31834
|
capName: "pipeline-analytics",
|
|
31598
31835
|
capScope: "device",
|
|
@@ -31611,6 +31848,12 @@ Object.freeze({
|
|
|
31611
31848
|
addonId: null,
|
|
31612
31849
|
access: "view"
|
|
31613
31850
|
},
|
|
31851
|
+
"pipelineAnalytics.getKeyEventsBatch": {
|
|
31852
|
+
capName: "pipeline-analytics",
|
|
31853
|
+
capScope: "device",
|
|
31854
|
+
addonId: null,
|
|
31855
|
+
access: "view"
|
|
31856
|
+
},
|
|
31614
31857
|
"pipelineAnalytics.getMotionEvents": {
|
|
31615
31858
|
capName: "pipeline-analytics",
|
|
31616
31859
|
capScope: "device",
|
|
@@ -32787,6 +33030,12 @@ Object.freeze({
|
|
|
32787
33030
|
addonId: null,
|
|
32788
33031
|
access: "view"
|
|
32789
33032
|
},
|
|
33033
|
+
"recording.reconcileLedgerAgainstDisk": {
|
|
33034
|
+
capName: "recording",
|
|
33035
|
+
capScope: "system",
|
|
33036
|
+
addonId: null,
|
|
33037
|
+
access: "create"
|
|
33038
|
+
},
|
|
32790
33039
|
"recording.refreshStorageLocationsForMigration": {
|
|
32791
33040
|
capName: "recording",
|
|
32792
33041
|
capScope: "system",
|
|
@@ -32913,6 +33162,12 @@ Object.freeze({
|
|
|
32913
33162
|
addonId: null,
|
|
32914
33163
|
access: "view"
|
|
32915
33164
|
},
|
|
33165
|
+
"sceneMonitor.listScenesBatch": {
|
|
33166
|
+
capName: "scene-monitor",
|
|
33167
|
+
capScope: "device",
|
|
33168
|
+
addonId: null,
|
|
33169
|
+
access: "view"
|
|
33170
|
+
},
|
|
32916
33171
|
"sceneMonitor.recheckNow": {
|
|
32917
33172
|
capName: "scene-monitor",
|
|
32918
33173
|
capScope: "device",
|
|
@@ -34269,6 +34524,12 @@ Object.freeze({
|
|
|
34269
34524
|
addonId: null,
|
|
34270
34525
|
access: "view"
|
|
34271
34526
|
},
|
|
34527
|
+
"zoneAnalytics.getCurrentSnapshotBatch": {
|
|
34528
|
+
capName: "zone-analytics",
|
|
34529
|
+
capScope: "device",
|
|
34530
|
+
addonId: null,
|
|
34531
|
+
access: "view"
|
|
34532
|
+
},
|
|
34272
34533
|
"zoneAnalytics.getUnzonedHistory": {
|
|
34273
34534
|
capName: "zone-analytics",
|
|
34274
34535
|
capScope: "device",
|
|
@@ -35258,6 +35519,11 @@ Object.freeze({
|
|
|
35258
35519
|
form: "single",
|
|
35259
35520
|
optional: false
|
|
35260
35521
|
}],
|
|
35522
|
+
"pipelineAnalytics.getEventMediaFootprintByKind": [{
|
|
35523
|
+
name: "deviceId",
|
|
35524
|
+
form: "single",
|
|
35525
|
+
optional: true
|
|
35526
|
+
}],
|
|
35261
35527
|
"pipelineAnalytics.getGroup": [{
|
|
35262
35528
|
name: "deviceId",
|
|
35263
35529
|
form: "single",
|
|
@@ -35268,6 +35534,11 @@ Object.freeze({
|
|
|
35268
35534
|
form: "single",
|
|
35269
35535
|
optional: false
|
|
35270
35536
|
}],
|
|
35537
|
+
"pipelineAnalytics.getKeyEventsBatch": [{
|
|
35538
|
+
name: "deviceIds",
|
|
35539
|
+
form: "array",
|
|
35540
|
+
optional: false
|
|
35541
|
+
}],
|
|
35271
35542
|
"pipelineAnalytics.getMotionEvents": [{
|
|
35272
35543
|
name: "deviceId",
|
|
35273
35544
|
form: "single",
|
|
@@ -35708,6 +35979,11 @@ Object.freeze({
|
|
|
35708
35979
|
form: "single",
|
|
35709
35980
|
optional: false
|
|
35710
35981
|
}],
|
|
35982
|
+
"recording.reconcileLedgerAgainstDisk": [{
|
|
35983
|
+
name: "deviceId",
|
|
35984
|
+
form: "single",
|
|
35985
|
+
optional: true
|
|
35986
|
+
}],
|
|
35711
35987
|
"recording.relocateFootage": [{
|
|
35712
35988
|
name: "deviceId",
|
|
35713
35989
|
form: "single",
|
|
@@ -35773,6 +36049,11 @@ Object.freeze({
|
|
|
35773
36049
|
form: "single",
|
|
35774
36050
|
optional: false
|
|
35775
36051
|
}],
|
|
36052
|
+
"sceneMonitor.listScenesBatch": [{
|
|
36053
|
+
name: "deviceIds",
|
|
36054
|
+
form: "array",
|
|
36055
|
+
optional: false
|
|
36056
|
+
}],
|
|
35776
36057
|
"sceneMonitor.recheckNow": [{
|
|
35777
36058
|
name: "deviceId",
|
|
35778
36059
|
form: "single",
|
|
@@ -36034,6 +36315,11 @@ Object.freeze({
|
|
|
36034
36315
|
form: "single",
|
|
36035
36316
|
optional: false
|
|
36036
36317
|
}],
|
|
36318
|
+
"zoneAnalytics.getCurrentSnapshotBatch": [{
|
|
36319
|
+
name: "deviceIds",
|
|
36320
|
+
form: "array",
|
|
36321
|
+
optional: false
|
|
36322
|
+
}],
|
|
36037
36323
|
"zoneAnalytics.getUnzonedHistory": [{
|
|
36038
36324
|
name: "deviceId",
|
|
36039
36325
|
form: "single",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-smtp-nodemailer",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.48",
|
|
4
4
|
"description": "SMTP email provider addon for CamStack — wraps `nodemailer` and registers a `smtp-provider` cap collection entry. Used by magic-link login + notifier addons.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|