@camstack/addon-provider-dreo 0.2.20 → 0.2.21
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 +179 -44
- package/dist/addon.mjs +179 -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,57 @@ var ClipSchema = object({
|
|
|
20216
20274
|
startMs: number(),
|
|
20217
20275
|
endMs: number()
|
|
20218
20276
|
}),
|
|
20219
|
-
/**
|
|
20220
|
-
thumbnail
|
|
20277
|
+
/**
|
|
20278
|
+
* Lazy thumbnail URL, never inlined.
|
|
20279
|
+
*
|
|
20280
|
+
* Recording-derived clips (events-mode keep-window, and the prepared
|
|
20281
|
+
* continuous event+fragment visit) MUST use the snapshot of the **main
|
|
20282
|
+
* event of the interval** — `getEventMedia({ eventId, kind: 'snapshot' })`
|
|
20283
|
+
* of the event that owns `kind` (object > motion > audio). Do not extract
|
|
20284
|
+
* a keyframe from the recorded segments. Other providers (onboard, HKSV)
|
|
20285
|
+
* mint their own stills.
|
|
20286
|
+
*
|
|
20287
|
+
* **VOUCHED, never fabricated.** A provider emits this only for an event it
|
|
20288
|
+
* has CONFIRMED owns at least one media row (its own, or its owning track's).
|
|
20289
|
+
* Absent is meaningful — "this visit has no event still" — never "we did not
|
|
20290
|
+
* look". Stamping it from a URL template made 35% of one camera's clips point
|
|
20291
|
+
* at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
|
|
20292
|
+
* A read that FAILS drops the claim; it never invents it.
|
|
20293
|
+
*/
|
|
20294
|
+
thumbnail: string().optional(),
|
|
20295
|
+
/**
|
|
20296
|
+
* An instant INSIDE this visit's footage, hole-safe, where a recorded still
|
|
20297
|
+
* can be decoded. Present whenever the visit came from recorded availability;
|
|
20298
|
+
* absent on a per-event padded window (there is no footage to promise).
|
|
20299
|
+
*
|
|
20300
|
+
* This is not a thumbnail and not a second byte path: it is the argument to
|
|
20301
|
+
* the recorder's existing still route. The surface — never the provider —
|
|
20302
|
+
* decides whether to use it. It is the midpoint of the visit's LONGEST
|
|
20303
|
+
* contiguous range, not of the visit: a visit spans its holes by
|
|
20304
|
+
* construction, so a naive midpoint lands in dead air.
|
|
20305
|
+
*/
|
|
20306
|
+
stillAtMs: number().optional(),
|
|
20307
|
+
/**
|
|
20308
|
+
* Analytics event ids that overlap this visit — a BOUNDED sample, newest
|
|
20309
|
+
* first within kind (object → motion → audio), capped at
|
|
20310
|
+
* {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
|
|
20311
|
+
*
|
|
20312
|
+
* Bounded because it is not a payload the surface pages through: one visit on
|
|
20313
|
+
* device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
|
|
20314
|
+
* camera-day. Read {@link eventCount} for the true total.
|
|
20315
|
+
*/
|
|
20316
|
+
eventIds: array(string()).optional(),
|
|
20317
|
+
/** How many analytics events actually overlap this visit. Differs from
|
|
20318
|
+
* `eventIds.length` exactly when the sample was capped — so a truncated
|
|
20319
|
+
* list is never mistaken for a quiet visit. */
|
|
20320
|
+
eventCount: number().int().nonnegative().optional(),
|
|
20321
|
+
/** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
|
|
20322
|
+
* than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
|
|
20323
|
+
* bar keeps showing them via `recording.getAvailability`. */
|
|
20324
|
+
holes: array(object({
|
|
20325
|
+
startMs: number(),
|
|
20326
|
+
endMs: number()
|
|
20327
|
+
})).optional()
|
|
20221
20328
|
});
|
|
20222
20329
|
var ClipPlaybackSchema = object({
|
|
20223
20330
|
/** HLS master URL through the hub data-plane (Range + token in path). */
|
|
@@ -20681,7 +20788,14 @@ var SearchResultSchema = object({
|
|
|
20681
20788
|
});
|
|
20682
20789
|
var AutoUpdateSettingsSchema = object({
|
|
20683
20790
|
channel: ChannelSchema,
|
|
20684
|
-
intervalSeconds: number()
|
|
20791
|
+
intervalSeconds: number(),
|
|
20792
|
+
/**
|
|
20793
|
+
* Cadence of the "an update exists" POLLER, in seconds. Independent of
|
|
20794
|
+
* `channel`: the poller runs while auto-apply is `off`, because being told
|
|
20795
|
+
* about a publish and installing it are different decisions. Clamped
|
|
20796
|
+
* server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
|
|
20797
|
+
*/
|
|
20798
|
+
updateCheckIntervalSeconds: number()
|
|
20685
20799
|
});
|
|
20686
20800
|
var AddonAutoUpdateSchema = ChannelWithInheritSchema;
|
|
20687
20801
|
var RestartAddonResultSchema = unknown();
|
|
@@ -20822,7 +20936,9 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
|
|
|
20822
20936
|
auth: "admin"
|
|
20823
20937
|
}), method(_void(), AutoUpdateSettingsSchema, { auth: "admin" }), method(object({
|
|
20824
20938
|
channel: ChannelSchema,
|
|
20825
|
-
intervalSeconds: number().min(300).max(86400).optional()
|
|
20939
|
+
intervalSeconds: number().min(300).max(86400).optional(),
|
|
20940
|
+
/** Availability-poll cadence; see AutoUpdateSettingsSchema. */
|
|
20941
|
+
updateCheckIntervalSeconds: number().min(900).max(604800).optional()
|
|
20826
20942
|
}), unknown(), {
|
|
20827
20943
|
kind: "mutation",
|
|
20828
20944
|
auth: "admin"
|
|
@@ -26511,7 +26627,9 @@ var ExportOptionsSchema = object({
|
|
|
26511
26627
|
includeAudio: boolean(),
|
|
26512
26628
|
maxLifeMs: number().int().positive(),
|
|
26513
26629
|
deleteAfterDownload: boolean(),
|
|
26514
|
-
title: string().max(200).optional()
|
|
26630
|
+
title: string().max(200).optional(),
|
|
26631
|
+
/** Notification-output target ids to ping when this export becomes ready. */
|
|
26632
|
+
notifyTargetIds: array(string().min(1)).max(20).optional()
|
|
26515
26633
|
}).superRefine((v, ctx) => {
|
|
26516
26634
|
if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
|
|
26517
26635
|
code: ZodIssueCode.custom,
|
|
@@ -26570,10 +26688,18 @@ var ExportBytesSchema = object({
|
|
|
26570
26688
|
});
|
|
26571
26689
|
method(object({
|
|
26572
26690
|
deviceId: number(),
|
|
26573
|
-
|
|
26691
|
+
/** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
|
|
26692
|
+
profile: string().optional(),
|
|
26693
|
+
profiles: array(string()).min(1).optional(),
|
|
26574
26694
|
fromMs: number(),
|
|
26575
26695
|
toMs: number(),
|
|
26576
26696
|
options: ExportOptionsSchema
|
|
26697
|
+
}).superRefine((v, ctx) => {
|
|
26698
|
+
if ((v.profiles !== void 0 && v.profiles.length > 0 ? v.profiles : v.profile !== void 0 ? [v.profile] : []).length < 1) ctx.addIssue({
|
|
26699
|
+
code: ZodIssueCode.custom,
|
|
26700
|
+
message: "pass profiles[] (min 1) or legacy profile",
|
|
26701
|
+
path: ["profiles"]
|
|
26702
|
+
});
|
|
26577
26703
|
}), ExportRecordSchema, {
|
|
26578
26704
|
kind: "mutation",
|
|
26579
26705
|
auth: "protected"
|
|
@@ -29515,15 +29641,6 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
29515
29641
|
labels: ["probe not implemented"]
|
|
29516
29642
|
};
|
|
29517
29643
|
}
|
|
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
29644
|
async restoreDevices(savedDevices) {
|
|
29528
29645
|
await this.onRestoreDevices(savedDevices);
|
|
29529
29646
|
if (savedDevices.length > 0) this.ctx.logger.info(`Restored ${savedDevices.length} ${this.providerName} device(s)`);
|
|
@@ -29578,14 +29695,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
29578
29695
|
});
|
|
29579
29696
|
}
|
|
29580
29697
|
};
|
|
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
|
-
}));
|
|
29698
|
+
await Promise.all(topLevel.map((saved) => restoreOne(saved)));
|
|
29589
29699
|
const childRows = savedDevices.filter((s) => s.parentDeviceId !== null);
|
|
29590
29700
|
for (const saved of childRows) {
|
|
29591
29701
|
const Class = this.deviceClasses[saved.type];
|
|
@@ -32858,6 +32968,12 @@ Object.freeze({
|
|
|
32858
32968
|
addonId: null,
|
|
32859
32969
|
access: "create"
|
|
32860
32970
|
},
|
|
32971
|
+
"pipelineAnalytics.reconcileFromDisk": {
|
|
32972
|
+
capName: "pipeline-analytics",
|
|
32973
|
+
capScope: "device",
|
|
32974
|
+
addonId: null,
|
|
32975
|
+
access: "create"
|
|
32976
|
+
},
|
|
32861
32977
|
"pipelineAnalytics.refreshStorageLocationsForMigration": {
|
|
32862
32978
|
capName: "pipeline-analytics",
|
|
32863
32979
|
capScope: "device",
|
|
@@ -33260,6 +33376,12 @@ Object.freeze({
|
|
|
33260
33376
|
addonId: null,
|
|
33261
33377
|
access: "view"
|
|
33262
33378
|
},
|
|
33379
|
+
"pipelineOrchestrator.getReconcileFromDiskStatus": {
|
|
33380
|
+
capName: "pipeline-orchestrator",
|
|
33381
|
+
capScope: "system",
|
|
33382
|
+
addonId: null,
|
|
33383
|
+
access: "view"
|
|
33384
|
+
},
|
|
33263
33385
|
"pipelineOrchestrator.listAgentSettings": {
|
|
33264
33386
|
capName: "pipeline-orchestrator",
|
|
33265
33387
|
capScope: "system",
|
|
@@ -33284,6 +33406,12 @@ Object.freeze({
|
|
|
33284
33406
|
addonId: null,
|
|
33285
33407
|
access: "create"
|
|
33286
33408
|
},
|
|
33409
|
+
"pipelineOrchestrator.reconcileFromDisk": {
|
|
33410
|
+
capName: "pipeline-orchestrator",
|
|
33411
|
+
capScope: "system",
|
|
33412
|
+
addonId: null,
|
|
33413
|
+
access: "create"
|
|
33414
|
+
},
|
|
33287
33415
|
"pipelineOrchestrator.removeAgentSettings": {
|
|
33288
33416
|
capName: "pipeline-orchestrator",
|
|
33289
33417
|
capScope: "system",
|
|
@@ -36275,6 +36403,11 @@ Object.freeze({
|
|
|
36275
36403
|
form: "single",
|
|
36276
36404
|
optional: true
|
|
36277
36405
|
}],
|
|
36406
|
+
"pipelineAnalytics.reconcileFromDisk": [{
|
|
36407
|
+
name: "deviceId",
|
|
36408
|
+
form: "single",
|
|
36409
|
+
optional: false
|
|
36410
|
+
}],
|
|
36278
36411
|
"pipelineAnalytics.restageRetrainTrack": [{
|
|
36279
36412
|
name: "deviceId",
|
|
36280
36413
|
form: "single",
|
|
@@ -37340,6 +37473,8 @@ DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb;
|
|
|
37340
37473
|
DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
|
|
37341
37474
|
DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
|
|
37342
37475
|
DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
37476
|
+
var MB = 1024 * 1024;
|
|
37477
|
+
1024 * MB, 3072 * MB;
|
|
37343
37478
|
//#endregion
|
|
37344
37479
|
//#region src/config.ts
|
|
37345
37480
|
/**
|
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,57 @@ var ClipSchema = object({
|
|
|
20217
20275
|
startMs: number(),
|
|
20218
20276
|
endMs: number()
|
|
20219
20277
|
}),
|
|
20220
|
-
/**
|
|
20221
|
-
thumbnail
|
|
20278
|
+
/**
|
|
20279
|
+
* Lazy thumbnail URL, never inlined.
|
|
20280
|
+
*
|
|
20281
|
+
* Recording-derived clips (events-mode keep-window, and the prepared
|
|
20282
|
+
* continuous event+fragment visit) MUST use the snapshot of the **main
|
|
20283
|
+
* event of the interval** — `getEventMedia({ eventId, kind: 'snapshot' })`
|
|
20284
|
+
* of the event that owns `kind` (object > motion > audio). Do not extract
|
|
20285
|
+
* a keyframe from the recorded segments. Other providers (onboard, HKSV)
|
|
20286
|
+
* mint their own stills.
|
|
20287
|
+
*
|
|
20288
|
+
* **VOUCHED, never fabricated.** A provider emits this only for an event it
|
|
20289
|
+
* has CONFIRMED owns at least one media row (its own, or its owning track's).
|
|
20290
|
+
* Absent is meaningful — "this visit has no event still" — never "we did not
|
|
20291
|
+
* look". Stamping it from a URL template made 35% of one camera's clips point
|
|
20292
|
+
* at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
|
|
20293
|
+
* A read that FAILS drops the claim; it never invents it.
|
|
20294
|
+
*/
|
|
20295
|
+
thumbnail: string().optional(),
|
|
20296
|
+
/**
|
|
20297
|
+
* An instant INSIDE this visit's footage, hole-safe, where a recorded still
|
|
20298
|
+
* can be decoded. Present whenever the visit came from recorded availability;
|
|
20299
|
+
* absent on a per-event padded window (there is no footage to promise).
|
|
20300
|
+
*
|
|
20301
|
+
* This is not a thumbnail and not a second byte path: it is the argument to
|
|
20302
|
+
* the recorder's existing still route. The surface — never the provider —
|
|
20303
|
+
* decides whether to use it. It is the midpoint of the visit's LONGEST
|
|
20304
|
+
* contiguous range, not of the visit: a visit spans its holes by
|
|
20305
|
+
* construction, so a naive midpoint lands in dead air.
|
|
20306
|
+
*/
|
|
20307
|
+
stillAtMs: number().optional(),
|
|
20308
|
+
/**
|
|
20309
|
+
* Analytics event ids that overlap this visit — a BOUNDED sample, newest
|
|
20310
|
+
* first within kind (object → motion → audio), capped at
|
|
20311
|
+
* {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
|
|
20312
|
+
*
|
|
20313
|
+
* Bounded because it is not a payload the surface pages through: one visit on
|
|
20314
|
+
* device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
|
|
20315
|
+
* camera-day. Read {@link eventCount} for the true total.
|
|
20316
|
+
*/
|
|
20317
|
+
eventIds: array(string()).optional(),
|
|
20318
|
+
/** How many analytics events actually overlap this visit. Differs from
|
|
20319
|
+
* `eventIds.length` exactly when the sample was capped — so a truncated
|
|
20320
|
+
* list is never mistaken for a quiet visit. */
|
|
20321
|
+
eventCount: number().int().nonnegative().optional(),
|
|
20322
|
+
/** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
|
|
20323
|
+
* than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
|
|
20324
|
+
* bar keeps showing them via `recording.getAvailability`. */
|
|
20325
|
+
holes: array(object({
|
|
20326
|
+
startMs: number(),
|
|
20327
|
+
endMs: number()
|
|
20328
|
+
})).optional()
|
|
20222
20329
|
});
|
|
20223
20330
|
var ClipPlaybackSchema = object({
|
|
20224
20331
|
/** HLS master URL through the hub data-plane (Range + token in path). */
|
|
@@ -20682,7 +20789,14 @@ var SearchResultSchema = object({
|
|
|
20682
20789
|
});
|
|
20683
20790
|
var AutoUpdateSettingsSchema = object({
|
|
20684
20791
|
channel: ChannelSchema,
|
|
20685
|
-
intervalSeconds: number()
|
|
20792
|
+
intervalSeconds: number(),
|
|
20793
|
+
/**
|
|
20794
|
+
* Cadence of the "an update exists" POLLER, in seconds. Independent of
|
|
20795
|
+
* `channel`: the poller runs while auto-apply is `off`, because being told
|
|
20796
|
+
* about a publish and installing it are different decisions. Clamped
|
|
20797
|
+
* server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
|
|
20798
|
+
*/
|
|
20799
|
+
updateCheckIntervalSeconds: number()
|
|
20686
20800
|
});
|
|
20687
20801
|
var AddonAutoUpdateSchema = ChannelWithInheritSchema;
|
|
20688
20802
|
var RestartAddonResultSchema = unknown();
|
|
@@ -20823,7 +20937,9 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
|
|
|
20823
20937
|
auth: "admin"
|
|
20824
20938
|
}), method(_void(), AutoUpdateSettingsSchema, { auth: "admin" }), method(object({
|
|
20825
20939
|
channel: ChannelSchema,
|
|
20826
|
-
intervalSeconds: number().min(300).max(86400).optional()
|
|
20940
|
+
intervalSeconds: number().min(300).max(86400).optional(),
|
|
20941
|
+
/** Availability-poll cadence; see AutoUpdateSettingsSchema. */
|
|
20942
|
+
updateCheckIntervalSeconds: number().min(900).max(604800).optional()
|
|
20827
20943
|
}), unknown(), {
|
|
20828
20944
|
kind: "mutation",
|
|
20829
20945
|
auth: "admin"
|
|
@@ -26512,7 +26628,9 @@ var ExportOptionsSchema = object({
|
|
|
26512
26628
|
includeAudio: boolean(),
|
|
26513
26629
|
maxLifeMs: number().int().positive(),
|
|
26514
26630
|
deleteAfterDownload: boolean(),
|
|
26515
|
-
title: string().max(200).optional()
|
|
26631
|
+
title: string().max(200).optional(),
|
|
26632
|
+
/** Notification-output target ids to ping when this export becomes ready. */
|
|
26633
|
+
notifyTargetIds: array(string().min(1)).max(20).optional()
|
|
26516
26634
|
}).superRefine((v, ctx) => {
|
|
26517
26635
|
if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
|
|
26518
26636
|
code: ZodIssueCode.custom,
|
|
@@ -26571,10 +26689,18 @@ var ExportBytesSchema = object({
|
|
|
26571
26689
|
});
|
|
26572
26690
|
method(object({
|
|
26573
26691
|
deviceId: number(),
|
|
26574
|
-
|
|
26692
|
+
/** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
|
|
26693
|
+
profile: string().optional(),
|
|
26694
|
+
profiles: array(string()).min(1).optional(),
|
|
26575
26695
|
fromMs: number(),
|
|
26576
26696
|
toMs: number(),
|
|
26577
26697
|
options: ExportOptionsSchema
|
|
26698
|
+
}).superRefine((v, ctx) => {
|
|
26699
|
+
if ((v.profiles !== void 0 && v.profiles.length > 0 ? v.profiles : v.profile !== void 0 ? [v.profile] : []).length < 1) ctx.addIssue({
|
|
26700
|
+
code: ZodIssueCode.custom,
|
|
26701
|
+
message: "pass profiles[] (min 1) or legacy profile",
|
|
26702
|
+
path: ["profiles"]
|
|
26703
|
+
});
|
|
26578
26704
|
}), ExportRecordSchema, {
|
|
26579
26705
|
kind: "mutation",
|
|
26580
26706
|
auth: "protected"
|
|
@@ -29516,15 +29642,6 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
29516
29642
|
labels: ["probe not implemented"]
|
|
29517
29643
|
};
|
|
29518
29644
|
}
|
|
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
29645
|
async restoreDevices(savedDevices) {
|
|
29529
29646
|
await this.onRestoreDevices(savedDevices);
|
|
29530
29647
|
if (savedDevices.length > 0) this.ctx.logger.info(`Restored ${savedDevices.length} ${this.providerName} device(s)`);
|
|
@@ -29579,14 +29696,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
29579
29696
|
});
|
|
29580
29697
|
}
|
|
29581
29698
|
};
|
|
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
|
-
}));
|
|
29699
|
+
await Promise.all(topLevel.map((saved) => restoreOne(saved)));
|
|
29590
29700
|
const childRows = savedDevices.filter((s) => s.parentDeviceId !== null);
|
|
29591
29701
|
for (const saved of childRows) {
|
|
29592
29702
|
const Class = this.deviceClasses[saved.type];
|
|
@@ -32859,6 +32969,12 @@ Object.freeze({
|
|
|
32859
32969
|
addonId: null,
|
|
32860
32970
|
access: "create"
|
|
32861
32971
|
},
|
|
32972
|
+
"pipelineAnalytics.reconcileFromDisk": {
|
|
32973
|
+
capName: "pipeline-analytics",
|
|
32974
|
+
capScope: "device",
|
|
32975
|
+
addonId: null,
|
|
32976
|
+
access: "create"
|
|
32977
|
+
},
|
|
32862
32978
|
"pipelineAnalytics.refreshStorageLocationsForMigration": {
|
|
32863
32979
|
capName: "pipeline-analytics",
|
|
32864
32980
|
capScope: "device",
|
|
@@ -33261,6 +33377,12 @@ Object.freeze({
|
|
|
33261
33377
|
addonId: null,
|
|
33262
33378
|
access: "view"
|
|
33263
33379
|
},
|
|
33380
|
+
"pipelineOrchestrator.getReconcileFromDiskStatus": {
|
|
33381
|
+
capName: "pipeline-orchestrator",
|
|
33382
|
+
capScope: "system",
|
|
33383
|
+
addonId: null,
|
|
33384
|
+
access: "view"
|
|
33385
|
+
},
|
|
33264
33386
|
"pipelineOrchestrator.listAgentSettings": {
|
|
33265
33387
|
capName: "pipeline-orchestrator",
|
|
33266
33388
|
capScope: "system",
|
|
@@ -33285,6 +33407,12 @@ Object.freeze({
|
|
|
33285
33407
|
addonId: null,
|
|
33286
33408
|
access: "create"
|
|
33287
33409
|
},
|
|
33410
|
+
"pipelineOrchestrator.reconcileFromDisk": {
|
|
33411
|
+
capName: "pipeline-orchestrator",
|
|
33412
|
+
capScope: "system",
|
|
33413
|
+
addonId: null,
|
|
33414
|
+
access: "create"
|
|
33415
|
+
},
|
|
33288
33416
|
"pipelineOrchestrator.removeAgentSettings": {
|
|
33289
33417
|
capName: "pipeline-orchestrator",
|
|
33290
33418
|
capScope: "system",
|
|
@@ -36276,6 +36404,11 @@ Object.freeze({
|
|
|
36276
36404
|
form: "single",
|
|
36277
36405
|
optional: true
|
|
36278
36406
|
}],
|
|
36407
|
+
"pipelineAnalytics.reconcileFromDisk": [{
|
|
36408
|
+
name: "deviceId",
|
|
36409
|
+
form: "single",
|
|
36410
|
+
optional: false
|
|
36411
|
+
}],
|
|
36279
36412
|
"pipelineAnalytics.restageRetrainTrack": [{
|
|
36280
36413
|
name: "deviceId",
|
|
36281
36414
|
form: "single",
|
|
@@ -37341,6 +37474,8 @@ DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb;
|
|
|
37341
37474
|
DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
|
|
37342
37475
|
DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
|
|
37343
37476
|
DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
37477
|
+
var MB = 1024 * 1024;
|
|
37478
|
+
1024 * MB, 3072 * MB;
|
|
37344
37479
|
//#endregion
|
|
37345
37480
|
//#region src/config.ts
|
|
37346
37481
|
/**
|
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.21",
|
|
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",
|