@camstack/addon-provider-dreo 0.2.20 → 0.2.22
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 +187 -44
- package/dist/addon.mjs +187 -44
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -35,7 +35,7 @@ var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["modu
|
|
|
35
35
|
//#endregion
|
|
36
36
|
let crypto$1 = require("crypto");
|
|
37
37
|
let events = require("events");
|
|
38
|
-
//#region ../types/dist/event-category-
|
|
38
|
+
//#region ../types/dist/event-category-XfKNtfCc.mjs
|
|
39
39
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
40
40
|
EventCategory["SystemBoot"] = "system.boot";
|
|
41
41
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -52,9 +52,10 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
52
52
|
EventCategory["SystemRestartCompleted"] = "system.restart-completed";
|
|
53
53
|
/**
|
|
54
54
|
* A newer addon or server-root package version was found by the
|
|
55
|
-
* authoritative registry check. Emitted once
|
|
56
|
-
* `(
|
|
57
|
-
*
|
|
55
|
+
* authoritative registry check. Emitted once when any observed
|
|
56
|
+
* `latestVersion` changes (or a package/node first appears behind);
|
|
57
|
+
* the payload carries the full currently-available list. Repeated
|
|
58
|
+
* polling of the same latests is silent.
|
|
58
59
|
*/
|
|
59
60
|
EventCategory["UpdateAvailable"] = "update.available";
|
|
60
61
|
/**
|
|
@@ -73,6 +74,22 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
73
74
|
EventCategory["AddonInstalled"] = "addon.installed";
|
|
74
75
|
EventCategory["AddonUninstalled"] = "addon.uninstalled";
|
|
75
76
|
EventCategory["AddonCrashed"] = "addon.crashed";
|
|
77
|
+
/**
|
|
78
|
+
* A RUNNER the D6 crash circuit-breaker gave up on — terminal.
|
|
79
|
+
*
|
|
80
|
+
* `AddonCrashed` is the routine, self-healing fact ("it crashed; it is being
|
|
81
|
+
* respawned"); this is the one that never resolves itself. Emitted exactly
|
|
82
|
+
* once per trip, by `process-service.ts`, carrying the node, the runner, the
|
|
83
|
+
* addons it hosted and the crash count that tripped the breaker.
|
|
84
|
+
*
|
|
85
|
+
* It exists because a runner marked terminally `failed` used to be SILENT:
|
|
86
|
+
* on 2026-08-18 the `recorder` runner died of three unhandled ffmpeg spawn
|
|
87
|
+
* errors, the breaker stopped respawning it (correctly), `/health` kept
|
|
88
|
+
* returning 200, and fleet-wide recording was gone for 3h15 before the
|
|
89
|
+
* operator's phone told him. The Notification Center's system-event intake
|
|
90
|
+
* consumes this category and maps it to the `addon-crash-loop` kind.
|
|
91
|
+
*/
|
|
92
|
+
EventCategory["AddonRunnerFailed"] = "addon.runner-failed";
|
|
76
93
|
EventCategory["AddonError"] = "addon.error";
|
|
77
94
|
EventCategory["AddonPageReady"] = "addon.page-ready";
|
|
78
95
|
EventCategory["AddonWidgetReady"] = "addon.widget-ready";
|
|
@@ -7584,6 +7601,10 @@ var RecordingBandSchema = object({
|
|
|
7584
7601
|
preBufferSec: number().min(0).optional(),
|
|
7585
7602
|
postBufferSec: number().min(0).optional()
|
|
7586
7603
|
});
|
|
7604
|
+
({
|
|
7605
|
+
preBufferSec: 10,
|
|
7606
|
+
postBufferSec: 30
|
|
7607
|
+
}).postBufferSec * 1e3;
|
|
7587
7608
|
/**
|
|
7588
7609
|
* Per-device retention overrides. Every field is optional; an unset or `0`
|
|
7589
7610
|
* value inherits the node-wide recorder default. Only footage-lifetime limits
|
|
@@ -7631,6 +7652,12 @@ var RecordingConfigSchema = object({
|
|
|
7631
7652
|
/** DERIVED summary of `bands`, stamped by the recorder on every save.
|
|
7632
7653
|
* Authoring it has no effect — see {@link RecordingStorageModeSchema}. */
|
|
7633
7654
|
mode: RecordingStorageModeSchema.optional(),
|
|
7655
|
+
/**
|
|
7656
|
+
* Which assigned broker slots to record. Absent / empty = {@link
|
|
7657
|
+
* DEFAULT_RECORDING_PROFILES} (`high`+`low`) intersected with the
|
|
7658
|
+
* camera's currently assigned slots — never `mid` unless the operator
|
|
7659
|
+
* picks it, and never a slot the broker has not assigned.
|
|
7660
|
+
*/
|
|
7634
7661
|
profiles: array(CamProfileSchema).optional(),
|
|
7635
7662
|
segmentSeconds: number().int().positive().optional(),
|
|
7636
7663
|
/**
|
|
@@ -7711,7 +7738,11 @@ var RelocateFootageInputSchema = object({
|
|
|
7711
7738
|
profiles: array(string()).optional(),
|
|
7712
7739
|
/** Copy throttle in MB/s (default 40) — the drain is a background chore,
|
|
7713
7740
|
* never allowed to starve live writers. */
|
|
7714
|
-
throttleMbps: number().min(1).max(1e3).optional()
|
|
7741
|
+
throttleMbps: number().min(1).max(1e3).optional(),
|
|
7742
|
+
/** Move only segments whose startMs is >= this. Absent = the whole source
|
|
7743
|
+
* pile. Used when a full drain is too expensive and the operator only
|
|
7744
|
+
* wants the recent window on the new disk. */
|
|
7745
|
+
sinceMs: number().int().optional()
|
|
7715
7746
|
});
|
|
7716
7747
|
/** Internal, lease-scoped participant operation. It is intentionally separate
|
|
7717
7748
|
* from persistent recording settings: a migration never changes
|
|
@@ -14593,6 +14624,7 @@ var NcSystemEventKindSchema = _enum([
|
|
|
14593
14624
|
"node-offline",
|
|
14594
14625
|
"node-inference-unavailable",
|
|
14595
14626
|
"detection-blind",
|
|
14627
|
+
"addon-crash-loop",
|
|
14596
14628
|
"addon-update-available",
|
|
14597
14629
|
"server-update-available",
|
|
14598
14630
|
"alarm-triggered",
|
|
@@ -14600,6 +14632,9 @@ var NcSystemEventKindSchema = _enum([
|
|
|
14600
14632
|
"alarm-disarmed",
|
|
14601
14633
|
"alarm-arming",
|
|
14602
14634
|
"alarm-arm-refused",
|
|
14635
|
+
"addon-updated",
|
|
14636
|
+
"server-updated",
|
|
14637
|
+
"export-completed",
|
|
14603
14638
|
"camera-online",
|
|
14604
14639
|
"camera-offline",
|
|
14605
14640
|
"camera-disabled",
|
|
@@ -16493,13 +16528,19 @@ var RetrainStatusSchema = _enum([
|
|
|
16493
16528
|
* "never marked" from "already trained" must read `retrainStatus`.
|
|
16494
16529
|
*
|
|
16495
16530
|
* `debug` does NOT pin; it is attention, not durability.
|
|
16531
|
+
*
|
|
16532
|
+
* `favourited` IS a BOOLEAN pin (durability bit), not a retrain lifecycle.
|
|
16533
|
+
* A favourited track is skipped by retention the same way `staging` is, but
|
|
16534
|
+
* it does not enter `none|staging|trained` and has no staging budget.
|
|
16496
16535
|
*/
|
|
16497
16536
|
var TrackFlagFields = {
|
|
16498
16537
|
/** Operator marked this track as training material — i.e. `retrainStatus` is
|
|
16499
16538
|
* `'staging'`. */
|
|
16500
16539
|
markForTrain: boolean().optional(),
|
|
16501
16540
|
/** Operator marked this track for diagnostic attention. */
|
|
16502
|
-
debug: boolean().optional()
|
|
16541
|
+
debug: boolean().optional(),
|
|
16542
|
+
/** Operator favourited this track. Pins it against pruning. */
|
|
16543
|
+
favourited: boolean().optional()
|
|
16503
16544
|
};
|
|
16504
16545
|
/**
|
|
16505
16546
|
* The lifecycle field itself, on the READ surfaces only (`Track`, `KeyEvent`).
|
|
@@ -16524,6 +16565,7 @@ var TrackFlagsSchema = object({
|
|
|
16524
16565
|
trackId: string(),
|
|
16525
16566
|
markForTrain: boolean(),
|
|
16526
16567
|
debug: boolean(),
|
|
16568
|
+
favourited: boolean(),
|
|
16527
16569
|
/** The lifecycle state the boolean was derived from. Required here (unlike on
|
|
16528
16570
|
* a track row) because this shape is only ever produced by the write body,
|
|
16529
16571
|
* which always knows it — and a surface that has just written needs to render
|
|
@@ -17156,6 +17198,12 @@ var TrackCascadeCountsSchema = object({
|
|
|
17156
17198
|
/** Per-track CLIP search vectors removed (best-effort). */
|
|
17157
17199
|
embeddings: number().int()
|
|
17158
17200
|
});
|
|
17201
|
+
/** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
|
|
17202
|
+
var DiskReconcileCountsSchema = object({
|
|
17203
|
+
mediaDropped: number().int(),
|
|
17204
|
+
tracks: number().int(),
|
|
17205
|
+
events: number().int()
|
|
17206
|
+
});
|
|
17159
17207
|
/** Event-store footprint for one camera. */
|
|
17160
17208
|
var EventStoreDeviceFootprintSchema = object({
|
|
17161
17209
|
deviceId: number(),
|
|
@@ -17332,6 +17380,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
17332
17380
|
}), method(object({ deviceId: number() }), TrackCascadeCountsSchema, {
|
|
17333
17381
|
kind: "mutation",
|
|
17334
17382
|
auth: "admin"
|
|
17383
|
+
}), method(object({ deviceId: number() }), DiskReconcileCountsSchema, {
|
|
17384
|
+
kind: "mutation",
|
|
17385
|
+
auth: "admin"
|
|
17335
17386
|
}), method(object({
|
|
17336
17387
|
deviceId: number(),
|
|
17337
17388
|
trackIds: array(string()).min(1)
|
|
@@ -18882,6 +18933,24 @@ var CameraStatusDegradationSchema = object({
|
|
|
18882
18933
|
*
|
|
18883
18934
|
* See spec: `docs/superpowers/specs/2026-06-24-camera-status-aggregator-cap.md`
|
|
18884
18935
|
*/
|
|
18936
|
+
var DiskReconcileJobSchema = object({
|
|
18937
|
+
state: _enum([
|
|
18938
|
+
"idle",
|
|
18939
|
+
"running",
|
|
18940
|
+
"done",
|
|
18941
|
+
"error"
|
|
18942
|
+
]),
|
|
18943
|
+
total: number().int().nonnegative(),
|
|
18944
|
+
completed: number().int().nonnegative(),
|
|
18945
|
+
currentDeviceId: number().int().nullable(),
|
|
18946
|
+
failed: array(number().int()).readonly(),
|
|
18947
|
+
mediaDropped: number().int().nonnegative(),
|
|
18948
|
+
tracks: number().int().nonnegative(),
|
|
18949
|
+
events: number().int().nonnegative(),
|
|
18950
|
+
startedAtMs: number().int().nullable(),
|
|
18951
|
+
finishedAtMs: number().int().nullable(),
|
|
18952
|
+
error: string().nullable()
|
|
18953
|
+
});
|
|
18885
18954
|
var CameraStatusSchema = object({
|
|
18886
18955
|
deviceId: number(),
|
|
18887
18956
|
assignment: CameraAssignmentStatusSchema,
|
|
@@ -19113,7 +19182,10 @@ method(StorageMigrationLeaseInputSchema, object({ paused: literal(true) }), {
|
|
|
19113
19182
|
}), CameraSwitchGroupSchema, {
|
|
19114
19183
|
kind: "mutation",
|
|
19115
19184
|
auth: "admin"
|
|
19116
|
-
}), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(),
|
|
19185
|
+
}), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), DiskReconcileJobSchema, {
|
|
19186
|
+
kind: "mutation",
|
|
19187
|
+
auth: "admin"
|
|
19188
|
+
}), method(_void(), DiskReconcileJobSchema, { auth: "admin" }), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
|
|
19117
19189
|
name: string(),
|
|
19118
19190
|
description: string().optional(),
|
|
19119
19191
|
config: CameraPipelineConfigSchema
|
|
@@ -20186,20 +20258,6 @@ var VectorStatsResultSchema = object({
|
|
|
20186
20258
|
exact: boolean()
|
|
20187
20259
|
});
|
|
20188
20260
|
method(VectorDeclareIndexInputSchema, _void(), { kind: "mutation" }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, { kind: "mutation" }), method(VectorQueryInputSchema, VectorQueryResultSchema), method(VectorGetInputSchema, VectorGetResultSchema), method(VectorDeleteInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorStatsInputSchema, VectorStatsResultSchema);
|
|
20189
|
-
/**
|
|
20190
|
-
* `videoclips` — the unified, navigable-clip surface for a camera.
|
|
20191
|
-
*
|
|
20192
|
-
* A device-scoped WRAPPER cap (like `pipeline-analytics`): exactly one active
|
|
20193
|
-
* provider per device, substitutable. The DEFAULT provider (registered by
|
|
20194
|
-
* `addon-post-analysis`, `defaultActive: true`) composes the analytics event
|
|
20195
|
-
* log (markers + thumbnails) with the recorder's `getPlaybackManifest` — a clip
|
|
20196
|
-
* is a time-WINDOW over existing footage, never a separate file. A camera that
|
|
20197
|
-
* exposes NATIVE onboard clips (Reolink/Hikvision NVR) can later substitute the
|
|
20198
|
-
* wrapper provider for its device and serve its own clip catalog + URLs.
|
|
20199
|
-
*
|
|
20200
|
-
* A `Clip` is purely time-based (subtree-blind): playback resolves segments by
|
|
20201
|
-
* temporal overlap, so the API never decides `continuous` vs `events`.
|
|
20202
|
-
*/
|
|
20203
20261
|
var ClipSchema = object({
|
|
20204
20262
|
/** Opaque, provider-namespaced id. The default provider encodes the time
|
|
20205
20263
|
* window so `getClipPlayback` is self-contained (no event re-query). */
|
|
@@ -20216,8 +20274,65 @@ var ClipSchema = object({
|
|
|
20216
20274
|
startMs: number(),
|
|
20217
20275
|
endMs: number()
|
|
20218
20276
|
}),
|
|
20219
|
-
/**
|
|
20220
|
-
|
|
20277
|
+
/**
|
|
20278
|
+
* Distinct object classes attached to this visit (`person`, `car`, …),
|
|
20279
|
+
* dominant first, capped at {@link MAX_CLIP_LABELS}. The ribbon renders these
|
|
20280
|
+
* instead of the bare kind — "Object" tells the operator nothing a colour bar
|
|
20281
|
+
* did not. Absent (never empty) when the visit attached no classified object
|
|
20282
|
+
* event, so a motion/audio-only visit keeps its kind label.
|
|
20283
|
+
*/
|
|
20284
|
+
labels: array(string()).max(3).optional(),
|
|
20285
|
+
/**
|
|
20286
|
+
* Lazy thumbnail URL, never inlined.
|
|
20287
|
+
*
|
|
20288
|
+
* Recording-derived clips (events-mode keep-window, and the prepared
|
|
20289
|
+
* continuous event+fragment visit) MUST use the snapshot of the **main
|
|
20290
|
+
* event of the interval** — `getEventMedia({ eventId, kind: 'snapshot' })`
|
|
20291
|
+
* of the event that owns `kind` (object > motion > audio). Do not extract
|
|
20292
|
+
* a keyframe from the recorded segments. Other providers (onboard, HKSV)
|
|
20293
|
+
* mint their own stills.
|
|
20294
|
+
*
|
|
20295
|
+
* **VOUCHED, never fabricated.** A provider emits this only for an event it
|
|
20296
|
+
* has CONFIRMED owns at least one media row (its own, or its owning track's).
|
|
20297
|
+
* Absent is meaningful — "this visit has no event still" — never "we did not
|
|
20298
|
+
* look". Stamping it from a URL template made 35% of one camera's clips point
|
|
20299
|
+
* at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
|
|
20300
|
+
* A read that FAILS drops the claim; it never invents it.
|
|
20301
|
+
*/
|
|
20302
|
+
thumbnail: string().optional(),
|
|
20303
|
+
/**
|
|
20304
|
+
* An instant INSIDE this visit's footage, hole-safe, where a recorded still
|
|
20305
|
+
* can be decoded. Present whenever the visit came from recorded availability;
|
|
20306
|
+
* absent on a per-event padded window (there is no footage to promise).
|
|
20307
|
+
*
|
|
20308
|
+
* This is not a thumbnail and not a second byte path: it is the argument to
|
|
20309
|
+
* the recorder's existing still route. The surface — never the provider —
|
|
20310
|
+
* decides whether to use it. It is the midpoint of the visit's LONGEST
|
|
20311
|
+
* contiguous range, not of the visit: a visit spans its holes by
|
|
20312
|
+
* construction, so a naive midpoint lands in dead air.
|
|
20313
|
+
*/
|
|
20314
|
+
stillAtMs: number().optional(),
|
|
20315
|
+
/**
|
|
20316
|
+
* Analytics event ids that overlap this visit — a BOUNDED sample, newest
|
|
20317
|
+
* first within kind (object → motion → audio), capped at
|
|
20318
|
+
* {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
|
|
20319
|
+
*
|
|
20320
|
+
* Bounded because it is not a payload the surface pages through: one visit on
|
|
20321
|
+
* device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
|
|
20322
|
+
* camera-day. Read {@link eventCount} for the true total.
|
|
20323
|
+
*/
|
|
20324
|
+
eventIds: array(string()).optional(),
|
|
20325
|
+
/** How many analytics events actually overlap this visit. Differs from
|
|
20326
|
+
* `eventIds.length` exactly when the sample was capped — so a truncated
|
|
20327
|
+
* list is never mistaken for a quiet visit. */
|
|
20328
|
+
eventCount: number().int().nonnegative().optional(),
|
|
20329
|
+
/** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
|
|
20330
|
+
* than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
|
|
20331
|
+
* bar keeps showing them via `recording.getAvailability`. */
|
|
20332
|
+
holes: array(object({
|
|
20333
|
+
startMs: number(),
|
|
20334
|
+
endMs: number()
|
|
20335
|
+
})).optional()
|
|
20221
20336
|
});
|
|
20222
20337
|
var ClipPlaybackSchema = object({
|
|
20223
20338
|
/** HLS master URL through the hub data-plane (Range + token in path). */
|
|
@@ -20681,7 +20796,14 @@ var SearchResultSchema = object({
|
|
|
20681
20796
|
});
|
|
20682
20797
|
var AutoUpdateSettingsSchema = object({
|
|
20683
20798
|
channel: ChannelSchema,
|
|
20684
|
-
intervalSeconds: number()
|
|
20799
|
+
intervalSeconds: number(),
|
|
20800
|
+
/**
|
|
20801
|
+
* Cadence of the "an update exists" POLLER, in seconds. Independent of
|
|
20802
|
+
* `channel`: the poller runs while auto-apply is `off`, because being told
|
|
20803
|
+
* about a publish and installing it are different decisions. Clamped
|
|
20804
|
+
* server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
|
|
20805
|
+
*/
|
|
20806
|
+
updateCheckIntervalSeconds: number()
|
|
20685
20807
|
});
|
|
20686
20808
|
var AddonAutoUpdateSchema = ChannelWithInheritSchema;
|
|
20687
20809
|
var RestartAddonResultSchema = unknown();
|
|
@@ -20822,7 +20944,9 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
|
|
|
20822
20944
|
auth: "admin"
|
|
20823
20945
|
}), method(_void(), AutoUpdateSettingsSchema, { auth: "admin" }), method(object({
|
|
20824
20946
|
channel: ChannelSchema,
|
|
20825
|
-
intervalSeconds: number().min(300).max(86400).optional()
|
|
20947
|
+
intervalSeconds: number().min(300).max(86400).optional(),
|
|
20948
|
+
/** Availability-poll cadence; see AutoUpdateSettingsSchema. */
|
|
20949
|
+
updateCheckIntervalSeconds: number().min(900).max(604800).optional()
|
|
20826
20950
|
}), unknown(), {
|
|
20827
20951
|
kind: "mutation",
|
|
20828
20952
|
auth: "admin"
|
|
@@ -26511,7 +26635,9 @@ var ExportOptionsSchema = object({
|
|
|
26511
26635
|
includeAudio: boolean(),
|
|
26512
26636
|
maxLifeMs: number().int().positive(),
|
|
26513
26637
|
deleteAfterDownload: boolean(),
|
|
26514
|
-
title: string().max(200).optional()
|
|
26638
|
+
title: string().max(200).optional(),
|
|
26639
|
+
/** Notification-output target ids to ping when this export becomes ready. */
|
|
26640
|
+
notifyTargetIds: array(string().min(1)).max(20).optional()
|
|
26515
26641
|
}).superRefine((v, ctx) => {
|
|
26516
26642
|
if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
|
|
26517
26643
|
code: ZodIssueCode.custom,
|
|
@@ -26570,10 +26696,18 @@ var ExportBytesSchema = object({
|
|
|
26570
26696
|
});
|
|
26571
26697
|
method(object({
|
|
26572
26698
|
deviceId: number(),
|
|
26573
|
-
|
|
26699
|
+
/** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
|
|
26700
|
+
profile: string().optional(),
|
|
26701
|
+
profiles: array(string()).min(1).optional(),
|
|
26574
26702
|
fromMs: number(),
|
|
26575
26703
|
toMs: number(),
|
|
26576
26704
|
options: ExportOptionsSchema
|
|
26705
|
+
}).superRefine((v, ctx) => {
|
|
26706
|
+
if ((v.profiles !== void 0 && v.profiles.length > 0 ? v.profiles : v.profile !== void 0 ? [v.profile] : []).length < 1) ctx.addIssue({
|
|
26707
|
+
code: ZodIssueCode.custom,
|
|
26708
|
+
message: "pass profiles[] (min 1) or legacy profile",
|
|
26709
|
+
path: ["profiles"]
|
|
26710
|
+
});
|
|
26577
26711
|
}), ExportRecordSchema, {
|
|
26578
26712
|
kind: "mutation",
|
|
26579
26713
|
auth: "protected"
|
|
@@ -29515,15 +29649,6 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
29515
29649
|
labels: ["probe not implemented"]
|
|
29516
29650
|
};
|
|
29517
29651
|
}
|
|
29518
|
-
/**
|
|
29519
|
-
* Top-level devices restored at once in {@link onRestoreDevices}.
|
|
29520
|
-
*
|
|
29521
|
-
* Four covers the fleets this ships to without turning a boot into a burst a
|
|
29522
|
-
* camera NVR answers with a refusal. A provider whose upstream is a single
|
|
29523
|
-
* session with a serial command channel (a Baichuan hub, an NVR that
|
|
29524
|
-
* serialises ISAPI) should lower it; nothing needs to raise it.
|
|
29525
|
-
*/
|
|
29526
|
-
restoreConcurrency = 4;
|
|
29527
29652
|
async restoreDevices(savedDevices) {
|
|
29528
29653
|
await this.onRestoreDevices(savedDevices);
|
|
29529
29654
|
if (savedDevices.length > 0) this.ctx.logger.info(`Restored ${savedDevices.length} ${this.providerName} device(s)`);
|
|
@@ -29578,14 +29703,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
29578
29703
|
});
|
|
29579
29704
|
}
|
|
29580
29705
|
};
|
|
29581
|
-
|
|
29582
|
-
await Promise.all(Array.from({ length: Math.min(Math.max(1, this.restoreConcurrency), topLevel.length) }, async () => {
|
|
29583
|
-
for (;;) {
|
|
29584
|
-
const saved = topLevel[nextTopLevel++];
|
|
29585
|
-
if (saved === void 0) return;
|
|
29586
|
-
await restoreOne(saved);
|
|
29587
|
-
}
|
|
29588
|
-
}));
|
|
29706
|
+
await Promise.all(topLevel.map((saved) => restoreOne(saved)));
|
|
29589
29707
|
const childRows = savedDevices.filter((s) => s.parentDeviceId !== null);
|
|
29590
29708
|
for (const saved of childRows) {
|
|
29591
29709
|
const Class = this.deviceClasses[saved.type];
|
|
@@ -32858,6 +32976,12 @@ Object.freeze({
|
|
|
32858
32976
|
addonId: null,
|
|
32859
32977
|
access: "create"
|
|
32860
32978
|
},
|
|
32979
|
+
"pipelineAnalytics.reconcileFromDisk": {
|
|
32980
|
+
capName: "pipeline-analytics",
|
|
32981
|
+
capScope: "device",
|
|
32982
|
+
addonId: null,
|
|
32983
|
+
access: "create"
|
|
32984
|
+
},
|
|
32861
32985
|
"pipelineAnalytics.refreshStorageLocationsForMigration": {
|
|
32862
32986
|
capName: "pipeline-analytics",
|
|
32863
32987
|
capScope: "device",
|
|
@@ -33260,6 +33384,12 @@ Object.freeze({
|
|
|
33260
33384
|
addonId: null,
|
|
33261
33385
|
access: "view"
|
|
33262
33386
|
},
|
|
33387
|
+
"pipelineOrchestrator.getReconcileFromDiskStatus": {
|
|
33388
|
+
capName: "pipeline-orchestrator",
|
|
33389
|
+
capScope: "system",
|
|
33390
|
+
addonId: null,
|
|
33391
|
+
access: "view"
|
|
33392
|
+
},
|
|
33263
33393
|
"pipelineOrchestrator.listAgentSettings": {
|
|
33264
33394
|
capName: "pipeline-orchestrator",
|
|
33265
33395
|
capScope: "system",
|
|
@@ -33284,6 +33414,12 @@ Object.freeze({
|
|
|
33284
33414
|
addonId: null,
|
|
33285
33415
|
access: "create"
|
|
33286
33416
|
},
|
|
33417
|
+
"pipelineOrchestrator.reconcileFromDisk": {
|
|
33418
|
+
capName: "pipeline-orchestrator",
|
|
33419
|
+
capScope: "system",
|
|
33420
|
+
addonId: null,
|
|
33421
|
+
access: "create"
|
|
33422
|
+
},
|
|
33287
33423
|
"pipelineOrchestrator.removeAgentSettings": {
|
|
33288
33424
|
capName: "pipeline-orchestrator",
|
|
33289
33425
|
capScope: "system",
|
|
@@ -36275,6 +36411,11 @@ Object.freeze({
|
|
|
36275
36411
|
form: "single",
|
|
36276
36412
|
optional: true
|
|
36277
36413
|
}],
|
|
36414
|
+
"pipelineAnalytics.reconcileFromDisk": [{
|
|
36415
|
+
name: "deviceId",
|
|
36416
|
+
form: "single",
|
|
36417
|
+
optional: false
|
|
36418
|
+
}],
|
|
36278
36419
|
"pipelineAnalytics.restageRetrainTrack": [{
|
|
36279
36420
|
name: "deviceId",
|
|
36280
36421
|
form: "single",
|
|
@@ -37340,6 +37481,8 @@ DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb;
|
|
|
37340
37481
|
DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
|
|
37341
37482
|
DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
|
|
37342
37483
|
DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
37484
|
+
var MB = 1024 * 1024;
|
|
37485
|
+
1024 * MB, 3072 * MB;
|
|
37343
37486
|
//#endregion
|
|
37344
37487
|
//#region src/config.ts
|
|
37345
37488
|
/**
|
package/dist/addon.mjs
CHANGED
|
@@ -36,7 +36,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
36
36
|
var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
37
37
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
38
38
|
//#endregion
|
|
39
|
-
//#region ../types/dist/event-category-
|
|
39
|
+
//#region ../types/dist/event-category-XfKNtfCc.mjs
|
|
40
40
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
41
41
|
EventCategory["SystemBoot"] = "system.boot";
|
|
42
42
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -53,9 +53,10 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
53
53
|
EventCategory["SystemRestartCompleted"] = "system.restart-completed";
|
|
54
54
|
/**
|
|
55
55
|
* A newer addon or server-root package version was found by the
|
|
56
|
-
* authoritative registry check. Emitted once
|
|
57
|
-
* `(
|
|
58
|
-
*
|
|
56
|
+
* authoritative registry check. Emitted once when any observed
|
|
57
|
+
* `latestVersion` changes (or a package/node first appears behind);
|
|
58
|
+
* the payload carries the full currently-available list. Repeated
|
|
59
|
+
* polling of the same latests is silent.
|
|
59
60
|
*/
|
|
60
61
|
EventCategory["UpdateAvailable"] = "update.available";
|
|
61
62
|
/**
|
|
@@ -74,6 +75,22 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
74
75
|
EventCategory["AddonInstalled"] = "addon.installed";
|
|
75
76
|
EventCategory["AddonUninstalled"] = "addon.uninstalled";
|
|
76
77
|
EventCategory["AddonCrashed"] = "addon.crashed";
|
|
78
|
+
/**
|
|
79
|
+
* A RUNNER the D6 crash circuit-breaker gave up on — terminal.
|
|
80
|
+
*
|
|
81
|
+
* `AddonCrashed` is the routine, self-healing fact ("it crashed; it is being
|
|
82
|
+
* respawned"); this is the one that never resolves itself. Emitted exactly
|
|
83
|
+
* once per trip, by `process-service.ts`, carrying the node, the runner, the
|
|
84
|
+
* addons it hosted and the crash count that tripped the breaker.
|
|
85
|
+
*
|
|
86
|
+
* It exists because a runner marked terminally `failed` used to be SILENT:
|
|
87
|
+
* on 2026-08-18 the `recorder` runner died of three unhandled ffmpeg spawn
|
|
88
|
+
* errors, the breaker stopped respawning it (correctly), `/health` kept
|
|
89
|
+
* returning 200, and fleet-wide recording was gone for 3h15 before the
|
|
90
|
+
* operator's phone told him. The Notification Center's system-event intake
|
|
91
|
+
* consumes this category and maps it to the `addon-crash-loop` kind.
|
|
92
|
+
*/
|
|
93
|
+
EventCategory["AddonRunnerFailed"] = "addon.runner-failed";
|
|
77
94
|
EventCategory["AddonError"] = "addon.error";
|
|
78
95
|
EventCategory["AddonPageReady"] = "addon.page-ready";
|
|
79
96
|
EventCategory["AddonWidgetReady"] = "addon.widget-ready";
|
|
@@ -7585,6 +7602,10 @@ var RecordingBandSchema = object({
|
|
|
7585
7602
|
preBufferSec: number().min(0).optional(),
|
|
7586
7603
|
postBufferSec: number().min(0).optional()
|
|
7587
7604
|
});
|
|
7605
|
+
({
|
|
7606
|
+
preBufferSec: 10,
|
|
7607
|
+
postBufferSec: 30
|
|
7608
|
+
}).postBufferSec * 1e3;
|
|
7588
7609
|
/**
|
|
7589
7610
|
* Per-device retention overrides. Every field is optional; an unset or `0`
|
|
7590
7611
|
* value inherits the node-wide recorder default. Only footage-lifetime limits
|
|
@@ -7632,6 +7653,12 @@ var RecordingConfigSchema = object({
|
|
|
7632
7653
|
/** DERIVED summary of `bands`, stamped by the recorder on every save.
|
|
7633
7654
|
* Authoring it has no effect — see {@link RecordingStorageModeSchema}. */
|
|
7634
7655
|
mode: RecordingStorageModeSchema.optional(),
|
|
7656
|
+
/**
|
|
7657
|
+
* Which assigned broker slots to record. Absent / empty = {@link
|
|
7658
|
+
* DEFAULT_RECORDING_PROFILES} (`high`+`low`) intersected with the
|
|
7659
|
+
* camera's currently assigned slots — never `mid` unless the operator
|
|
7660
|
+
* picks it, and never a slot the broker has not assigned.
|
|
7661
|
+
*/
|
|
7635
7662
|
profiles: array(CamProfileSchema).optional(),
|
|
7636
7663
|
segmentSeconds: number().int().positive().optional(),
|
|
7637
7664
|
/**
|
|
@@ -7712,7 +7739,11 @@ var RelocateFootageInputSchema = object({
|
|
|
7712
7739
|
profiles: array(string()).optional(),
|
|
7713
7740
|
/** Copy throttle in MB/s (default 40) — the drain is a background chore,
|
|
7714
7741
|
* never allowed to starve live writers. */
|
|
7715
|
-
throttleMbps: number().min(1).max(1e3).optional()
|
|
7742
|
+
throttleMbps: number().min(1).max(1e3).optional(),
|
|
7743
|
+
/** Move only segments whose startMs is >= this. Absent = the whole source
|
|
7744
|
+
* pile. Used when a full drain is too expensive and the operator only
|
|
7745
|
+
* wants the recent window on the new disk. */
|
|
7746
|
+
sinceMs: number().int().optional()
|
|
7716
7747
|
});
|
|
7717
7748
|
/** Internal, lease-scoped participant operation. It is intentionally separate
|
|
7718
7749
|
* from persistent recording settings: a migration never changes
|
|
@@ -14594,6 +14625,7 @@ var NcSystemEventKindSchema = _enum([
|
|
|
14594
14625
|
"node-offline",
|
|
14595
14626
|
"node-inference-unavailable",
|
|
14596
14627
|
"detection-blind",
|
|
14628
|
+
"addon-crash-loop",
|
|
14597
14629
|
"addon-update-available",
|
|
14598
14630
|
"server-update-available",
|
|
14599
14631
|
"alarm-triggered",
|
|
@@ -14601,6 +14633,9 @@ var NcSystemEventKindSchema = _enum([
|
|
|
14601
14633
|
"alarm-disarmed",
|
|
14602
14634
|
"alarm-arming",
|
|
14603
14635
|
"alarm-arm-refused",
|
|
14636
|
+
"addon-updated",
|
|
14637
|
+
"server-updated",
|
|
14638
|
+
"export-completed",
|
|
14604
14639
|
"camera-online",
|
|
14605
14640
|
"camera-offline",
|
|
14606
14641
|
"camera-disabled",
|
|
@@ -16494,13 +16529,19 @@ var RetrainStatusSchema = _enum([
|
|
|
16494
16529
|
* "never marked" from "already trained" must read `retrainStatus`.
|
|
16495
16530
|
*
|
|
16496
16531
|
* `debug` does NOT pin; it is attention, not durability.
|
|
16532
|
+
*
|
|
16533
|
+
* `favourited` IS a BOOLEAN pin (durability bit), not a retrain lifecycle.
|
|
16534
|
+
* A favourited track is skipped by retention the same way `staging` is, but
|
|
16535
|
+
* it does not enter `none|staging|trained` and has no staging budget.
|
|
16497
16536
|
*/
|
|
16498
16537
|
var TrackFlagFields = {
|
|
16499
16538
|
/** Operator marked this track as training material — i.e. `retrainStatus` is
|
|
16500
16539
|
* `'staging'`. */
|
|
16501
16540
|
markForTrain: boolean().optional(),
|
|
16502
16541
|
/** Operator marked this track for diagnostic attention. */
|
|
16503
|
-
debug: boolean().optional()
|
|
16542
|
+
debug: boolean().optional(),
|
|
16543
|
+
/** Operator favourited this track. Pins it against pruning. */
|
|
16544
|
+
favourited: boolean().optional()
|
|
16504
16545
|
};
|
|
16505
16546
|
/**
|
|
16506
16547
|
* The lifecycle field itself, on the READ surfaces only (`Track`, `KeyEvent`).
|
|
@@ -16525,6 +16566,7 @@ var TrackFlagsSchema = object({
|
|
|
16525
16566
|
trackId: string(),
|
|
16526
16567
|
markForTrain: boolean(),
|
|
16527
16568
|
debug: boolean(),
|
|
16569
|
+
favourited: boolean(),
|
|
16528
16570
|
/** The lifecycle state the boolean was derived from. Required here (unlike on
|
|
16529
16571
|
* a track row) because this shape is only ever produced by the write body,
|
|
16530
16572
|
* which always knows it — and a surface that has just written needs to render
|
|
@@ -17157,6 +17199,12 @@ var TrackCascadeCountsSchema = object({
|
|
|
17157
17199
|
/** Per-track CLIP search vectors removed (best-effort). */
|
|
17158
17200
|
embeddings: number().int()
|
|
17159
17201
|
});
|
|
17202
|
+
/** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
|
|
17203
|
+
var DiskReconcileCountsSchema = object({
|
|
17204
|
+
mediaDropped: number().int(),
|
|
17205
|
+
tracks: number().int(),
|
|
17206
|
+
events: number().int()
|
|
17207
|
+
});
|
|
17160
17208
|
/** Event-store footprint for one camera. */
|
|
17161
17209
|
var EventStoreDeviceFootprintSchema = object({
|
|
17162
17210
|
deviceId: number(),
|
|
@@ -17333,6 +17381,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
17333
17381
|
}), method(object({ deviceId: number() }), TrackCascadeCountsSchema, {
|
|
17334
17382
|
kind: "mutation",
|
|
17335
17383
|
auth: "admin"
|
|
17384
|
+
}), method(object({ deviceId: number() }), DiskReconcileCountsSchema, {
|
|
17385
|
+
kind: "mutation",
|
|
17386
|
+
auth: "admin"
|
|
17336
17387
|
}), method(object({
|
|
17337
17388
|
deviceId: number(),
|
|
17338
17389
|
trackIds: array(string()).min(1)
|
|
@@ -18883,6 +18934,24 @@ var CameraStatusDegradationSchema = object({
|
|
|
18883
18934
|
*
|
|
18884
18935
|
* See spec: `docs/superpowers/specs/2026-06-24-camera-status-aggregator-cap.md`
|
|
18885
18936
|
*/
|
|
18937
|
+
var DiskReconcileJobSchema = object({
|
|
18938
|
+
state: _enum([
|
|
18939
|
+
"idle",
|
|
18940
|
+
"running",
|
|
18941
|
+
"done",
|
|
18942
|
+
"error"
|
|
18943
|
+
]),
|
|
18944
|
+
total: number().int().nonnegative(),
|
|
18945
|
+
completed: number().int().nonnegative(),
|
|
18946
|
+
currentDeviceId: number().int().nullable(),
|
|
18947
|
+
failed: array(number().int()).readonly(),
|
|
18948
|
+
mediaDropped: number().int().nonnegative(),
|
|
18949
|
+
tracks: number().int().nonnegative(),
|
|
18950
|
+
events: number().int().nonnegative(),
|
|
18951
|
+
startedAtMs: number().int().nullable(),
|
|
18952
|
+
finishedAtMs: number().int().nullable(),
|
|
18953
|
+
error: string().nullable()
|
|
18954
|
+
});
|
|
18886
18955
|
var CameraStatusSchema = object({
|
|
18887
18956
|
deviceId: number(),
|
|
18888
18957
|
assignment: CameraAssignmentStatusSchema,
|
|
@@ -19114,7 +19183,10 @@ method(StorageMigrationLeaseInputSchema, object({ paused: literal(true) }), {
|
|
|
19114
19183
|
}), CameraSwitchGroupSchema, {
|
|
19115
19184
|
kind: "mutation",
|
|
19116
19185
|
auth: "admin"
|
|
19117
|
-
}), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(),
|
|
19186
|
+
}), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), DiskReconcileJobSchema, {
|
|
19187
|
+
kind: "mutation",
|
|
19188
|
+
auth: "admin"
|
|
19189
|
+
}), method(_void(), DiskReconcileJobSchema, { auth: "admin" }), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
|
|
19118
19190
|
name: string(),
|
|
19119
19191
|
description: string().optional(),
|
|
19120
19192
|
config: CameraPipelineConfigSchema
|
|
@@ -20187,20 +20259,6 @@ var VectorStatsResultSchema = object({
|
|
|
20187
20259
|
exact: boolean()
|
|
20188
20260
|
});
|
|
20189
20261
|
method(VectorDeclareIndexInputSchema, _void(), { kind: "mutation" }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, { kind: "mutation" }), method(VectorQueryInputSchema, VectorQueryResultSchema), method(VectorGetInputSchema, VectorGetResultSchema), method(VectorDeleteInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorStatsInputSchema, VectorStatsResultSchema);
|
|
20190
|
-
/**
|
|
20191
|
-
* `videoclips` — the unified, navigable-clip surface for a camera.
|
|
20192
|
-
*
|
|
20193
|
-
* A device-scoped WRAPPER cap (like `pipeline-analytics`): exactly one active
|
|
20194
|
-
* provider per device, substitutable. The DEFAULT provider (registered by
|
|
20195
|
-
* `addon-post-analysis`, `defaultActive: true`) composes the analytics event
|
|
20196
|
-
* log (markers + thumbnails) with the recorder's `getPlaybackManifest` — a clip
|
|
20197
|
-
* is a time-WINDOW over existing footage, never a separate file. A camera that
|
|
20198
|
-
* exposes NATIVE onboard clips (Reolink/Hikvision NVR) can later substitute the
|
|
20199
|
-
* wrapper provider for its device and serve its own clip catalog + URLs.
|
|
20200
|
-
*
|
|
20201
|
-
* A `Clip` is purely time-based (subtree-blind): playback resolves segments by
|
|
20202
|
-
* temporal overlap, so the API never decides `continuous` vs `events`.
|
|
20203
|
-
*/
|
|
20204
20262
|
var ClipSchema = object({
|
|
20205
20263
|
/** Opaque, provider-namespaced id. The default provider encodes the time
|
|
20206
20264
|
* window so `getClipPlayback` is self-contained (no event re-query). */
|
|
@@ -20217,8 +20275,65 @@ var ClipSchema = object({
|
|
|
20217
20275
|
startMs: number(),
|
|
20218
20276
|
endMs: number()
|
|
20219
20277
|
}),
|
|
20220
|
-
/**
|
|
20221
|
-
|
|
20278
|
+
/**
|
|
20279
|
+
* Distinct object classes attached to this visit (`person`, `car`, …),
|
|
20280
|
+
* dominant first, capped at {@link MAX_CLIP_LABELS}. The ribbon renders these
|
|
20281
|
+
* instead of the bare kind — "Object" tells the operator nothing a colour bar
|
|
20282
|
+
* did not. Absent (never empty) when the visit attached no classified object
|
|
20283
|
+
* event, so a motion/audio-only visit keeps its kind label.
|
|
20284
|
+
*/
|
|
20285
|
+
labels: array(string()).max(3).optional(),
|
|
20286
|
+
/**
|
|
20287
|
+
* Lazy thumbnail URL, never inlined.
|
|
20288
|
+
*
|
|
20289
|
+
* Recording-derived clips (events-mode keep-window, and the prepared
|
|
20290
|
+
* continuous event+fragment visit) MUST use the snapshot of the **main
|
|
20291
|
+
* event of the interval** — `getEventMedia({ eventId, kind: 'snapshot' })`
|
|
20292
|
+
* of the event that owns `kind` (object > motion > audio). Do not extract
|
|
20293
|
+
* a keyframe from the recorded segments. Other providers (onboard, HKSV)
|
|
20294
|
+
* mint their own stills.
|
|
20295
|
+
*
|
|
20296
|
+
* **VOUCHED, never fabricated.** A provider emits this only for an event it
|
|
20297
|
+
* has CONFIRMED owns at least one media row (its own, or its owning track's).
|
|
20298
|
+
* Absent is meaningful — "this visit has no event still" — never "we did not
|
|
20299
|
+
* look". Stamping it from a URL template made 35% of one camera's clips point
|
|
20300
|
+
* at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
|
|
20301
|
+
* A read that FAILS drops the claim; it never invents it.
|
|
20302
|
+
*/
|
|
20303
|
+
thumbnail: string().optional(),
|
|
20304
|
+
/**
|
|
20305
|
+
* An instant INSIDE this visit's footage, hole-safe, where a recorded still
|
|
20306
|
+
* can be decoded. Present whenever the visit came from recorded availability;
|
|
20307
|
+
* absent on a per-event padded window (there is no footage to promise).
|
|
20308
|
+
*
|
|
20309
|
+
* This is not a thumbnail and not a second byte path: it is the argument to
|
|
20310
|
+
* the recorder's existing still route. The surface — never the provider —
|
|
20311
|
+
* decides whether to use it. It is the midpoint of the visit's LONGEST
|
|
20312
|
+
* contiguous range, not of the visit: a visit spans its holes by
|
|
20313
|
+
* construction, so a naive midpoint lands in dead air.
|
|
20314
|
+
*/
|
|
20315
|
+
stillAtMs: number().optional(),
|
|
20316
|
+
/**
|
|
20317
|
+
* Analytics event ids that overlap this visit — a BOUNDED sample, newest
|
|
20318
|
+
* first within kind (object → motion → audio), capped at
|
|
20319
|
+
* {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
|
|
20320
|
+
*
|
|
20321
|
+
* Bounded because it is not a payload the surface pages through: one visit on
|
|
20322
|
+
* device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
|
|
20323
|
+
* camera-day. Read {@link eventCount} for the true total.
|
|
20324
|
+
*/
|
|
20325
|
+
eventIds: array(string()).optional(),
|
|
20326
|
+
/** How many analytics events actually overlap this visit. Differs from
|
|
20327
|
+
* `eventIds.length` exactly when the sample was capped — so a truncated
|
|
20328
|
+
* list is never mistaken for a quiet visit. */
|
|
20329
|
+
eventCount: number().int().nonnegative().optional(),
|
|
20330
|
+
/** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
|
|
20331
|
+
* than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
|
|
20332
|
+
* bar keeps showing them via `recording.getAvailability`. */
|
|
20333
|
+
holes: array(object({
|
|
20334
|
+
startMs: number(),
|
|
20335
|
+
endMs: number()
|
|
20336
|
+
})).optional()
|
|
20222
20337
|
});
|
|
20223
20338
|
var ClipPlaybackSchema = object({
|
|
20224
20339
|
/** HLS master URL through the hub data-plane (Range + token in path). */
|
|
@@ -20682,7 +20797,14 @@ var SearchResultSchema = object({
|
|
|
20682
20797
|
});
|
|
20683
20798
|
var AutoUpdateSettingsSchema = object({
|
|
20684
20799
|
channel: ChannelSchema,
|
|
20685
|
-
intervalSeconds: number()
|
|
20800
|
+
intervalSeconds: number(),
|
|
20801
|
+
/**
|
|
20802
|
+
* Cadence of the "an update exists" POLLER, in seconds. Independent of
|
|
20803
|
+
* `channel`: the poller runs while auto-apply is `off`, because being told
|
|
20804
|
+
* about a publish and installing it are different decisions. Clamped
|
|
20805
|
+
* server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
|
|
20806
|
+
*/
|
|
20807
|
+
updateCheckIntervalSeconds: number()
|
|
20686
20808
|
});
|
|
20687
20809
|
var AddonAutoUpdateSchema = ChannelWithInheritSchema;
|
|
20688
20810
|
var RestartAddonResultSchema = unknown();
|
|
@@ -20823,7 +20945,9 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
|
|
|
20823
20945
|
auth: "admin"
|
|
20824
20946
|
}), method(_void(), AutoUpdateSettingsSchema, { auth: "admin" }), method(object({
|
|
20825
20947
|
channel: ChannelSchema,
|
|
20826
|
-
intervalSeconds: number().min(300).max(86400).optional()
|
|
20948
|
+
intervalSeconds: number().min(300).max(86400).optional(),
|
|
20949
|
+
/** Availability-poll cadence; see AutoUpdateSettingsSchema. */
|
|
20950
|
+
updateCheckIntervalSeconds: number().min(900).max(604800).optional()
|
|
20827
20951
|
}), unknown(), {
|
|
20828
20952
|
kind: "mutation",
|
|
20829
20953
|
auth: "admin"
|
|
@@ -26512,7 +26636,9 @@ var ExportOptionsSchema = object({
|
|
|
26512
26636
|
includeAudio: boolean(),
|
|
26513
26637
|
maxLifeMs: number().int().positive(),
|
|
26514
26638
|
deleteAfterDownload: boolean(),
|
|
26515
|
-
title: string().max(200).optional()
|
|
26639
|
+
title: string().max(200).optional(),
|
|
26640
|
+
/** Notification-output target ids to ping when this export becomes ready. */
|
|
26641
|
+
notifyTargetIds: array(string().min(1)).max(20).optional()
|
|
26516
26642
|
}).superRefine((v, ctx) => {
|
|
26517
26643
|
if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
|
|
26518
26644
|
code: ZodIssueCode.custom,
|
|
@@ -26571,10 +26697,18 @@ var ExportBytesSchema = object({
|
|
|
26571
26697
|
});
|
|
26572
26698
|
method(object({
|
|
26573
26699
|
deviceId: number(),
|
|
26574
|
-
|
|
26700
|
+
/** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
|
|
26701
|
+
profile: string().optional(),
|
|
26702
|
+
profiles: array(string()).min(1).optional(),
|
|
26575
26703
|
fromMs: number(),
|
|
26576
26704
|
toMs: number(),
|
|
26577
26705
|
options: ExportOptionsSchema
|
|
26706
|
+
}).superRefine((v, ctx) => {
|
|
26707
|
+
if ((v.profiles !== void 0 && v.profiles.length > 0 ? v.profiles : v.profile !== void 0 ? [v.profile] : []).length < 1) ctx.addIssue({
|
|
26708
|
+
code: ZodIssueCode.custom,
|
|
26709
|
+
message: "pass profiles[] (min 1) or legacy profile",
|
|
26710
|
+
path: ["profiles"]
|
|
26711
|
+
});
|
|
26578
26712
|
}), ExportRecordSchema, {
|
|
26579
26713
|
kind: "mutation",
|
|
26580
26714
|
auth: "protected"
|
|
@@ -29516,15 +29650,6 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
29516
29650
|
labels: ["probe not implemented"]
|
|
29517
29651
|
};
|
|
29518
29652
|
}
|
|
29519
|
-
/**
|
|
29520
|
-
* Top-level devices restored at once in {@link onRestoreDevices}.
|
|
29521
|
-
*
|
|
29522
|
-
* Four covers the fleets this ships to without turning a boot into a burst a
|
|
29523
|
-
* camera NVR answers with a refusal. A provider whose upstream is a single
|
|
29524
|
-
* session with a serial command channel (a Baichuan hub, an NVR that
|
|
29525
|
-
* serialises ISAPI) should lower it; nothing needs to raise it.
|
|
29526
|
-
*/
|
|
29527
|
-
restoreConcurrency = 4;
|
|
29528
29653
|
async restoreDevices(savedDevices) {
|
|
29529
29654
|
await this.onRestoreDevices(savedDevices);
|
|
29530
29655
|
if (savedDevices.length > 0) this.ctx.logger.info(`Restored ${savedDevices.length} ${this.providerName} device(s)`);
|
|
@@ -29579,14 +29704,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
29579
29704
|
});
|
|
29580
29705
|
}
|
|
29581
29706
|
};
|
|
29582
|
-
|
|
29583
|
-
await Promise.all(Array.from({ length: Math.min(Math.max(1, this.restoreConcurrency), topLevel.length) }, async () => {
|
|
29584
|
-
for (;;) {
|
|
29585
|
-
const saved = topLevel[nextTopLevel++];
|
|
29586
|
-
if (saved === void 0) return;
|
|
29587
|
-
await restoreOne(saved);
|
|
29588
|
-
}
|
|
29589
|
-
}));
|
|
29707
|
+
await Promise.all(topLevel.map((saved) => restoreOne(saved)));
|
|
29590
29708
|
const childRows = savedDevices.filter((s) => s.parentDeviceId !== null);
|
|
29591
29709
|
for (const saved of childRows) {
|
|
29592
29710
|
const Class = this.deviceClasses[saved.type];
|
|
@@ -32859,6 +32977,12 @@ Object.freeze({
|
|
|
32859
32977
|
addonId: null,
|
|
32860
32978
|
access: "create"
|
|
32861
32979
|
},
|
|
32980
|
+
"pipelineAnalytics.reconcileFromDisk": {
|
|
32981
|
+
capName: "pipeline-analytics",
|
|
32982
|
+
capScope: "device",
|
|
32983
|
+
addonId: null,
|
|
32984
|
+
access: "create"
|
|
32985
|
+
},
|
|
32862
32986
|
"pipelineAnalytics.refreshStorageLocationsForMigration": {
|
|
32863
32987
|
capName: "pipeline-analytics",
|
|
32864
32988
|
capScope: "device",
|
|
@@ -33261,6 +33385,12 @@ Object.freeze({
|
|
|
33261
33385
|
addonId: null,
|
|
33262
33386
|
access: "view"
|
|
33263
33387
|
},
|
|
33388
|
+
"pipelineOrchestrator.getReconcileFromDiskStatus": {
|
|
33389
|
+
capName: "pipeline-orchestrator",
|
|
33390
|
+
capScope: "system",
|
|
33391
|
+
addonId: null,
|
|
33392
|
+
access: "view"
|
|
33393
|
+
},
|
|
33264
33394
|
"pipelineOrchestrator.listAgentSettings": {
|
|
33265
33395
|
capName: "pipeline-orchestrator",
|
|
33266
33396
|
capScope: "system",
|
|
@@ -33285,6 +33415,12 @@ Object.freeze({
|
|
|
33285
33415
|
addonId: null,
|
|
33286
33416
|
access: "create"
|
|
33287
33417
|
},
|
|
33418
|
+
"pipelineOrchestrator.reconcileFromDisk": {
|
|
33419
|
+
capName: "pipeline-orchestrator",
|
|
33420
|
+
capScope: "system",
|
|
33421
|
+
addonId: null,
|
|
33422
|
+
access: "create"
|
|
33423
|
+
},
|
|
33288
33424
|
"pipelineOrchestrator.removeAgentSettings": {
|
|
33289
33425
|
capName: "pipeline-orchestrator",
|
|
33290
33426
|
capScope: "system",
|
|
@@ -36276,6 +36412,11 @@ Object.freeze({
|
|
|
36276
36412
|
form: "single",
|
|
36277
36413
|
optional: true
|
|
36278
36414
|
}],
|
|
36415
|
+
"pipelineAnalytics.reconcileFromDisk": [{
|
|
36416
|
+
name: "deviceId",
|
|
36417
|
+
form: "single",
|
|
36418
|
+
optional: false
|
|
36419
|
+
}],
|
|
36279
36420
|
"pipelineAnalytics.restageRetrainTrack": [{
|
|
36280
36421
|
name: "deviceId",
|
|
36281
36422
|
form: "single",
|
|
@@ -37341,6 +37482,8 @@ DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb;
|
|
|
37341
37482
|
DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
|
|
37342
37483
|
DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
|
|
37343
37484
|
DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
37485
|
+
var MB = 1024 * 1024;
|
|
37486
|
+
1024 * MB, 3072 * MB;
|
|
37344
37487
|
//#endregion
|
|
37345
37488
|
//#region src/config.ts
|
|
37346
37489
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-provider-dreo",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.22",
|
|
4
4
|
"description": "Dreo smart-device (fan / air-circulator / purifier / heater / humidifier) device-provider addon for CamStack — wraps the @apocaliss92/nodedreo Dreo cloud client (REST + WebSocket)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|