@camstack/addon-notifiers 1.2.103 → 1.2.105
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/addon.js +292 -58
- package/dist/addon.mjs +292 -58
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -29,7 +29,7 @@ let node_fs = require("node:fs");
|
|
|
29
29
|
let node_path = require("node:path");
|
|
30
30
|
node_path = __toESM(node_path);
|
|
31
31
|
let node_url = require("node:url");
|
|
32
|
-
//#region ../types/dist/event-category-
|
|
32
|
+
//#region ../types/dist/event-category-ZyX6jcse.mjs
|
|
33
33
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
34
34
|
EventCategory["SystemBoot"] = "system.boot";
|
|
35
35
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -590,7 +590,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
590
590
|
* a package zone — a newly-appeared stationary object of a package class
|
|
591
591
|
* that cleared the class / zone / dwell / size gates. Payload:
|
|
592
592
|
* `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
|
|
593
|
-
* entryId, className, zoneIds,
|
|
593
|
+
* entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
|
|
594
594
|
* Telemetry (D8): the durable record is the `package-events` store row;
|
|
595
595
|
* this bus topic drives notifier rules + live UI. See
|
|
596
596
|
* docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
|
|
@@ -5376,7 +5376,7 @@ var ZodIssueCode = {
|
|
|
5376
5376
|
var ZodFirstPartyTypeKind;
|
|
5377
5377
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5378
5378
|
//#endregion
|
|
5379
|
-
//#region ../types/dist/sleep-
|
|
5379
|
+
//#region ../types/dist/sleep-DBKu2-U5.mjs
|
|
5380
5380
|
/**
|
|
5381
5381
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5382
5382
|
* window to float samples (D455).
|
|
@@ -7327,13 +7327,30 @@ new Set([
|
|
|
7327
7327
|
"scene",
|
|
7328
7328
|
"motion",
|
|
7329
7329
|
"object",
|
|
7330
|
-
"audio"
|
|
7330
|
+
"audio",
|
|
7331
|
+
"mosaic"
|
|
7331
7332
|
]);
|
|
7332
|
-
|
|
7333
|
+
/**
|
|
7334
|
+
* The owner types that are event tables, in the order the event-media resolver
|
|
7335
|
+
* should consider them. Exported so a consumer asking "is this key an event?"
|
|
7336
|
+
* does not re-spell the list and drift from it.
|
|
7337
|
+
*/
|
|
7338
|
+
var EVENT_OWNER_TYPES = [
|
|
7333
7339
|
"motion",
|
|
7334
7340
|
"object",
|
|
7335
7341
|
"audio"
|
|
7336
|
-
]
|
|
7342
|
+
];
|
|
7343
|
+
/**
|
|
7344
|
+
* The same list as a Zod enum, for the cap inputs that must NAME the table
|
|
7345
|
+
* (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
|
|
7346
|
+
* `(table, id)`).
|
|
7347
|
+
*
|
|
7348
|
+
* Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
|
|
7349
|
+
* table would otherwise be accepted by the codec and refused at the door, with
|
|
7350
|
+
* nothing failing until a caller asked.
|
|
7351
|
+
*/
|
|
7352
|
+
var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
|
|
7353
|
+
new Set(EVENT_OWNER_TYPES);
|
|
7337
7354
|
/**
|
|
7338
7355
|
* Build an `IAddonRouteProvider` from a list of routes. Implements
|
|
7339
7356
|
* both the operator-facing `getRoutes` (returning route descriptors
|
|
@@ -7489,7 +7506,23 @@ var EncodeProfileSchema = object({
|
|
|
7489
7506
|
"zerolatency",
|
|
7490
7507
|
"film",
|
|
7491
7508
|
"animation"
|
|
7492
|
-
]).optional()
|
|
7509
|
+
]).optional(),
|
|
7510
|
+
/**
|
|
7511
|
+
* ONE slice per access unit.
|
|
7512
|
+
*
|
|
7513
|
+
* `-tune zerolatency` turns on x264's sliced threads, and a frame then leaves
|
|
7514
|
+
* the encoder as five NAL slices that share one RTP timestamp and carry one
|
|
7515
|
+
* marker bit. A libwebrtc depacketiser sees five frame-starts and one
|
|
7516
|
+
* frame-end per frame: the first pictures render and the video then freezes
|
|
7517
|
+
* for good while the audio, on its own plane, plays on. Measured on this hub
|
|
7518
|
+
* 2026-09-13 against the Echo, and reduced to one slice by this flag alone.
|
|
7519
|
+
*
|
|
7520
|
+
* A NAMED field and not a raw flag, because {@link EgressEncodeSchema} omits
|
|
7521
|
+
* `outputArgs` on purpose: an opaque array is part of the sharing key, so two
|
|
7522
|
+
* consumers meaning the same thing spelled differently would stop sharing one
|
|
7523
|
+
* child. Absent means "whatever the encoder does" — today's behaviour.
|
|
7524
|
+
*/
|
|
7525
|
+
singleSlicePerFrame: boolean().optional()
|
|
7493
7526
|
}),
|
|
7494
7527
|
audio: union([literal("passthrough"), object({
|
|
7495
7528
|
codec: _enum([
|
|
@@ -7539,7 +7572,7 @@ var BASE_LIVE_EGRESS_PROFILE = {
|
|
|
7539
7572
|
audio: "passthrough"
|
|
7540
7573
|
};
|
|
7541
7574
|
({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
|
|
7542
|
-
({ ...BASE_LIVE_EGRESS_PROFILE });
|
|
7575
|
+
({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
|
|
7543
7576
|
/**
|
|
7544
7577
|
* Deep wiring healthcheck — snapshot of active reachability probes across
|
|
7545
7578
|
* every declared capability + widget of every installed plugin, on every
|
|
@@ -12588,6 +12621,16 @@ method(object({
|
|
|
12588
12621
|
filter: QueryFilterSchema.optional(),
|
|
12589
12622
|
columns: array(string()).readonly().optional()
|
|
12590
12623
|
}), array(SettingsRecordSchema).readonly()), method(object({
|
|
12624
|
+
namespace: string().optional(),
|
|
12625
|
+
collection: string(),
|
|
12626
|
+
/** Declared columns identifying the slot; must be covered by a UNIQUE
|
|
12627
|
+
* index on the collection. Never empty. */
|
|
12628
|
+
conflict: array(string()).readonly(),
|
|
12629
|
+
record: BulkRecordSchema
|
|
12630
|
+
}), object({
|
|
12631
|
+
/** The id the row HAS: assigned by SQLite on an insert, or the existing
|
|
12632
|
+
* row's own id on an update. */
|
|
12633
|
+
id: union([string(), number()]) }), { kind: "mutation" }), method(object({
|
|
12591
12634
|
namespace: string().optional(),
|
|
12592
12635
|
collection: string(),
|
|
12593
12636
|
record: object({
|
|
@@ -12722,6 +12765,14 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
|
|
|
12722
12765
|
id: union([string(), number()]) }), {
|
|
12723
12766
|
kind: "mutation",
|
|
12724
12767
|
auth: "admin"
|
|
12768
|
+
}), method(object({
|
|
12769
|
+
namespace: string().optional(),
|
|
12770
|
+
collection: string(),
|
|
12771
|
+
conflict: array(string()).readonly(),
|
|
12772
|
+
record: BulkRecordSchema
|
|
12773
|
+
}), object({ id: union([string(), number()]) }), {
|
|
12774
|
+
kind: "mutation",
|
|
12775
|
+
auth: "admin"
|
|
12725
12776
|
}), method(object({
|
|
12726
12777
|
namespace: string().optional(),
|
|
12727
12778
|
collection: string(),
|
|
@@ -18952,8 +19003,8 @@ var TrackPositionSchema = object({
|
|
|
18952
19003
|
var TrackSnapshotSchema = object({
|
|
18953
19004
|
timestamp: number(),
|
|
18954
19005
|
position: TrackPositionSchema,
|
|
18955
|
-
/**
|
|
18956
|
-
|
|
19006
|
+
/** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
|
|
19007
|
+
mediaId: number().int()
|
|
18957
19008
|
});
|
|
18958
19009
|
/**
|
|
18959
19010
|
* Normalized 0..1 trajectory envelope (min/max over every position bbox,
|
|
@@ -19557,14 +19608,14 @@ var ObjectEventSchema = object({
|
|
|
19557
19608
|
* pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
|
|
19558
19609
|
frameWidth: number().optional(),
|
|
19559
19610
|
frameHeight: number().optional(),
|
|
19560
|
-
/**
|
|
19561
|
-
|
|
19562
|
-
/** Design B:
|
|
19611
|
+
/** Media row id of the crop attached to this event (if any) — D482. */
|
|
19612
|
+
mediaId: number().int().optional(),
|
|
19613
|
+
/** Design B: media row id of the track's native-resolution key frame (the
|
|
19563
19614
|
* best-detection full frame). Resolve via the event-media data-plane
|
|
19564
|
-
* (`/addon/<addonId>/event-media/<
|
|
19615
|
+
* (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
|
|
19565
19616
|
* draws `bbox` over the native frame. Absent on legacy rows / non-decoded
|
|
19566
|
-
* sources — consumers fall back to `
|
|
19567
|
-
|
|
19617
|
+
* sources — consumers fall back to `mediaId` (the tight crop). */
|
|
19618
|
+
keyFrameMediaId: number().int().optional(),
|
|
19568
19619
|
/** Populated by B5 (recording playback URL for this event). */
|
|
19569
19620
|
mediaUrl: string().optional(),
|
|
19570
19621
|
/** The parent track's key-event importance [0,1], propagated to every object
|
|
@@ -19614,7 +19665,7 @@ var MediaFileKindEnum = _enum([
|
|
|
19614
19665
|
* hub-main's heap on the way past — for an `<img>` that would have cached it.
|
|
19615
19666
|
*
|
|
19616
19667
|
* `url` points at the `event-media` data plane
|
|
19617
|
-
* (`/addon/<addonId>/event-media/<
|
|
19668
|
+
* (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
|
|
19618
19669
|
* with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
|
|
19619
19670
|
* render a `?variant=thumb`, and streams. The hub gate in front of it requires
|
|
19620
19671
|
* a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
|
|
@@ -19622,10 +19673,13 @@ var MediaFileKindEnum = _enum([
|
|
|
19622
19673
|
* `data-plane-access.ts` for the rule and the one gap it does not close
|
|
19623
19674
|
* (per-device scoping).
|
|
19624
19675
|
*
|
|
19625
|
-
* The URL is built from the row's
|
|
19626
|
-
*
|
|
19676
|
+
* The URL is built from the row's own id, which since D482 says nothing about
|
|
19677
|
+
* the row's owner or kind — and that is the point. The published `kind` is not
|
|
19678
|
+
* the stored one (a track's face/plate crop is stored as `crop` under
|
|
19627
19679
|
* `('face'|'plate', '<prefix>-<trackId>')` and published as
|
|
19628
|
-
* `faceCrop`/`plateCrop
|
|
19680
|
+
* `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
|
|
19681
|
+
* taken from `row.key` verbatim or it 404'd exactly those two rows. An id
|
|
19682
|
+
* cannot be mis-reconstructed, because there is nothing in it to reconstruct.
|
|
19629
19683
|
*
|
|
19630
19684
|
* ## `base64` is TRANSITIONAL and is going away
|
|
19631
19685
|
*
|
|
@@ -19638,7 +19692,19 @@ var MediaFileKindEnum = _enum([
|
|
|
19638
19692
|
* `analytics-query-facade.ts`; nothing else reads it.
|
|
19639
19693
|
*/
|
|
19640
19694
|
var MediaFileSchema = object({
|
|
19641
|
-
|
|
19695
|
+
/**
|
|
19696
|
+
* The media row's OWN id — the rowid SQLite assigned it (D482).
|
|
19697
|
+
*
|
|
19698
|
+
* It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
|
|
19699
|
+
* [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
|
|
19700
|
+
* per-table rowid aliases, so `object:101314` and `motion:101314` are two
|
|
19701
|
+
* different rows that spell the same number, and a track whose
|
|
19702
|
+
* `bestEventId` was an object event was served a motion event's snapshot —
|
|
19703
|
+
* a night-time thumbnail on an afternoon track, with no error anywhere. A
|
|
19704
|
+
* surrogate id cannot name the wrong row's owner, because it names no owner
|
|
19705
|
+
* at all.
|
|
19706
|
+
*/
|
|
19707
|
+
mediaId: number().int(),
|
|
19642
19708
|
kind: MediaFileKindEnum,
|
|
19643
19709
|
sizeBytes: number(),
|
|
19644
19710
|
timestamp: number()
|
|
@@ -19746,7 +19812,7 @@ var RetrainTrackSchema = object({
|
|
|
19746
19812
|
});
|
|
19747
19813
|
/** A frame the picker may offer — an index row, no blob was read to produce it. */
|
|
19748
19814
|
var RetrainFrameCandidateSchema = object({
|
|
19749
|
-
|
|
19815
|
+
mediaId: number().int(),
|
|
19750
19816
|
kind: MediaFileKindEnum,
|
|
19751
19817
|
timestamp: number(),
|
|
19752
19818
|
sizeBytes: number().int(),
|
|
@@ -19760,7 +19826,7 @@ var RetrainFrameSchema = object({
|
|
|
19760
19826
|
deviceId: number(),
|
|
19761
19827
|
trackId: string(),
|
|
19762
19828
|
/** Provenance only. It may already point at nothing — that is expected. */
|
|
19763
|
-
|
|
19829
|
+
sourceMediaId: number().int(),
|
|
19764
19830
|
sourceKind: MediaFileKindEnum,
|
|
19765
19831
|
sizeBytes: number().int(),
|
|
19766
19832
|
width: number().int(),
|
|
@@ -19776,7 +19842,7 @@ var RetrainCopyRefusalSchema = _enum([
|
|
|
19776
19842
|
var RetrainFrameSelectionSchema = object({
|
|
19777
19843
|
copied: array(RetrainFrameSchema).readonly(),
|
|
19778
19844
|
refused: array(object({
|
|
19779
|
-
|
|
19845
|
+
sourceMediaId: number().int(),
|
|
19780
19846
|
reason: RetrainCopyRefusalSchema
|
|
19781
19847
|
})).readonly()
|
|
19782
19848
|
});
|
|
@@ -19784,7 +19850,7 @@ var RetrainFrameListSchema = object({
|
|
|
19784
19850
|
candidates: array(RetrainFrameCandidateSchema).readonly(),
|
|
19785
19851
|
copies: array(RetrainFrameSchema).readonly(),
|
|
19786
19852
|
/** What the page pre-selects — the native key frame when one survives. */
|
|
19787
|
-
|
|
19853
|
+
autoPickMediaId: number().int().optional()
|
|
19788
19854
|
});
|
|
19789
19855
|
/** What the operator asked the assist to look for. */
|
|
19790
19856
|
var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
|
|
@@ -19879,9 +19945,11 @@ var RecentTracksQueryInput = object({
|
|
|
19879
19945
|
});
|
|
19880
19946
|
/**
|
|
19881
19947
|
* A Summary (D359): the per-camera session envelope that references tracks.
|
|
19882
|
-
* `
|
|
19883
|
-
* served by the event-media plane
|
|
19884
|
-
* member visible has been materialised
|
|
19948
|
+
* `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
|
|
19949
|
+
* 'summary'`, served by the event-media plane at `/m/<id>`), null until the
|
|
19950
|
+
* first frame with a member visible has been materialised (D482 — it was the
|
|
19951
|
+
* row's composite key, which embedded the owner and could name another table's
|
|
19952
|
+
* row once event ids became per-table rowids).
|
|
19885
19953
|
*/
|
|
19886
19954
|
var SummarySchema = object({
|
|
19887
19955
|
id: string(),
|
|
@@ -19890,19 +19958,115 @@ var SummarySchema = object({
|
|
|
19890
19958
|
lastActiveAt: number(),
|
|
19891
19959
|
sealedAt: number().nullable(),
|
|
19892
19960
|
memberCount: number().int().nonnegative(),
|
|
19893
|
-
|
|
19961
|
+
groupShotMediaId: number().int().nullable()
|
|
19894
19962
|
});
|
|
19895
19963
|
var SummariesQueryInput = object({
|
|
19896
19964
|
deviceIds: array(number()).min(1),
|
|
19897
19965
|
since: number().optional(),
|
|
19898
19966
|
until: number().optional(),
|
|
19899
|
-
limit: number().int().min(1).max(200).default(50)
|
|
19967
|
+
limit: number().int().min(1).max(200).default(50),
|
|
19968
|
+
/** The same member-wise filter {@link listRecentSummaries} takes — one
|
|
19969
|
+
* meaning of "dog" on every summary surface. */
|
|
19970
|
+
classes: array(string().min(1).max(60)).max(40).optional()
|
|
19900
19971
|
});
|
|
19901
|
-
var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
|
|
19902
19972
|
var SummaryDetailSchema = object({
|
|
19903
19973
|
summary: SummarySchema,
|
|
19904
|
-
/**
|
|
19905
|
-
|
|
19974
|
+
/** Members in absorption order, oldest join first. */
|
|
19975
|
+
members: array(object({
|
|
19976
|
+
trackId: string(),
|
|
19977
|
+
/**
|
|
19978
|
+
* The track's class at the time it JOINED, or `null` when it was unresolved
|
|
19979
|
+
* and the track no longer exists to fill it.
|
|
19980
|
+
*
|
|
19981
|
+
* The join, not the latest opinion: class voting can re-decide a track's
|
|
19982
|
+
* class afterwards, and a group's composition is the history of who came
|
|
19983
|
+
* together. A consumer counts these to badge a thumbnail — "two people and a
|
|
19984
|
+
* dog" — rather than being handed a second, denormalised tally that would
|
|
19985
|
+
* drift from the members it claims to describe.
|
|
19986
|
+
*/
|
|
19987
|
+
className: string().nullable(),
|
|
19988
|
+
/** `primary` is the one member that carries the group's identity. */
|
|
19989
|
+
role: _enum([
|
|
19990
|
+
"primary",
|
|
19991
|
+
"sibling",
|
|
19992
|
+
"child"
|
|
19993
|
+
]).nullable(),
|
|
19994
|
+
joinedAt: number(),
|
|
19995
|
+
/** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
|
|
19996
|
+
* at read time. Absent when nothing resolved, never an empty string. */
|
|
19997
|
+
label: string().optional(),
|
|
19998
|
+
/** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
|
|
19999
|
+
* recognised nobody, and absent for a member whose track has been pruned —
|
|
20000
|
+
* a thumbnail that names the wrong person is worse than one naming nobody. */
|
|
20001
|
+
subLabel: string().optional()
|
|
20002
|
+
})).readonly()
|
|
20003
|
+
});
|
|
20004
|
+
/**
|
|
20005
|
+
* What a LIST of groups carries: the envelope plus what it is MADE OF.
|
|
20006
|
+
*
|
|
20007
|
+
* Coarse on purpose — the operator's rule is "only the rough data on the list,
|
|
20008
|
+
* we extend when we notice we need more". Enough to draw the card ("two people
|
|
20009
|
+
* and a dog") and no more. The MEMBERS, their names and the per-member links
|
|
20010
|
+
* are on {@link getSummary}: shipping every member of every group so a client
|
|
20011
|
+
* can count them turns a page of two hundred groups into a thousand records to
|
|
20012
|
+
* draw two hundred badges.
|
|
20013
|
+
*/
|
|
20014
|
+
var SummaryListItemSchema = object({
|
|
20015
|
+
summary: SummarySchema,
|
|
20016
|
+
/** Class counts, most numerous first; `className: null` is a member whose
|
|
20017
|
+
* class never resolved, counted so the counts add up to `memberCount`. */
|
|
20018
|
+
classes: array(object({
|
|
20019
|
+
className: string().nullable(),
|
|
20020
|
+
count: number().int().positive()
|
|
20021
|
+
})).readonly()
|
|
20022
|
+
});
|
|
20023
|
+
var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
|
|
20024
|
+
/**
|
|
20025
|
+
* The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
|
|
20026
|
+
*
|
|
20027
|
+
* The reel on the cameras page and the Events gallery both ride a cursor, so a
|
|
20028
|
+
* summaries feed that could only answer "the last N for these cameras"
|
|
20029
|
+
* (`listSummaries`) would make groups available in the timeline and nowhere
|
|
20030
|
+
* else. Two feeds with different ordering contracts would be two ideas of what
|
|
20031
|
+
* "newest" means, on one screen.
|
|
20032
|
+
*/
|
|
20033
|
+
var RecentSummariesQueryInput = object({
|
|
20034
|
+
/** Devices to merge. An empty array yields an empty page. */
|
|
20035
|
+
deviceIds: array(number()),
|
|
20036
|
+
/** Window lower bound on `lastActiveAt` (inclusive). */
|
|
20037
|
+
since: number().optional(),
|
|
20038
|
+
/** Window upper bound on `lastActiveAt` (inclusive). */
|
|
20039
|
+
until: number().optional(),
|
|
20040
|
+
limit: number().int().min(1).max(500).default(100),
|
|
20041
|
+
/** Opaque continuation cursor from a previous page's `nextCursor`. */
|
|
20042
|
+
cursor: string().optional(),
|
|
20043
|
+
/**
|
|
20044
|
+
* Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
|
|
20045
|
+
* deliberately the same name: the events filter tree emits one list of ticked
|
|
20046
|
+
* taxonomy nodes, and a summaries feed that called it something else would
|
|
20047
|
+
* make the operator's one filter component produce two shapes.
|
|
20048
|
+
*
|
|
20049
|
+
* A question about the MEMBERS: a group matches when ANY of them does. "Show
|
|
20050
|
+
* me the arrivals with a dog" has to return the group of four people who had
|
|
20051
|
+
* one with them; a filter on the group's own fields could only ever answer
|
|
20052
|
+
* about its primary.
|
|
20053
|
+
*
|
|
20054
|
+
* Matched against all THREE tiers of a member — the class (`animal`), the
|
|
20055
|
+
* sub-class (`dog`) and the instance (a name, a plate) — because which tier a
|
|
20056
|
+
* ticked node lives on is the taxonomy's business, not the caller's. `dog` is
|
|
20057
|
+
* an event kind in the tree and a tier-1 label on an `animal` track; a filter
|
|
20058
|
+
* that read only the class would answer "animal" and never "dog".
|
|
20059
|
+
*
|
|
20060
|
+
* ABSENT or EMPTY means no filter — absent is not a selection of none.
|
|
20061
|
+
*/
|
|
20062
|
+
classes: array(string().min(1).max(60)).max(40).optional()
|
|
20063
|
+
});
|
|
20064
|
+
var RecentSummariesPageSchema = object({
|
|
20065
|
+
/** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
|
|
20066
|
+
* {@link listSummaries} — one list shape, so a card renders the same
|
|
20067
|
+
* whichever feed drew it. */
|
|
20068
|
+
summaries: array(SummaryListItemSchema).readonly(),
|
|
20069
|
+
nextCursor: string().nullable()
|
|
19906
20070
|
});
|
|
19907
20071
|
var RecentTracksPageSchema = object({
|
|
19908
20072
|
/** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
|
|
@@ -20316,7 +20480,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
20316
20480
|
classes: array(string()).optional(),
|
|
20317
20481
|
/** See {@link ExcludeSourcesDoc}. */
|
|
20318
20482
|
excludeSources: array(TrackSourceSchema).optional()
|
|
20319
|
-
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
|
|
20483
|
+
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
|
|
20320
20484
|
kind: "mutation",
|
|
20321
20485
|
auth: "admin"
|
|
20322
20486
|
}), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
|
|
@@ -20444,7 +20608,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
20444
20608
|
}), method(object({
|
|
20445
20609
|
deviceId: number(),
|
|
20446
20610
|
trackId: string(),
|
|
20447
|
-
|
|
20611
|
+
mediaIds: array(number().int()).min(1)
|
|
20448
20612
|
}), RetrainFrameSelectionSchema, {
|
|
20449
20613
|
kind: "mutation",
|
|
20450
20614
|
auth: "admin"
|
|
@@ -20512,7 +20676,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
20512
20676
|
kind: "query",
|
|
20513
20677
|
auth: "admin"
|
|
20514
20678
|
}), method(object({
|
|
20515
|
-
|
|
20679
|
+
/** Which of the three event tables owns the row. */
|
|
20680
|
+
ownerType: EventOwnerTypeSchema,
|
|
20681
|
+
/** The event's rowid in that table (D474: an event id is a number). */
|
|
20682
|
+
eventId: number().int(),
|
|
20516
20683
|
kind: MediaFileKindEnum.optional(),
|
|
20517
20684
|
deviceId: number()
|
|
20518
20685
|
}), array(MediaFileSchema).readonly()), method(object({
|
|
@@ -20523,7 +20690,8 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
20523
20690
|
trackId: string(),
|
|
20524
20691
|
deviceId: number()
|
|
20525
20692
|
}), array(MediaFileInfoSchema).readonly()), method(object({
|
|
20526
|
-
|
|
20693
|
+
ownerType: EventOwnerTypeSchema,
|
|
20694
|
+
eventId: number().int(),
|
|
20527
20695
|
deviceId: number()
|
|
20528
20696
|
}), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
|
|
20529
20697
|
kind: "mutation",
|
|
@@ -24986,8 +25154,8 @@ var IdentitySchema = object({
|
|
|
24986
25154
|
id: string(),
|
|
24987
25155
|
name: string(),
|
|
24988
25156
|
sampleCount: number().int().nonnegative(),
|
|
24989
|
-
|
|
24990
|
-
/** Inline base64 of the cover sample's crop, resolved from `
|
|
25157
|
+
coverMediaId: number().int().optional(),
|
|
25158
|
+
/** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
|
|
24991
25159
|
* Lets the UI render the person's face avatar instead of a placeholder icon. */
|
|
24992
25160
|
coverBase64: string().optional()
|
|
24993
25161
|
});
|
|
@@ -25013,8 +25181,8 @@ var IdentitySampleAuditRowSchema = object({
|
|
|
25013
25181
|
enrolledAt: number().int(),
|
|
25014
25182
|
deviceId: number().int().optional(),
|
|
25015
25183
|
source: _enum(["detected", "photo"]),
|
|
25016
|
-
/** The enrolled crop's media
|
|
25017
|
-
|
|
25184
|
+
/** The enrolled crop's media row id, so the panel can show the evidence. */
|
|
25185
|
+
mediaId: number().int().optional(),
|
|
25018
25186
|
/**
|
|
25019
25187
|
* False = the sample is stamped with a DIFFERENT face model, and a cosine
|
|
25020
25188
|
* across feature spaces is a well-formed float with no meaning. Every number
|
|
@@ -25083,15 +25251,15 @@ var FaceInfoSchema = object({
|
|
|
25083
25251
|
*/
|
|
25084
25252
|
cropUrl: string().optional(),
|
|
25085
25253
|
/** Design B: the face bbox (pixel space) on the key frame — lets a detail
|
|
25086
|
-
* view draw the box over the native `
|
|
25254
|
+
* view draw the box over the native `keyFrameMediaId` frame. Absent on
|
|
25087
25255
|
* legacy rows written before design B. */
|
|
25088
25256
|
faceBbox: BoundingBoxSchema.optional(),
|
|
25089
25257
|
/** Design B: MediaStore key of the track's native-resolution key frame.
|
|
25090
25258
|
* Fetch the native JPEG via the event-media data-plane
|
|
25091
|
-
* (`/addon/<addonId>/event-media/<
|
|
25259
|
+
* (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
|
|
25092
25260
|
* track produced no key frame (e.g. native/onboard source) — the UI falls
|
|
25093
25261
|
* back to the inline `base64` face crop. */
|
|
25094
|
-
|
|
25262
|
+
keyFrameMediaId: number().int().optional(),
|
|
25095
25263
|
/** Winning identity-match cosine (0..1) for this face's track, when an
|
|
25096
25264
|
* identity was auto-confirmed. Lets the UI surface WHY a face was assigned
|
|
25097
25265
|
* (confidence badge / low-confidence audit). Absent on legacy rows and on
|
|
@@ -25160,11 +25328,14 @@ var FaceClusterSchema = object({
|
|
|
25160
25328
|
* `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
|
|
25161
25329
|
* dialog already rendering its key FRAME from the `event-media` plane.
|
|
25162
25330
|
*
|
|
25163
|
-
* `url` is `/addon/<addonId>/event-media/<
|
|
25164
|
-
* media
|
|
25331
|
+
* `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
|
|
25332
|
+
* resolves a media row by its own id, so this needed no new plane and no new
|
|
25333
|
+
* access decision.
|
|
25165
25334
|
*/
|
|
25166
25335
|
var MediaFileLiteSchema$1 = object({
|
|
25167
|
-
|
|
25336
|
+
/** The media row's own id — the same address `MediaFileRef.mediaId` carries
|
|
25337
|
+
* (D482). It was the row's composite key, which embedded the owner. */
|
|
25338
|
+
mediaId: number().int(),
|
|
25168
25339
|
kind: string(),
|
|
25169
25340
|
url: string(),
|
|
25170
25341
|
sizeBytes: number(),
|
|
@@ -25181,7 +25352,7 @@ method(object({
|
|
|
25181
25352
|
* covers inline, ~10 KB of base64 per row, on a query this UI mounts
|
|
25182
25353
|
* four times and the viewer holds at `staleTime: 30_000`.
|
|
25183
25354
|
*
|
|
25184
|
-
* Nothing loses its avatar: `
|
|
25355
|
+
* Nothing loses its avatar: `coverMediaId` is already on every row and
|
|
25185
25356
|
* the `event-media` plane serves that key `immutable` with an ETag.
|
|
25186
25357
|
*
|
|
25187
25358
|
* **This is an INPUT field, so it does not reach the addon until the
|
|
@@ -27649,8 +27820,8 @@ var VehicleSchema = object({
|
|
|
27649
27820
|
id: string(),
|
|
27650
27821
|
name: string(),
|
|
27651
27822
|
sampleCount: number().int().nonnegative(),
|
|
27652
|
-
|
|
27653
|
-
/** Inline base64 of the cover sample's plate crop, resolved from `
|
|
27823
|
+
coverMediaId: number().int().optional(),
|
|
27824
|
+
/** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
|
|
27654
27825
|
coverBase64: string().optional()
|
|
27655
27826
|
});
|
|
27656
27827
|
var VehicleSampleInfoSchema = object({
|
|
@@ -27683,7 +27854,7 @@ var PlateInfoSchema = object({
|
|
|
27683
27854
|
/** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
|
|
27684
27855
|
plateBbox: BoundingBoxSchema.optional(),
|
|
27685
27856
|
/** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
|
|
27686
|
-
|
|
27857
|
+
keyFrameMediaId: number().int().optional(),
|
|
27687
27858
|
base64: string().optional(),
|
|
27688
27859
|
/**
|
|
27689
27860
|
* Same crop as a data-plane URL, always present when the plate has a stored
|
|
@@ -27704,11 +27875,14 @@ var PlateInfoSchema = object({
|
|
|
27704
27875
|
* `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
|
|
27705
27876
|
* dialog already rendering its key FRAME from the `event-media` plane.
|
|
27706
27877
|
*
|
|
27707
|
-
* `url` is `/addon/<addonId>/event-media/<
|
|
27708
|
-
* media
|
|
27878
|
+
* `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
|
|
27879
|
+
* resolves a media row by its own id, so this needed no new plane and no new
|
|
27880
|
+
* access decision.
|
|
27709
27881
|
*/
|
|
27710
27882
|
var MediaFileLiteSchema = object({
|
|
27711
|
-
|
|
27883
|
+
/** The media row's own id — the same address `MediaFileRef.mediaId` carries
|
|
27884
|
+
* (D482). It was the row's composite key, which embedded the owner. */
|
|
27885
|
+
mediaId: number().int(),
|
|
27712
27886
|
kind: string(),
|
|
27713
27887
|
url: string(),
|
|
27714
27888
|
sizeBytes: number(),
|
|
@@ -27767,7 +27941,7 @@ method(object({
|
|
|
27767
27941
|
}), method(object({
|
|
27768
27942
|
/** Inline {@link VehicleSchema.coverBase64} on every row. Default
|
|
27769
27943
|
* `false` — the vehicle twin of `faceGallery.listIdentities`'s
|
|
27770
|
-
* option; `
|
|
27944
|
+
* option; `coverMediaId` + the `event-media` plane carry the picture.
|
|
27771
27945
|
* INPUT field: stripped by the hub router until the train ships, which
|
|
27772
27946
|
* resolves to `false` and is exactly the intended default. */
|
|
27773
27947
|
includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
|
|
@@ -29091,7 +29265,14 @@ var SceneReferenceSchema = object({
|
|
|
29091
29265
|
modelId: string(),
|
|
29092
29266
|
condition: SceneConditionSchema,
|
|
29093
29267
|
capturedAt: number(),
|
|
29094
|
-
|
|
29268
|
+
/**
|
|
29269
|
+
* The reference thumbnail's media ROW id (D482).
|
|
29270
|
+
*
|
|
29271
|
+
* The name already said `mediaId` while the value was the row's composite
|
|
29272
|
+
* key — a leftover that read as done and was not. It is the row's own id
|
|
29273
|
+
* now, addressed at `/addon/<addonId>/event-media/m/<id>`.
|
|
29274
|
+
*/
|
|
29275
|
+
thumbnailMediaId: number().int().optional(),
|
|
29095
29276
|
/** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
|
|
29096
29277
|
* anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
|
|
29097
29278
|
* normalized rect frame a different piece of world, and the scene would
|
|
@@ -29905,7 +30086,29 @@ var LoggingSettingsPatchSchema = object({
|
|
|
29905
30086
|
* disarmed the channels it did not mention would make the Levels page and
|
|
29906
30087
|
* the Diagnostics page fight over the same value.
|
|
29907
30088
|
*/
|
|
29908
|
-
channels: array(LogChannelWindowPatchSchema).readonly().optional()
|
|
30089
|
+
channels: array(LogChannelWindowPatchSchema).readonly().optional(),
|
|
30090
|
+
/**
|
|
30091
|
+
* How many days the analytics OPS LOG is kept — the audit trail of what
|
|
30092
|
+
* retention actually deleted.
|
|
30093
|
+
*
|
|
30094
|
+
* It lives HERE, on the logging document, and not on the analytics addon's
|
|
30095
|
+
* settings form, on the operator's instruction: every other log-ish window in
|
|
30096
|
+
* this system is configured through this one document, and a retention audit
|
|
30097
|
+
* configured somewhere else is a second place to look when the question is
|
|
30098
|
+
* "what happened to my data".
|
|
30099
|
+
*
|
|
30100
|
+
* That placement has a cost worth stating: this document ships in the
|
|
30101
|
+
* framework closure, so this knob needs a published `@camstack/server` while
|
|
30102
|
+
* the four windows beside it (D485) reach a node on `camstack deploy`.
|
|
30103
|
+
*
|
|
30104
|
+
* `retention-model.ts` argued this one must NOT follow a device window,
|
|
30105
|
+
* because the audit trail is the history of the SWEEP and not of the camera.
|
|
30106
|
+
* That argument stands and is untouched — what changes is that 30 days stops
|
|
30107
|
+
* being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
|
|
30108
|
+
* rather than 0-as-delete: an audit log truncated to nothing is precisely the
|
|
30109
|
+
* state in which the next 9.5 GiB leak cannot be explained.
|
|
30110
|
+
*/
|
|
30111
|
+
opsLogRetentionDays: number().int().min(0).max(3650).optional()
|
|
29909
30112
|
});
|
|
29910
30113
|
/**
|
|
29911
30114
|
* Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
|
|
@@ -29978,6 +30181,14 @@ var LoggingSettingsStateSchema = object({
|
|
|
29978
30181
|
channels: array(LogChannelDescriptorSchema).readonly(),
|
|
29979
30182
|
/** The channels ARMED right now, each with its deadline. */
|
|
29980
30183
|
activeChannels: array(LogChannelWindowStateSchema).readonly(),
|
|
30184
|
+
/**
|
|
30185
|
+
* The ops-log window in days, as it is in force. `0` = kept forever.
|
|
30186
|
+
*
|
|
30187
|
+
* Reported rather than left to the caller's memory of what it wrote: this is
|
|
30188
|
+
* the value the analytics sweep will actually use, and an operator asking how
|
|
30189
|
+
* long their audit trail lives should not have to infer it from a default.
|
|
30190
|
+
*/
|
|
30191
|
+
opsLogRetentionDays: number().int().min(0),
|
|
29981
30192
|
persisted: boolean()
|
|
29982
30193
|
});
|
|
29983
30194
|
method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
|
|
@@ -30539,7 +30750,7 @@ var StationaryObjectSchema = object({
|
|
|
30539
30750
|
/** Enrichment label carried from the source track (identity / plate). */
|
|
30540
30751
|
label: string().optional(),
|
|
30541
30752
|
/** Native-resolution key-frame media key for the parked object's best image. */
|
|
30542
|
-
|
|
30753
|
+
keyFrameMediaId: number().int().optional()
|
|
30543
30754
|
});
|
|
30544
30755
|
var CameraOccupancySnapshotSchema = object({
|
|
30545
30756
|
/** Frame timestamp of the inference result that produced this snapshot. */
|
|
@@ -31695,6 +31906,12 @@ Object.freeze({
|
|
|
31695
31906
|
addonId: null,
|
|
31696
31907
|
access: "create"
|
|
31697
31908
|
},
|
|
31909
|
+
"dataStoreProvider.upsert": {
|
|
31910
|
+
capName: "data-store-provider",
|
|
31911
|
+
capScope: "system",
|
|
31912
|
+
addonId: null,
|
|
31913
|
+
access: "create"
|
|
31914
|
+
},
|
|
31698
31915
|
"dayNight.getOptions": {
|
|
31699
31916
|
capName: "day-night",
|
|
31700
31917
|
capScope: "device",
|
|
@@ -34131,6 +34348,12 @@ Object.freeze({
|
|
|
34131
34348
|
addonId: null,
|
|
34132
34349
|
access: "view"
|
|
34133
34350
|
},
|
|
34351
|
+
"pipelineAnalytics.listRecentSummaries": {
|
|
34352
|
+
capName: "pipeline-analytics",
|
|
34353
|
+
capScope: "device",
|
|
34354
|
+
addonId: null,
|
|
34355
|
+
access: "view"
|
|
34356
|
+
},
|
|
34134
34357
|
"pipelineAnalytics.listRecentTracks": {
|
|
34135
34358
|
capName: "pipeline-analytics",
|
|
34136
34359
|
capScope: "device",
|
|
@@ -35559,6 +35782,12 @@ Object.freeze({
|
|
|
35559
35782
|
addonId: null,
|
|
35560
35783
|
access: "create"
|
|
35561
35784
|
},
|
|
35785
|
+
"settingsStore.upsert": {
|
|
35786
|
+
capName: "settings-store",
|
|
35787
|
+
capScope: "system",
|
|
35788
|
+
addonId: null,
|
|
35789
|
+
access: "create"
|
|
35790
|
+
},
|
|
35562
35791
|
"smtpProvider.getStatus": {
|
|
35563
35792
|
capName: "smtp-provider",
|
|
35564
35793
|
capScope: "system",
|
|
@@ -37952,6 +38181,11 @@ Object.freeze({
|
|
|
37952
38181
|
form: "single",
|
|
37953
38182
|
optional: true
|
|
37954
38183
|
}],
|
|
38184
|
+
"pipelineAnalytics.listRecentSummaries": [{
|
|
38185
|
+
name: "deviceIds",
|
|
38186
|
+
form: "array",
|
|
38187
|
+
optional: false
|
|
38188
|
+
}],
|
|
37955
38189
|
"pipelineAnalytics.listRecentTracks": [{
|
|
37956
38190
|
name: "deviceIds",
|
|
37957
38191
|
form: "array",
|
package/dist/addon.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { brotliCompressSync, deflateSync, gzipSync } from "node:zlib";
|
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
//#region ../types/dist/event-category-
|
|
5
|
+
//#region ../types/dist/event-category-ZyX6jcse.mjs
|
|
6
6
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
7
7
|
EventCategory["SystemBoot"] = "system.boot";
|
|
8
8
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -563,7 +563,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
563
563
|
* a package zone — a newly-appeared stationary object of a package class
|
|
564
564
|
* that cleared the class / zone / dwell / size gates. Payload:
|
|
565
565
|
* `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
|
|
566
|
-
* entryId, className, zoneIds,
|
|
566
|
+
* entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
|
|
567
567
|
* Telemetry (D8): the durable record is the `package-events` store row;
|
|
568
568
|
* this bus topic drives notifier rules + live UI. See
|
|
569
569
|
* docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
|
|
@@ -5349,7 +5349,7 @@ var ZodIssueCode = {
|
|
|
5349
5349
|
var ZodFirstPartyTypeKind;
|
|
5350
5350
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5351
5351
|
//#endregion
|
|
5352
|
-
//#region ../types/dist/sleep-
|
|
5352
|
+
//#region ../types/dist/sleep-DBKu2-U5.mjs
|
|
5353
5353
|
/**
|
|
5354
5354
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5355
5355
|
* window to float samples (D455).
|
|
@@ -7300,13 +7300,30 @@ new Set([
|
|
|
7300
7300
|
"scene",
|
|
7301
7301
|
"motion",
|
|
7302
7302
|
"object",
|
|
7303
|
-
"audio"
|
|
7303
|
+
"audio",
|
|
7304
|
+
"mosaic"
|
|
7304
7305
|
]);
|
|
7305
|
-
|
|
7306
|
+
/**
|
|
7307
|
+
* The owner types that are event tables, in the order the event-media resolver
|
|
7308
|
+
* should consider them. Exported so a consumer asking "is this key an event?"
|
|
7309
|
+
* does not re-spell the list and drift from it.
|
|
7310
|
+
*/
|
|
7311
|
+
var EVENT_OWNER_TYPES = [
|
|
7306
7312
|
"motion",
|
|
7307
7313
|
"object",
|
|
7308
7314
|
"audio"
|
|
7309
|
-
]
|
|
7315
|
+
];
|
|
7316
|
+
/**
|
|
7317
|
+
* The same list as a Zod enum, for the cap inputs that must NAME the table
|
|
7318
|
+
* (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
|
|
7319
|
+
* `(table, id)`).
|
|
7320
|
+
*
|
|
7321
|
+
* Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
|
|
7322
|
+
* table would otherwise be accepted by the codec and refused at the door, with
|
|
7323
|
+
* nothing failing until a caller asked.
|
|
7324
|
+
*/
|
|
7325
|
+
var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
|
|
7326
|
+
new Set(EVENT_OWNER_TYPES);
|
|
7310
7327
|
/**
|
|
7311
7328
|
* Build an `IAddonRouteProvider` from a list of routes. Implements
|
|
7312
7329
|
* both the operator-facing `getRoutes` (returning route descriptors
|
|
@@ -7462,7 +7479,23 @@ var EncodeProfileSchema = object({
|
|
|
7462
7479
|
"zerolatency",
|
|
7463
7480
|
"film",
|
|
7464
7481
|
"animation"
|
|
7465
|
-
]).optional()
|
|
7482
|
+
]).optional(),
|
|
7483
|
+
/**
|
|
7484
|
+
* ONE slice per access unit.
|
|
7485
|
+
*
|
|
7486
|
+
* `-tune zerolatency` turns on x264's sliced threads, and a frame then leaves
|
|
7487
|
+
* the encoder as five NAL slices that share one RTP timestamp and carry one
|
|
7488
|
+
* marker bit. A libwebrtc depacketiser sees five frame-starts and one
|
|
7489
|
+
* frame-end per frame: the first pictures render and the video then freezes
|
|
7490
|
+
* for good while the audio, on its own plane, plays on. Measured on this hub
|
|
7491
|
+
* 2026-09-13 against the Echo, and reduced to one slice by this flag alone.
|
|
7492
|
+
*
|
|
7493
|
+
* A NAMED field and not a raw flag, because {@link EgressEncodeSchema} omits
|
|
7494
|
+
* `outputArgs` on purpose: an opaque array is part of the sharing key, so two
|
|
7495
|
+
* consumers meaning the same thing spelled differently would stop sharing one
|
|
7496
|
+
* child. Absent means "whatever the encoder does" — today's behaviour.
|
|
7497
|
+
*/
|
|
7498
|
+
singleSlicePerFrame: boolean().optional()
|
|
7466
7499
|
}),
|
|
7467
7500
|
audio: union([literal("passthrough"), object({
|
|
7468
7501
|
codec: _enum([
|
|
@@ -7512,7 +7545,7 @@ var BASE_LIVE_EGRESS_PROFILE = {
|
|
|
7512
7545
|
audio: "passthrough"
|
|
7513
7546
|
};
|
|
7514
7547
|
({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
|
|
7515
|
-
({ ...BASE_LIVE_EGRESS_PROFILE });
|
|
7548
|
+
({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
|
|
7516
7549
|
/**
|
|
7517
7550
|
* Deep wiring healthcheck — snapshot of active reachability probes across
|
|
7518
7551
|
* every declared capability + widget of every installed plugin, on every
|
|
@@ -12561,6 +12594,16 @@ method(object({
|
|
|
12561
12594
|
filter: QueryFilterSchema.optional(),
|
|
12562
12595
|
columns: array(string()).readonly().optional()
|
|
12563
12596
|
}), array(SettingsRecordSchema).readonly()), method(object({
|
|
12597
|
+
namespace: string().optional(),
|
|
12598
|
+
collection: string(),
|
|
12599
|
+
/** Declared columns identifying the slot; must be covered by a UNIQUE
|
|
12600
|
+
* index on the collection. Never empty. */
|
|
12601
|
+
conflict: array(string()).readonly(),
|
|
12602
|
+
record: BulkRecordSchema
|
|
12603
|
+
}), object({
|
|
12604
|
+
/** The id the row HAS: assigned by SQLite on an insert, or the existing
|
|
12605
|
+
* row's own id on an update. */
|
|
12606
|
+
id: union([string(), number()]) }), { kind: "mutation" }), method(object({
|
|
12564
12607
|
namespace: string().optional(),
|
|
12565
12608
|
collection: string(),
|
|
12566
12609
|
record: object({
|
|
@@ -12695,6 +12738,14 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
|
|
|
12695
12738
|
id: union([string(), number()]) }), {
|
|
12696
12739
|
kind: "mutation",
|
|
12697
12740
|
auth: "admin"
|
|
12741
|
+
}), method(object({
|
|
12742
|
+
namespace: string().optional(),
|
|
12743
|
+
collection: string(),
|
|
12744
|
+
conflict: array(string()).readonly(),
|
|
12745
|
+
record: BulkRecordSchema
|
|
12746
|
+
}), object({ id: union([string(), number()]) }), {
|
|
12747
|
+
kind: "mutation",
|
|
12748
|
+
auth: "admin"
|
|
12698
12749
|
}), method(object({
|
|
12699
12750
|
namespace: string().optional(),
|
|
12700
12751
|
collection: string(),
|
|
@@ -18925,8 +18976,8 @@ var TrackPositionSchema = object({
|
|
|
18925
18976
|
var TrackSnapshotSchema = object({
|
|
18926
18977
|
timestamp: number(),
|
|
18927
18978
|
position: TrackPositionSchema,
|
|
18928
|
-
/**
|
|
18929
|
-
|
|
18979
|
+
/** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
|
|
18980
|
+
mediaId: number().int()
|
|
18930
18981
|
});
|
|
18931
18982
|
/**
|
|
18932
18983
|
* Normalized 0..1 trajectory envelope (min/max over every position bbox,
|
|
@@ -19530,14 +19581,14 @@ var ObjectEventSchema = object({
|
|
|
19530
19581
|
* pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
|
|
19531
19582
|
frameWidth: number().optional(),
|
|
19532
19583
|
frameHeight: number().optional(),
|
|
19533
|
-
/**
|
|
19534
|
-
|
|
19535
|
-
/** Design B:
|
|
19584
|
+
/** Media row id of the crop attached to this event (if any) — D482. */
|
|
19585
|
+
mediaId: number().int().optional(),
|
|
19586
|
+
/** Design B: media row id of the track's native-resolution key frame (the
|
|
19536
19587
|
* best-detection full frame). Resolve via the event-media data-plane
|
|
19537
|
-
* (`/addon/<addonId>/event-media/<
|
|
19588
|
+
* (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
|
|
19538
19589
|
* draws `bbox` over the native frame. Absent on legacy rows / non-decoded
|
|
19539
|
-
* sources — consumers fall back to `
|
|
19540
|
-
|
|
19590
|
+
* sources — consumers fall back to `mediaId` (the tight crop). */
|
|
19591
|
+
keyFrameMediaId: number().int().optional(),
|
|
19541
19592
|
/** Populated by B5 (recording playback URL for this event). */
|
|
19542
19593
|
mediaUrl: string().optional(),
|
|
19543
19594
|
/** The parent track's key-event importance [0,1], propagated to every object
|
|
@@ -19587,7 +19638,7 @@ var MediaFileKindEnum = _enum([
|
|
|
19587
19638
|
* hub-main's heap on the way past — for an `<img>` that would have cached it.
|
|
19588
19639
|
*
|
|
19589
19640
|
* `url` points at the `event-media` data plane
|
|
19590
|
-
* (`/addon/<addonId>/event-media/<
|
|
19641
|
+
* (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
|
|
19591
19642
|
* with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
|
|
19592
19643
|
* render a `?variant=thumb`, and streams. The hub gate in front of it requires
|
|
19593
19644
|
* a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
|
|
@@ -19595,10 +19646,13 @@ var MediaFileKindEnum = _enum([
|
|
|
19595
19646
|
* `data-plane-access.ts` for the rule and the one gap it does not close
|
|
19596
19647
|
* (per-device scoping).
|
|
19597
19648
|
*
|
|
19598
|
-
* The URL is built from the row's
|
|
19599
|
-
*
|
|
19649
|
+
* The URL is built from the row's own id, which since D482 says nothing about
|
|
19650
|
+
* the row's owner or kind — and that is the point. The published `kind` is not
|
|
19651
|
+
* the stored one (a track's face/plate crop is stored as `crop` under
|
|
19600
19652
|
* `('face'|'plate', '<prefix>-<trackId>')` and published as
|
|
19601
|
-
* `faceCrop`/`plateCrop
|
|
19653
|
+
* `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
|
|
19654
|
+
* taken from `row.key` verbatim or it 404'd exactly those two rows. An id
|
|
19655
|
+
* cannot be mis-reconstructed, because there is nothing in it to reconstruct.
|
|
19602
19656
|
*
|
|
19603
19657
|
* ## `base64` is TRANSITIONAL and is going away
|
|
19604
19658
|
*
|
|
@@ -19611,7 +19665,19 @@ var MediaFileKindEnum = _enum([
|
|
|
19611
19665
|
* `analytics-query-facade.ts`; nothing else reads it.
|
|
19612
19666
|
*/
|
|
19613
19667
|
var MediaFileSchema = object({
|
|
19614
|
-
|
|
19668
|
+
/**
|
|
19669
|
+
* The media row's OWN id — the rowid SQLite assigned it (D482).
|
|
19670
|
+
*
|
|
19671
|
+
* It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
|
|
19672
|
+
* [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
|
|
19673
|
+
* per-table rowid aliases, so `object:101314` and `motion:101314` are two
|
|
19674
|
+
* different rows that spell the same number, and a track whose
|
|
19675
|
+
* `bestEventId` was an object event was served a motion event's snapshot —
|
|
19676
|
+
* a night-time thumbnail on an afternoon track, with no error anywhere. A
|
|
19677
|
+
* surrogate id cannot name the wrong row's owner, because it names no owner
|
|
19678
|
+
* at all.
|
|
19679
|
+
*/
|
|
19680
|
+
mediaId: number().int(),
|
|
19615
19681
|
kind: MediaFileKindEnum,
|
|
19616
19682
|
sizeBytes: number(),
|
|
19617
19683
|
timestamp: number()
|
|
@@ -19719,7 +19785,7 @@ var RetrainTrackSchema = object({
|
|
|
19719
19785
|
});
|
|
19720
19786
|
/** A frame the picker may offer — an index row, no blob was read to produce it. */
|
|
19721
19787
|
var RetrainFrameCandidateSchema = object({
|
|
19722
|
-
|
|
19788
|
+
mediaId: number().int(),
|
|
19723
19789
|
kind: MediaFileKindEnum,
|
|
19724
19790
|
timestamp: number(),
|
|
19725
19791
|
sizeBytes: number().int(),
|
|
@@ -19733,7 +19799,7 @@ var RetrainFrameSchema = object({
|
|
|
19733
19799
|
deviceId: number(),
|
|
19734
19800
|
trackId: string(),
|
|
19735
19801
|
/** Provenance only. It may already point at nothing — that is expected. */
|
|
19736
|
-
|
|
19802
|
+
sourceMediaId: number().int(),
|
|
19737
19803
|
sourceKind: MediaFileKindEnum,
|
|
19738
19804
|
sizeBytes: number().int(),
|
|
19739
19805
|
width: number().int(),
|
|
@@ -19749,7 +19815,7 @@ var RetrainCopyRefusalSchema = _enum([
|
|
|
19749
19815
|
var RetrainFrameSelectionSchema = object({
|
|
19750
19816
|
copied: array(RetrainFrameSchema).readonly(),
|
|
19751
19817
|
refused: array(object({
|
|
19752
|
-
|
|
19818
|
+
sourceMediaId: number().int(),
|
|
19753
19819
|
reason: RetrainCopyRefusalSchema
|
|
19754
19820
|
})).readonly()
|
|
19755
19821
|
});
|
|
@@ -19757,7 +19823,7 @@ var RetrainFrameListSchema = object({
|
|
|
19757
19823
|
candidates: array(RetrainFrameCandidateSchema).readonly(),
|
|
19758
19824
|
copies: array(RetrainFrameSchema).readonly(),
|
|
19759
19825
|
/** What the page pre-selects — the native key frame when one survives. */
|
|
19760
|
-
|
|
19826
|
+
autoPickMediaId: number().int().optional()
|
|
19761
19827
|
});
|
|
19762
19828
|
/** What the operator asked the assist to look for. */
|
|
19763
19829
|
var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
|
|
@@ -19852,9 +19918,11 @@ var RecentTracksQueryInput = object({
|
|
|
19852
19918
|
});
|
|
19853
19919
|
/**
|
|
19854
19920
|
* A Summary (D359): the per-camera session envelope that references tracks.
|
|
19855
|
-
* `
|
|
19856
|
-
* served by the event-media plane
|
|
19857
|
-
* member visible has been materialised
|
|
19921
|
+
* `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
|
|
19922
|
+
* 'summary'`, served by the event-media plane at `/m/<id>`), null until the
|
|
19923
|
+
* first frame with a member visible has been materialised (D482 — it was the
|
|
19924
|
+
* row's composite key, which embedded the owner and could name another table's
|
|
19925
|
+
* row once event ids became per-table rowids).
|
|
19858
19926
|
*/
|
|
19859
19927
|
var SummarySchema = object({
|
|
19860
19928
|
id: string(),
|
|
@@ -19863,19 +19931,115 @@ var SummarySchema = object({
|
|
|
19863
19931
|
lastActiveAt: number(),
|
|
19864
19932
|
sealedAt: number().nullable(),
|
|
19865
19933
|
memberCount: number().int().nonnegative(),
|
|
19866
|
-
|
|
19934
|
+
groupShotMediaId: number().int().nullable()
|
|
19867
19935
|
});
|
|
19868
19936
|
var SummariesQueryInput = object({
|
|
19869
19937
|
deviceIds: array(number()).min(1),
|
|
19870
19938
|
since: number().optional(),
|
|
19871
19939
|
until: number().optional(),
|
|
19872
|
-
limit: number().int().min(1).max(200).default(50)
|
|
19940
|
+
limit: number().int().min(1).max(200).default(50),
|
|
19941
|
+
/** The same member-wise filter {@link listRecentSummaries} takes — one
|
|
19942
|
+
* meaning of "dog" on every summary surface. */
|
|
19943
|
+
classes: array(string().min(1).max(60)).max(40).optional()
|
|
19873
19944
|
});
|
|
19874
|
-
var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
|
|
19875
19945
|
var SummaryDetailSchema = object({
|
|
19876
19946
|
summary: SummarySchema,
|
|
19877
|
-
/**
|
|
19878
|
-
|
|
19947
|
+
/** Members in absorption order, oldest join first. */
|
|
19948
|
+
members: array(object({
|
|
19949
|
+
trackId: string(),
|
|
19950
|
+
/**
|
|
19951
|
+
* The track's class at the time it JOINED, or `null` when it was unresolved
|
|
19952
|
+
* and the track no longer exists to fill it.
|
|
19953
|
+
*
|
|
19954
|
+
* The join, not the latest opinion: class voting can re-decide a track's
|
|
19955
|
+
* class afterwards, and a group's composition is the history of who came
|
|
19956
|
+
* together. A consumer counts these to badge a thumbnail — "two people and a
|
|
19957
|
+
* dog" — rather than being handed a second, denormalised tally that would
|
|
19958
|
+
* drift from the members it claims to describe.
|
|
19959
|
+
*/
|
|
19960
|
+
className: string().nullable(),
|
|
19961
|
+
/** `primary` is the one member that carries the group's identity. */
|
|
19962
|
+
role: _enum([
|
|
19963
|
+
"primary",
|
|
19964
|
+
"sibling",
|
|
19965
|
+
"child"
|
|
19966
|
+
]).nullable(),
|
|
19967
|
+
joinedAt: number(),
|
|
19968
|
+
/** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
|
|
19969
|
+
* at read time. Absent when nothing resolved, never an empty string. */
|
|
19970
|
+
label: string().optional(),
|
|
19971
|
+
/** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
|
|
19972
|
+
* recognised nobody, and absent for a member whose track has been pruned —
|
|
19973
|
+
* a thumbnail that names the wrong person is worse than one naming nobody. */
|
|
19974
|
+
subLabel: string().optional()
|
|
19975
|
+
})).readonly()
|
|
19976
|
+
});
|
|
19977
|
+
/**
|
|
19978
|
+
* What a LIST of groups carries: the envelope plus what it is MADE OF.
|
|
19979
|
+
*
|
|
19980
|
+
* Coarse on purpose — the operator's rule is "only the rough data on the list,
|
|
19981
|
+
* we extend when we notice we need more". Enough to draw the card ("two people
|
|
19982
|
+
* and a dog") and no more. The MEMBERS, their names and the per-member links
|
|
19983
|
+
* are on {@link getSummary}: shipping every member of every group so a client
|
|
19984
|
+
* can count them turns a page of two hundred groups into a thousand records to
|
|
19985
|
+
* draw two hundred badges.
|
|
19986
|
+
*/
|
|
19987
|
+
var SummaryListItemSchema = object({
|
|
19988
|
+
summary: SummarySchema,
|
|
19989
|
+
/** Class counts, most numerous first; `className: null` is a member whose
|
|
19990
|
+
* class never resolved, counted so the counts add up to `memberCount`. */
|
|
19991
|
+
classes: array(object({
|
|
19992
|
+
className: string().nullable(),
|
|
19993
|
+
count: number().int().positive()
|
|
19994
|
+
})).readonly()
|
|
19995
|
+
});
|
|
19996
|
+
var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
|
|
19997
|
+
/**
|
|
19998
|
+
* The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
|
|
19999
|
+
*
|
|
20000
|
+
* The reel on the cameras page and the Events gallery both ride a cursor, so a
|
|
20001
|
+
* summaries feed that could only answer "the last N for these cameras"
|
|
20002
|
+
* (`listSummaries`) would make groups available in the timeline and nowhere
|
|
20003
|
+
* else. Two feeds with different ordering contracts would be two ideas of what
|
|
20004
|
+
* "newest" means, on one screen.
|
|
20005
|
+
*/
|
|
20006
|
+
var RecentSummariesQueryInput = object({
|
|
20007
|
+
/** Devices to merge. An empty array yields an empty page. */
|
|
20008
|
+
deviceIds: array(number()),
|
|
20009
|
+
/** Window lower bound on `lastActiveAt` (inclusive). */
|
|
20010
|
+
since: number().optional(),
|
|
20011
|
+
/** Window upper bound on `lastActiveAt` (inclusive). */
|
|
20012
|
+
until: number().optional(),
|
|
20013
|
+
limit: number().int().min(1).max(500).default(100),
|
|
20014
|
+
/** Opaque continuation cursor from a previous page's `nextCursor`. */
|
|
20015
|
+
cursor: string().optional(),
|
|
20016
|
+
/**
|
|
20017
|
+
* Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
|
|
20018
|
+
* deliberately the same name: the events filter tree emits one list of ticked
|
|
20019
|
+
* taxonomy nodes, and a summaries feed that called it something else would
|
|
20020
|
+
* make the operator's one filter component produce two shapes.
|
|
20021
|
+
*
|
|
20022
|
+
* A question about the MEMBERS: a group matches when ANY of them does. "Show
|
|
20023
|
+
* me the arrivals with a dog" has to return the group of four people who had
|
|
20024
|
+
* one with them; a filter on the group's own fields could only ever answer
|
|
20025
|
+
* about its primary.
|
|
20026
|
+
*
|
|
20027
|
+
* Matched against all THREE tiers of a member — the class (`animal`), the
|
|
20028
|
+
* sub-class (`dog`) and the instance (a name, a plate) — because which tier a
|
|
20029
|
+
* ticked node lives on is the taxonomy's business, not the caller's. `dog` is
|
|
20030
|
+
* an event kind in the tree and a tier-1 label on an `animal` track; a filter
|
|
20031
|
+
* that read only the class would answer "animal" and never "dog".
|
|
20032
|
+
*
|
|
20033
|
+
* ABSENT or EMPTY means no filter — absent is not a selection of none.
|
|
20034
|
+
*/
|
|
20035
|
+
classes: array(string().min(1).max(60)).max(40).optional()
|
|
20036
|
+
});
|
|
20037
|
+
var RecentSummariesPageSchema = object({
|
|
20038
|
+
/** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
|
|
20039
|
+
* {@link listSummaries} — one list shape, so a card renders the same
|
|
20040
|
+
* whichever feed drew it. */
|
|
20041
|
+
summaries: array(SummaryListItemSchema).readonly(),
|
|
20042
|
+
nextCursor: string().nullable()
|
|
19879
20043
|
});
|
|
19880
20044
|
var RecentTracksPageSchema = object({
|
|
19881
20045
|
/** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
|
|
@@ -20289,7 +20453,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
20289
20453
|
classes: array(string()).optional(),
|
|
20290
20454
|
/** See {@link ExcludeSourcesDoc}. */
|
|
20291
20455
|
excludeSources: array(TrackSourceSchema).optional()
|
|
20292
|
-
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
|
|
20456
|
+
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
|
|
20293
20457
|
kind: "mutation",
|
|
20294
20458
|
auth: "admin"
|
|
20295
20459
|
}), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
|
|
@@ -20417,7 +20581,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
20417
20581
|
}), method(object({
|
|
20418
20582
|
deviceId: number(),
|
|
20419
20583
|
trackId: string(),
|
|
20420
|
-
|
|
20584
|
+
mediaIds: array(number().int()).min(1)
|
|
20421
20585
|
}), RetrainFrameSelectionSchema, {
|
|
20422
20586
|
kind: "mutation",
|
|
20423
20587
|
auth: "admin"
|
|
@@ -20485,7 +20649,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
20485
20649
|
kind: "query",
|
|
20486
20650
|
auth: "admin"
|
|
20487
20651
|
}), method(object({
|
|
20488
|
-
|
|
20652
|
+
/** Which of the three event tables owns the row. */
|
|
20653
|
+
ownerType: EventOwnerTypeSchema,
|
|
20654
|
+
/** The event's rowid in that table (D474: an event id is a number). */
|
|
20655
|
+
eventId: number().int(),
|
|
20489
20656
|
kind: MediaFileKindEnum.optional(),
|
|
20490
20657
|
deviceId: number()
|
|
20491
20658
|
}), array(MediaFileSchema).readonly()), method(object({
|
|
@@ -20496,7 +20663,8 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
20496
20663
|
trackId: string(),
|
|
20497
20664
|
deviceId: number()
|
|
20498
20665
|
}), array(MediaFileInfoSchema).readonly()), method(object({
|
|
20499
|
-
|
|
20666
|
+
ownerType: EventOwnerTypeSchema,
|
|
20667
|
+
eventId: number().int(),
|
|
20500
20668
|
deviceId: number()
|
|
20501
20669
|
}), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
|
|
20502
20670
|
kind: "mutation",
|
|
@@ -24959,8 +25127,8 @@ var IdentitySchema = object({
|
|
|
24959
25127
|
id: string(),
|
|
24960
25128
|
name: string(),
|
|
24961
25129
|
sampleCount: number().int().nonnegative(),
|
|
24962
|
-
|
|
24963
|
-
/** Inline base64 of the cover sample's crop, resolved from `
|
|
25130
|
+
coverMediaId: number().int().optional(),
|
|
25131
|
+
/** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
|
|
24964
25132
|
* Lets the UI render the person's face avatar instead of a placeholder icon. */
|
|
24965
25133
|
coverBase64: string().optional()
|
|
24966
25134
|
});
|
|
@@ -24986,8 +25154,8 @@ var IdentitySampleAuditRowSchema = object({
|
|
|
24986
25154
|
enrolledAt: number().int(),
|
|
24987
25155
|
deviceId: number().int().optional(),
|
|
24988
25156
|
source: _enum(["detected", "photo"]),
|
|
24989
|
-
/** The enrolled crop's media
|
|
24990
|
-
|
|
25157
|
+
/** The enrolled crop's media row id, so the panel can show the evidence. */
|
|
25158
|
+
mediaId: number().int().optional(),
|
|
24991
25159
|
/**
|
|
24992
25160
|
* False = the sample is stamped with a DIFFERENT face model, and a cosine
|
|
24993
25161
|
* across feature spaces is a well-formed float with no meaning. Every number
|
|
@@ -25056,15 +25224,15 @@ var FaceInfoSchema = object({
|
|
|
25056
25224
|
*/
|
|
25057
25225
|
cropUrl: string().optional(),
|
|
25058
25226
|
/** Design B: the face bbox (pixel space) on the key frame — lets a detail
|
|
25059
|
-
* view draw the box over the native `
|
|
25227
|
+
* view draw the box over the native `keyFrameMediaId` frame. Absent on
|
|
25060
25228
|
* legacy rows written before design B. */
|
|
25061
25229
|
faceBbox: BoundingBoxSchema.optional(),
|
|
25062
25230
|
/** Design B: MediaStore key of the track's native-resolution key frame.
|
|
25063
25231
|
* Fetch the native JPEG via the event-media data-plane
|
|
25064
|
-
* (`/addon/<addonId>/event-media/<
|
|
25232
|
+
* (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
|
|
25065
25233
|
* track produced no key frame (e.g. native/onboard source) — the UI falls
|
|
25066
25234
|
* back to the inline `base64` face crop. */
|
|
25067
|
-
|
|
25235
|
+
keyFrameMediaId: number().int().optional(),
|
|
25068
25236
|
/** Winning identity-match cosine (0..1) for this face's track, when an
|
|
25069
25237
|
* identity was auto-confirmed. Lets the UI surface WHY a face was assigned
|
|
25070
25238
|
* (confidence badge / low-confidence audit). Absent on legacy rows and on
|
|
@@ -25133,11 +25301,14 @@ var FaceClusterSchema = object({
|
|
|
25133
25301
|
* `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
|
|
25134
25302
|
* dialog already rendering its key FRAME from the `event-media` plane.
|
|
25135
25303
|
*
|
|
25136
|
-
* `url` is `/addon/<addonId>/event-media/<
|
|
25137
|
-
* media
|
|
25304
|
+
* `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
|
|
25305
|
+
* resolves a media row by its own id, so this needed no new plane and no new
|
|
25306
|
+
* access decision.
|
|
25138
25307
|
*/
|
|
25139
25308
|
var MediaFileLiteSchema$1 = object({
|
|
25140
|
-
|
|
25309
|
+
/** The media row's own id — the same address `MediaFileRef.mediaId` carries
|
|
25310
|
+
* (D482). It was the row's composite key, which embedded the owner. */
|
|
25311
|
+
mediaId: number().int(),
|
|
25141
25312
|
kind: string(),
|
|
25142
25313
|
url: string(),
|
|
25143
25314
|
sizeBytes: number(),
|
|
@@ -25154,7 +25325,7 @@ method(object({
|
|
|
25154
25325
|
* covers inline, ~10 KB of base64 per row, on a query this UI mounts
|
|
25155
25326
|
* four times and the viewer holds at `staleTime: 30_000`.
|
|
25156
25327
|
*
|
|
25157
|
-
* Nothing loses its avatar: `
|
|
25328
|
+
* Nothing loses its avatar: `coverMediaId` is already on every row and
|
|
25158
25329
|
* the `event-media` plane serves that key `immutable` with an ETag.
|
|
25159
25330
|
*
|
|
25160
25331
|
* **This is an INPUT field, so it does not reach the addon until the
|
|
@@ -27622,8 +27793,8 @@ var VehicleSchema = object({
|
|
|
27622
27793
|
id: string(),
|
|
27623
27794
|
name: string(),
|
|
27624
27795
|
sampleCount: number().int().nonnegative(),
|
|
27625
|
-
|
|
27626
|
-
/** Inline base64 of the cover sample's plate crop, resolved from `
|
|
27796
|
+
coverMediaId: number().int().optional(),
|
|
27797
|
+
/** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
|
|
27627
27798
|
coverBase64: string().optional()
|
|
27628
27799
|
});
|
|
27629
27800
|
var VehicleSampleInfoSchema = object({
|
|
@@ -27656,7 +27827,7 @@ var PlateInfoSchema = object({
|
|
|
27656
27827
|
/** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
|
|
27657
27828
|
plateBbox: BoundingBoxSchema.optional(),
|
|
27658
27829
|
/** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
|
|
27659
|
-
|
|
27830
|
+
keyFrameMediaId: number().int().optional(),
|
|
27660
27831
|
base64: string().optional(),
|
|
27661
27832
|
/**
|
|
27662
27833
|
* Same crop as a data-plane URL, always present when the plate has a stored
|
|
@@ -27677,11 +27848,14 @@ var PlateInfoSchema = object({
|
|
|
27677
27848
|
* `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
|
|
27678
27849
|
* dialog already rendering its key FRAME from the `event-media` plane.
|
|
27679
27850
|
*
|
|
27680
|
-
* `url` is `/addon/<addonId>/event-media/<
|
|
27681
|
-
* media
|
|
27851
|
+
* `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
|
|
27852
|
+
* resolves a media row by its own id, so this needed no new plane and no new
|
|
27853
|
+
* access decision.
|
|
27682
27854
|
*/
|
|
27683
27855
|
var MediaFileLiteSchema = object({
|
|
27684
|
-
|
|
27856
|
+
/** The media row's own id — the same address `MediaFileRef.mediaId` carries
|
|
27857
|
+
* (D482). It was the row's composite key, which embedded the owner. */
|
|
27858
|
+
mediaId: number().int(),
|
|
27685
27859
|
kind: string(),
|
|
27686
27860
|
url: string(),
|
|
27687
27861
|
sizeBytes: number(),
|
|
@@ -27740,7 +27914,7 @@ method(object({
|
|
|
27740
27914
|
}), method(object({
|
|
27741
27915
|
/** Inline {@link VehicleSchema.coverBase64} on every row. Default
|
|
27742
27916
|
* `false` — the vehicle twin of `faceGallery.listIdentities`'s
|
|
27743
|
-
* option; `
|
|
27917
|
+
* option; `coverMediaId` + the `event-media` plane carry the picture.
|
|
27744
27918
|
* INPUT field: stripped by the hub router until the train ships, which
|
|
27745
27919
|
* resolves to `false` and is exactly the intended default. */
|
|
27746
27920
|
includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
|
|
@@ -29064,7 +29238,14 @@ var SceneReferenceSchema = object({
|
|
|
29064
29238
|
modelId: string(),
|
|
29065
29239
|
condition: SceneConditionSchema,
|
|
29066
29240
|
capturedAt: number(),
|
|
29067
|
-
|
|
29241
|
+
/**
|
|
29242
|
+
* The reference thumbnail's media ROW id (D482).
|
|
29243
|
+
*
|
|
29244
|
+
* The name already said `mediaId` while the value was the row's composite
|
|
29245
|
+
* key — a leftover that read as done and was not. It is the row's own id
|
|
29246
|
+
* now, addressed at `/addon/<addonId>/event-media/m/<id>`.
|
|
29247
|
+
*/
|
|
29248
|
+
thumbnailMediaId: number().int().optional(),
|
|
29068
29249
|
/** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
|
|
29069
29250
|
* anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
|
|
29070
29251
|
* normalized rect frame a different piece of world, and the scene would
|
|
@@ -29878,7 +30059,29 @@ var LoggingSettingsPatchSchema = object({
|
|
|
29878
30059
|
* disarmed the channels it did not mention would make the Levels page and
|
|
29879
30060
|
* the Diagnostics page fight over the same value.
|
|
29880
30061
|
*/
|
|
29881
|
-
channels: array(LogChannelWindowPatchSchema).readonly().optional()
|
|
30062
|
+
channels: array(LogChannelWindowPatchSchema).readonly().optional(),
|
|
30063
|
+
/**
|
|
30064
|
+
* How many days the analytics OPS LOG is kept — the audit trail of what
|
|
30065
|
+
* retention actually deleted.
|
|
30066
|
+
*
|
|
30067
|
+
* It lives HERE, on the logging document, and not on the analytics addon's
|
|
30068
|
+
* settings form, on the operator's instruction: every other log-ish window in
|
|
30069
|
+
* this system is configured through this one document, and a retention audit
|
|
30070
|
+
* configured somewhere else is a second place to look when the question is
|
|
30071
|
+
* "what happened to my data".
|
|
30072
|
+
*
|
|
30073
|
+
* That placement has a cost worth stating: this document ships in the
|
|
30074
|
+
* framework closure, so this knob needs a published `@camstack/server` while
|
|
30075
|
+
* the four windows beside it (D485) reach a node on `camstack deploy`.
|
|
30076
|
+
*
|
|
30077
|
+
* `retention-model.ts` argued this one must NOT follow a device window,
|
|
30078
|
+
* because the audit trail is the history of the SWEEP and not of the camera.
|
|
30079
|
+
* That argument stands and is untouched — what changes is that 30 days stops
|
|
30080
|
+
* being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
|
|
30081
|
+
* rather than 0-as-delete: an audit log truncated to nothing is precisely the
|
|
30082
|
+
* state in which the next 9.5 GiB leak cannot be explained.
|
|
30083
|
+
*/
|
|
30084
|
+
opsLogRetentionDays: number().int().min(0).max(3650).optional()
|
|
29882
30085
|
});
|
|
29883
30086
|
/**
|
|
29884
30087
|
* Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
|
|
@@ -29951,6 +30154,14 @@ var LoggingSettingsStateSchema = object({
|
|
|
29951
30154
|
channels: array(LogChannelDescriptorSchema).readonly(),
|
|
29952
30155
|
/** The channels ARMED right now, each with its deadline. */
|
|
29953
30156
|
activeChannels: array(LogChannelWindowStateSchema).readonly(),
|
|
30157
|
+
/**
|
|
30158
|
+
* The ops-log window in days, as it is in force. `0` = kept forever.
|
|
30159
|
+
*
|
|
30160
|
+
* Reported rather than left to the caller's memory of what it wrote: this is
|
|
30161
|
+
* the value the analytics sweep will actually use, and an operator asking how
|
|
30162
|
+
* long their audit trail lives should not have to infer it from a default.
|
|
30163
|
+
*/
|
|
30164
|
+
opsLogRetentionDays: number().int().min(0),
|
|
29954
30165
|
persisted: boolean()
|
|
29955
30166
|
});
|
|
29956
30167
|
method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
|
|
@@ -30512,7 +30723,7 @@ var StationaryObjectSchema = object({
|
|
|
30512
30723
|
/** Enrichment label carried from the source track (identity / plate). */
|
|
30513
30724
|
label: string().optional(),
|
|
30514
30725
|
/** Native-resolution key-frame media key for the parked object's best image. */
|
|
30515
|
-
|
|
30726
|
+
keyFrameMediaId: number().int().optional()
|
|
30516
30727
|
});
|
|
30517
30728
|
var CameraOccupancySnapshotSchema = object({
|
|
30518
30729
|
/** Frame timestamp of the inference result that produced this snapshot. */
|
|
@@ -31668,6 +31879,12 @@ Object.freeze({
|
|
|
31668
31879
|
addonId: null,
|
|
31669
31880
|
access: "create"
|
|
31670
31881
|
},
|
|
31882
|
+
"dataStoreProvider.upsert": {
|
|
31883
|
+
capName: "data-store-provider",
|
|
31884
|
+
capScope: "system",
|
|
31885
|
+
addonId: null,
|
|
31886
|
+
access: "create"
|
|
31887
|
+
},
|
|
31671
31888
|
"dayNight.getOptions": {
|
|
31672
31889
|
capName: "day-night",
|
|
31673
31890
|
capScope: "device",
|
|
@@ -34104,6 +34321,12 @@ Object.freeze({
|
|
|
34104
34321
|
addonId: null,
|
|
34105
34322
|
access: "view"
|
|
34106
34323
|
},
|
|
34324
|
+
"pipelineAnalytics.listRecentSummaries": {
|
|
34325
|
+
capName: "pipeline-analytics",
|
|
34326
|
+
capScope: "device",
|
|
34327
|
+
addonId: null,
|
|
34328
|
+
access: "view"
|
|
34329
|
+
},
|
|
34107
34330
|
"pipelineAnalytics.listRecentTracks": {
|
|
34108
34331
|
capName: "pipeline-analytics",
|
|
34109
34332
|
capScope: "device",
|
|
@@ -35532,6 +35755,12 @@ Object.freeze({
|
|
|
35532
35755
|
addonId: null,
|
|
35533
35756
|
access: "create"
|
|
35534
35757
|
},
|
|
35758
|
+
"settingsStore.upsert": {
|
|
35759
|
+
capName: "settings-store",
|
|
35760
|
+
capScope: "system",
|
|
35761
|
+
addonId: null,
|
|
35762
|
+
access: "create"
|
|
35763
|
+
},
|
|
35535
35764
|
"smtpProvider.getStatus": {
|
|
35536
35765
|
capName: "smtp-provider",
|
|
35537
35766
|
capScope: "system",
|
|
@@ -37925,6 +38154,11 @@ Object.freeze({
|
|
|
37925
38154
|
form: "single",
|
|
37926
38155
|
optional: true
|
|
37927
38156
|
}],
|
|
38157
|
+
"pipelineAnalytics.listRecentSummaries": [{
|
|
38158
|
+
name: "deviceIds",
|
|
38159
|
+
form: "array",
|
|
38160
|
+
optional: false
|
|
38161
|
+
}],
|
|
37928
38162
|
"pipelineAnalytics.listRecentTracks": [{
|
|
37929
38163
|
name: "deviceIds",
|
|
37930
38164
|
form: "array",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-notifiers",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.105",
|
|
4
4
|
"description": "System notifiers addon for CamStack — a `notification-output` collection provider hosting per-kind notifier adapters (ntfy, pushover, gotify, telegram, discord, webhook, zentik).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|