@camstack/addon-provider-homematic 1.2.21 → 1.2.23
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
|
@@ -8,7 +8,7 @@ let http = require("http");
|
|
|
8
8
|
let crypto$1 = require("crypto");
|
|
9
9
|
let fs_promises = require("fs/promises");
|
|
10
10
|
let path = require("path");
|
|
11
|
-
//#region ../types/dist/event-category-
|
|
11
|
+
//#region ../types/dist/event-category-XfKNtfCc.mjs
|
|
12
12
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
13
13
|
EventCategory["SystemBoot"] = "system.boot";
|
|
14
14
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -25,9 +25,10 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
25
25
|
EventCategory["SystemRestartCompleted"] = "system.restart-completed";
|
|
26
26
|
/**
|
|
27
27
|
* A newer addon or server-root package version was found by the
|
|
28
|
-
* authoritative registry check. Emitted once
|
|
29
|
-
* `(
|
|
30
|
-
*
|
|
28
|
+
* authoritative registry check. Emitted once when any observed
|
|
29
|
+
* `latestVersion` changes (or a package/node first appears behind);
|
|
30
|
+
* the payload carries the full currently-available list. Repeated
|
|
31
|
+
* polling of the same latests is silent.
|
|
31
32
|
*/
|
|
32
33
|
EventCategory["UpdateAvailable"] = "update.available";
|
|
33
34
|
/**
|
|
@@ -46,6 +47,22 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
46
47
|
EventCategory["AddonInstalled"] = "addon.installed";
|
|
47
48
|
EventCategory["AddonUninstalled"] = "addon.uninstalled";
|
|
48
49
|
EventCategory["AddonCrashed"] = "addon.crashed";
|
|
50
|
+
/**
|
|
51
|
+
* A RUNNER the D6 crash circuit-breaker gave up on — terminal.
|
|
52
|
+
*
|
|
53
|
+
* `AddonCrashed` is the routine, self-healing fact ("it crashed; it is being
|
|
54
|
+
* respawned"); this is the one that never resolves itself. Emitted exactly
|
|
55
|
+
* once per trip, by `process-service.ts`, carrying the node, the runner, the
|
|
56
|
+
* addons it hosted and the crash count that tripped the breaker.
|
|
57
|
+
*
|
|
58
|
+
* It exists because a runner marked terminally `failed` used to be SILENT:
|
|
59
|
+
* on 2026-08-18 the `recorder` runner died of three unhandled ffmpeg spawn
|
|
60
|
+
* errors, the breaker stopped respawning it (correctly), `/health` kept
|
|
61
|
+
* returning 200, and fleet-wide recording was gone for 3h15 before the
|
|
62
|
+
* operator's phone told him. The Notification Center's system-event intake
|
|
63
|
+
* consumes this category and maps it to the `addon-crash-loop` kind.
|
|
64
|
+
*/
|
|
65
|
+
EventCategory["AddonRunnerFailed"] = "addon.runner-failed";
|
|
49
66
|
EventCategory["AddonError"] = "addon.error";
|
|
50
67
|
EventCategory["AddonPageReady"] = "addon.page-ready";
|
|
51
68
|
EventCategory["AddonWidgetReady"] = "addon.widget-ready";
|
|
@@ -7557,6 +7574,10 @@ var RecordingBandSchema = object({
|
|
|
7557
7574
|
preBufferSec: number().min(0).optional(),
|
|
7558
7575
|
postBufferSec: number().min(0).optional()
|
|
7559
7576
|
});
|
|
7577
|
+
({
|
|
7578
|
+
preBufferSec: 10,
|
|
7579
|
+
postBufferSec: 30
|
|
7580
|
+
}).postBufferSec * 1e3;
|
|
7560
7581
|
/**
|
|
7561
7582
|
* Per-device retention overrides. Every field is optional; an unset or `0`
|
|
7562
7583
|
* value inherits the node-wide recorder default. Only footage-lifetime limits
|
|
@@ -7604,6 +7625,12 @@ var RecordingConfigSchema = object({
|
|
|
7604
7625
|
/** DERIVED summary of `bands`, stamped by the recorder on every save.
|
|
7605
7626
|
* Authoring it has no effect — see {@link RecordingStorageModeSchema}. */
|
|
7606
7627
|
mode: RecordingStorageModeSchema.optional(),
|
|
7628
|
+
/**
|
|
7629
|
+
* Which assigned broker slots to record. Absent / empty = {@link
|
|
7630
|
+
* DEFAULT_RECORDING_PROFILES} (`high`+`low`) intersected with the
|
|
7631
|
+
* camera's currently assigned slots — never `mid` unless the operator
|
|
7632
|
+
* picks it, and never a slot the broker has not assigned.
|
|
7633
|
+
*/
|
|
7607
7634
|
profiles: array(CamProfileSchema).optional(),
|
|
7608
7635
|
segmentSeconds: number().int().positive().optional(),
|
|
7609
7636
|
/**
|
|
@@ -7684,7 +7711,11 @@ var RelocateFootageInputSchema = object({
|
|
|
7684
7711
|
profiles: array(string()).optional(),
|
|
7685
7712
|
/** Copy throttle in MB/s (default 40) — the drain is a background chore,
|
|
7686
7713
|
* never allowed to starve live writers. */
|
|
7687
|
-
throttleMbps: number().min(1).max(1e3).optional()
|
|
7714
|
+
throttleMbps: number().min(1).max(1e3).optional(),
|
|
7715
|
+
/** Move only segments whose startMs is >= this. Absent = the whole source
|
|
7716
|
+
* pile. Used when a full drain is too expensive and the operator only
|
|
7717
|
+
* wants the recent window on the new disk. */
|
|
7718
|
+
sinceMs: number().int().optional()
|
|
7688
7719
|
});
|
|
7689
7720
|
/** Internal, lease-scoped participant operation. It is intentionally separate
|
|
7690
7721
|
* from persistent recording settings: a migration never changes
|
|
@@ -14615,6 +14646,7 @@ var NcSystemEventKindSchema = _enum([
|
|
|
14615
14646
|
"node-offline",
|
|
14616
14647
|
"node-inference-unavailable",
|
|
14617
14648
|
"detection-blind",
|
|
14649
|
+
"addon-crash-loop",
|
|
14618
14650
|
"addon-update-available",
|
|
14619
14651
|
"server-update-available",
|
|
14620
14652
|
"alarm-triggered",
|
|
@@ -14622,6 +14654,9 @@ var NcSystemEventKindSchema = _enum([
|
|
|
14622
14654
|
"alarm-disarmed",
|
|
14623
14655
|
"alarm-arming",
|
|
14624
14656
|
"alarm-arm-refused",
|
|
14657
|
+
"addon-updated",
|
|
14658
|
+
"server-updated",
|
|
14659
|
+
"export-completed",
|
|
14625
14660
|
"camera-online",
|
|
14626
14661
|
"camera-offline",
|
|
14627
14662
|
"camera-disabled",
|
|
@@ -16515,13 +16550,19 @@ var RetrainStatusSchema = _enum([
|
|
|
16515
16550
|
* "never marked" from "already trained" must read `retrainStatus`.
|
|
16516
16551
|
*
|
|
16517
16552
|
* `debug` does NOT pin; it is attention, not durability.
|
|
16553
|
+
*
|
|
16554
|
+
* `favourited` IS a BOOLEAN pin (durability bit), not a retrain lifecycle.
|
|
16555
|
+
* A favourited track is skipped by retention the same way `staging` is, but
|
|
16556
|
+
* it does not enter `none|staging|trained` and has no staging budget.
|
|
16518
16557
|
*/
|
|
16519
16558
|
var TrackFlagFields = {
|
|
16520
16559
|
/** Operator marked this track as training material — i.e. `retrainStatus` is
|
|
16521
16560
|
* `'staging'`. */
|
|
16522
16561
|
markForTrain: boolean().optional(),
|
|
16523
16562
|
/** Operator marked this track for diagnostic attention. */
|
|
16524
|
-
debug: boolean().optional()
|
|
16563
|
+
debug: boolean().optional(),
|
|
16564
|
+
/** Operator favourited this track. Pins it against pruning. */
|
|
16565
|
+
favourited: boolean().optional()
|
|
16525
16566
|
};
|
|
16526
16567
|
/**
|
|
16527
16568
|
* The lifecycle field itself, on the READ surfaces only (`Track`, `KeyEvent`).
|
|
@@ -16546,6 +16587,7 @@ var TrackFlagsSchema = object({
|
|
|
16546
16587
|
trackId: string(),
|
|
16547
16588
|
markForTrain: boolean(),
|
|
16548
16589
|
debug: boolean(),
|
|
16590
|
+
favourited: boolean(),
|
|
16549
16591
|
/** The lifecycle state the boolean was derived from. Required here (unlike on
|
|
16550
16592
|
* a track row) because this shape is only ever produced by the write body,
|
|
16551
16593
|
* which always knows it — and a surface that has just written needs to render
|
|
@@ -17178,6 +17220,12 @@ var TrackCascadeCountsSchema = object({
|
|
|
17178
17220
|
/** Per-track CLIP search vectors removed (best-effort). */
|
|
17179
17221
|
embeddings: number().int()
|
|
17180
17222
|
});
|
|
17223
|
+
/** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
|
|
17224
|
+
var DiskReconcileCountsSchema = object({
|
|
17225
|
+
mediaDropped: number().int(),
|
|
17226
|
+
tracks: number().int(),
|
|
17227
|
+
events: number().int()
|
|
17228
|
+
});
|
|
17181
17229
|
/** Event-store footprint for one camera. */
|
|
17182
17230
|
var EventStoreDeviceFootprintSchema = object({
|
|
17183
17231
|
deviceId: number(),
|
|
@@ -17354,6 +17402,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
17354
17402
|
}), method(object({ deviceId: number() }), TrackCascadeCountsSchema, {
|
|
17355
17403
|
kind: "mutation",
|
|
17356
17404
|
auth: "admin"
|
|
17405
|
+
}), method(object({ deviceId: number() }), DiskReconcileCountsSchema, {
|
|
17406
|
+
kind: "mutation",
|
|
17407
|
+
auth: "admin"
|
|
17357
17408
|
}), method(object({
|
|
17358
17409
|
deviceId: number(),
|
|
17359
17410
|
trackIds: array(string()).min(1)
|
|
@@ -18904,6 +18955,24 @@ var CameraStatusDegradationSchema = object({
|
|
|
18904
18955
|
*
|
|
18905
18956
|
* See spec: `docs/superpowers/specs/2026-06-24-camera-status-aggregator-cap.md`
|
|
18906
18957
|
*/
|
|
18958
|
+
var DiskReconcileJobSchema = object({
|
|
18959
|
+
state: _enum([
|
|
18960
|
+
"idle",
|
|
18961
|
+
"running",
|
|
18962
|
+
"done",
|
|
18963
|
+
"error"
|
|
18964
|
+
]),
|
|
18965
|
+
total: number().int().nonnegative(),
|
|
18966
|
+
completed: number().int().nonnegative(),
|
|
18967
|
+
currentDeviceId: number().int().nullable(),
|
|
18968
|
+
failed: array(number().int()).readonly(),
|
|
18969
|
+
mediaDropped: number().int().nonnegative(),
|
|
18970
|
+
tracks: number().int().nonnegative(),
|
|
18971
|
+
events: number().int().nonnegative(),
|
|
18972
|
+
startedAtMs: number().int().nullable(),
|
|
18973
|
+
finishedAtMs: number().int().nullable(),
|
|
18974
|
+
error: string().nullable()
|
|
18975
|
+
});
|
|
18907
18976
|
var CameraStatusSchema = object({
|
|
18908
18977
|
deviceId: number(),
|
|
18909
18978
|
assignment: CameraAssignmentStatusSchema,
|
|
@@ -19135,7 +19204,10 @@ method(StorageMigrationLeaseInputSchema, object({ paused: literal(true) }), {
|
|
|
19135
19204
|
}), CameraSwitchGroupSchema, {
|
|
19136
19205
|
kind: "mutation",
|
|
19137
19206
|
auth: "admin"
|
|
19138
|
-
}), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(),
|
|
19207
|
+
}), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), DiskReconcileJobSchema, {
|
|
19208
|
+
kind: "mutation",
|
|
19209
|
+
auth: "admin"
|
|
19210
|
+
}), method(_void(), DiskReconcileJobSchema, { auth: "admin" }), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
|
|
19139
19211
|
name: string(),
|
|
19140
19212
|
description: string().optional(),
|
|
19141
19213
|
config: CameraPipelineConfigSchema
|
|
@@ -20208,20 +20280,6 @@ var VectorStatsResultSchema = object({
|
|
|
20208
20280
|
exact: boolean()
|
|
20209
20281
|
});
|
|
20210
20282
|
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);
|
|
20211
|
-
/**
|
|
20212
|
-
* `videoclips` — the unified, navigable-clip surface for a camera.
|
|
20213
|
-
*
|
|
20214
|
-
* A device-scoped WRAPPER cap (like `pipeline-analytics`): exactly one active
|
|
20215
|
-
* provider per device, substitutable. The DEFAULT provider (registered by
|
|
20216
|
-
* `addon-post-analysis`, `defaultActive: true`) composes the analytics event
|
|
20217
|
-
* log (markers + thumbnails) with the recorder's `getPlaybackManifest` — a clip
|
|
20218
|
-
* is a time-WINDOW over existing footage, never a separate file. A camera that
|
|
20219
|
-
* exposes NATIVE onboard clips (Reolink/Hikvision NVR) can later substitute the
|
|
20220
|
-
* wrapper provider for its device and serve its own clip catalog + URLs.
|
|
20221
|
-
*
|
|
20222
|
-
* A `Clip` is purely time-based (subtree-blind): playback resolves segments by
|
|
20223
|
-
* temporal overlap, so the API never decides `continuous` vs `events`.
|
|
20224
|
-
*/
|
|
20225
20283
|
var ClipSchema = object({
|
|
20226
20284
|
/** Opaque, provider-namespaced id. The default provider encodes the time
|
|
20227
20285
|
* window so `getClipPlayback` is self-contained (no event re-query). */
|
|
@@ -20238,8 +20296,65 @@ var ClipSchema = object({
|
|
|
20238
20296
|
startMs: number(),
|
|
20239
20297
|
endMs: number()
|
|
20240
20298
|
}),
|
|
20241
|
-
/**
|
|
20242
|
-
|
|
20299
|
+
/**
|
|
20300
|
+
* Distinct object classes attached to this visit (`person`, `car`, …),
|
|
20301
|
+
* dominant first, capped at {@link MAX_CLIP_LABELS}. The ribbon renders these
|
|
20302
|
+
* instead of the bare kind — "Object" tells the operator nothing a colour bar
|
|
20303
|
+
* did not. Absent (never empty) when the visit attached no classified object
|
|
20304
|
+
* event, so a motion/audio-only visit keeps its kind label.
|
|
20305
|
+
*/
|
|
20306
|
+
labels: array(string()).max(3).optional(),
|
|
20307
|
+
/**
|
|
20308
|
+
* Lazy thumbnail URL, never inlined.
|
|
20309
|
+
*
|
|
20310
|
+
* Recording-derived clips (events-mode keep-window, and the prepared
|
|
20311
|
+
* continuous event+fragment visit) MUST use the snapshot of the **main
|
|
20312
|
+
* event of the interval** — `getEventMedia({ eventId, kind: 'snapshot' })`
|
|
20313
|
+
* of the event that owns `kind` (object > motion > audio). Do not extract
|
|
20314
|
+
* a keyframe from the recorded segments. Other providers (onboard, HKSV)
|
|
20315
|
+
* mint their own stills.
|
|
20316
|
+
*
|
|
20317
|
+
* **VOUCHED, never fabricated.** A provider emits this only for an event it
|
|
20318
|
+
* has CONFIRMED owns at least one media row (its own, or its owning track's).
|
|
20319
|
+
* Absent is meaningful — "this visit has no event still" — never "we did not
|
|
20320
|
+
* look". Stamping it from a URL template made 35% of one camera's clips point
|
|
20321
|
+
* at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
|
|
20322
|
+
* A read that FAILS drops the claim; it never invents it.
|
|
20323
|
+
*/
|
|
20324
|
+
thumbnail: string().optional(),
|
|
20325
|
+
/**
|
|
20326
|
+
* An instant INSIDE this visit's footage, hole-safe, where a recorded still
|
|
20327
|
+
* can be decoded. Present whenever the visit came from recorded availability;
|
|
20328
|
+
* absent on a per-event padded window (there is no footage to promise).
|
|
20329
|
+
*
|
|
20330
|
+
* This is not a thumbnail and not a second byte path: it is the argument to
|
|
20331
|
+
* the recorder's existing still route. The surface — never the provider —
|
|
20332
|
+
* decides whether to use it. It is the midpoint of the visit's LONGEST
|
|
20333
|
+
* contiguous range, not of the visit: a visit spans its holes by
|
|
20334
|
+
* construction, so a naive midpoint lands in dead air.
|
|
20335
|
+
*/
|
|
20336
|
+
stillAtMs: number().optional(),
|
|
20337
|
+
/**
|
|
20338
|
+
* Analytics event ids that overlap this visit — a BOUNDED sample, newest
|
|
20339
|
+
* first within kind (object → motion → audio), capped at
|
|
20340
|
+
* {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
|
|
20341
|
+
*
|
|
20342
|
+
* Bounded because it is not a payload the surface pages through: one visit on
|
|
20343
|
+
* device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
|
|
20344
|
+
* camera-day. Read {@link eventCount} for the true total.
|
|
20345
|
+
*/
|
|
20346
|
+
eventIds: array(string()).optional(),
|
|
20347
|
+
/** How many analytics events actually overlap this visit. Differs from
|
|
20348
|
+
* `eventIds.length` exactly when the sample was capped — so a truncated
|
|
20349
|
+
* list is never mistaken for a quiet visit. */
|
|
20350
|
+
eventCount: number().int().nonnegative().optional(),
|
|
20351
|
+
/** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
|
|
20352
|
+
* than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
|
|
20353
|
+
* bar keeps showing them via `recording.getAvailability`. */
|
|
20354
|
+
holes: array(object({
|
|
20355
|
+
startMs: number(),
|
|
20356
|
+
endMs: number()
|
|
20357
|
+
})).optional()
|
|
20243
20358
|
});
|
|
20244
20359
|
var ClipPlaybackSchema = object({
|
|
20245
20360
|
/** HLS master URL through the hub data-plane (Range + token in path). */
|
|
@@ -20703,7 +20818,14 @@ var SearchResultSchema = object({
|
|
|
20703
20818
|
});
|
|
20704
20819
|
var AutoUpdateSettingsSchema = object({
|
|
20705
20820
|
channel: ChannelSchema,
|
|
20706
|
-
intervalSeconds: number()
|
|
20821
|
+
intervalSeconds: number(),
|
|
20822
|
+
/**
|
|
20823
|
+
* Cadence of the "an update exists" POLLER, in seconds. Independent of
|
|
20824
|
+
* `channel`: the poller runs while auto-apply is `off`, because being told
|
|
20825
|
+
* about a publish and installing it are different decisions. Clamped
|
|
20826
|
+
* server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
|
|
20827
|
+
*/
|
|
20828
|
+
updateCheckIntervalSeconds: number()
|
|
20707
20829
|
});
|
|
20708
20830
|
var AddonAutoUpdateSchema = ChannelWithInheritSchema;
|
|
20709
20831
|
var RestartAddonResultSchema = unknown();
|
|
@@ -20844,7 +20966,9 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
|
|
|
20844
20966
|
auth: "admin"
|
|
20845
20967
|
}), method(_void(), AutoUpdateSettingsSchema, { auth: "admin" }), method(object({
|
|
20846
20968
|
channel: ChannelSchema,
|
|
20847
|
-
intervalSeconds: number().min(300).max(86400).optional()
|
|
20969
|
+
intervalSeconds: number().min(300).max(86400).optional(),
|
|
20970
|
+
/** Availability-poll cadence; see AutoUpdateSettingsSchema. */
|
|
20971
|
+
updateCheckIntervalSeconds: number().min(900).max(604800).optional()
|
|
20848
20972
|
}), unknown(), {
|
|
20849
20973
|
kind: "mutation",
|
|
20850
20974
|
auth: "admin"
|
|
@@ -26525,7 +26649,9 @@ var ExportOptionsSchema = object({
|
|
|
26525
26649
|
includeAudio: boolean(),
|
|
26526
26650
|
maxLifeMs: number().int().positive(),
|
|
26527
26651
|
deleteAfterDownload: boolean(),
|
|
26528
|
-
title: string().max(200).optional()
|
|
26652
|
+
title: string().max(200).optional(),
|
|
26653
|
+
/** Notification-output target ids to ping when this export becomes ready. */
|
|
26654
|
+
notifyTargetIds: array(string().min(1)).max(20).optional()
|
|
26529
26655
|
}).superRefine((v, ctx) => {
|
|
26530
26656
|
if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
|
|
26531
26657
|
code: ZodIssueCode.custom,
|
|
@@ -26584,10 +26710,18 @@ var ExportBytesSchema = object({
|
|
|
26584
26710
|
});
|
|
26585
26711
|
method(object({
|
|
26586
26712
|
deviceId: number(),
|
|
26587
|
-
|
|
26713
|
+
/** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
|
|
26714
|
+
profile: string().optional(),
|
|
26715
|
+
profiles: array(string()).min(1).optional(),
|
|
26588
26716
|
fromMs: number(),
|
|
26589
26717
|
toMs: number(),
|
|
26590
26718
|
options: ExportOptionsSchema
|
|
26719
|
+
}).superRefine((v, ctx) => {
|
|
26720
|
+
if ((v.profiles !== void 0 && v.profiles.length > 0 ? v.profiles : v.profile !== void 0 ? [v.profile] : []).length < 1) ctx.addIssue({
|
|
26721
|
+
code: ZodIssueCode.custom,
|
|
26722
|
+
message: "pass profiles[] (min 1) or legacy profile",
|
|
26723
|
+
path: ["profiles"]
|
|
26724
|
+
});
|
|
26591
26725
|
}), ExportRecordSchema, {
|
|
26592
26726
|
kind: "mutation",
|
|
26593
26727
|
auth: "protected"
|
|
@@ -29529,15 +29663,6 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
29529
29663
|
labels: ["probe not implemented"]
|
|
29530
29664
|
};
|
|
29531
29665
|
}
|
|
29532
|
-
/**
|
|
29533
|
-
* Top-level devices restored at once in {@link onRestoreDevices}.
|
|
29534
|
-
*
|
|
29535
|
-
* Four covers the fleets this ships to without turning a boot into a burst a
|
|
29536
|
-
* camera NVR answers with a refusal. A provider whose upstream is a single
|
|
29537
|
-
* session with a serial command channel (a Baichuan hub, an NVR that
|
|
29538
|
-
* serialises ISAPI) should lower it; nothing needs to raise it.
|
|
29539
|
-
*/
|
|
29540
|
-
restoreConcurrency = 4;
|
|
29541
29666
|
async restoreDevices(savedDevices) {
|
|
29542
29667
|
await this.onRestoreDevices(savedDevices);
|
|
29543
29668
|
if (savedDevices.length > 0) this.ctx.logger.info(`Restored ${savedDevices.length} ${this.providerName} device(s)`);
|
|
@@ -29592,14 +29717,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
29592
29717
|
});
|
|
29593
29718
|
}
|
|
29594
29719
|
};
|
|
29595
|
-
|
|
29596
|
-
await Promise.all(Array.from({ length: Math.min(Math.max(1, this.restoreConcurrency), topLevel.length) }, async () => {
|
|
29597
|
-
for (;;) {
|
|
29598
|
-
const saved = topLevel[nextTopLevel++];
|
|
29599
|
-
if (saved === void 0) return;
|
|
29600
|
-
await restoreOne(saved);
|
|
29601
|
-
}
|
|
29602
|
-
}));
|
|
29720
|
+
await Promise.all(topLevel.map((saved) => restoreOne(saved)));
|
|
29603
29721
|
const childRows = savedDevices.filter((s) => s.parentDeviceId !== null);
|
|
29604
29722
|
for (const saved of childRows) {
|
|
29605
29723
|
const Class = this.deviceClasses[saved.type];
|
|
@@ -32872,6 +32990,12 @@ Object.freeze({
|
|
|
32872
32990
|
addonId: null,
|
|
32873
32991
|
access: "create"
|
|
32874
32992
|
},
|
|
32993
|
+
"pipelineAnalytics.reconcileFromDisk": {
|
|
32994
|
+
capName: "pipeline-analytics",
|
|
32995
|
+
capScope: "device",
|
|
32996
|
+
addonId: null,
|
|
32997
|
+
access: "create"
|
|
32998
|
+
},
|
|
32875
32999
|
"pipelineAnalytics.refreshStorageLocationsForMigration": {
|
|
32876
33000
|
capName: "pipeline-analytics",
|
|
32877
33001
|
capScope: "device",
|
|
@@ -33274,6 +33398,12 @@ Object.freeze({
|
|
|
33274
33398
|
addonId: null,
|
|
33275
33399
|
access: "view"
|
|
33276
33400
|
},
|
|
33401
|
+
"pipelineOrchestrator.getReconcileFromDiskStatus": {
|
|
33402
|
+
capName: "pipeline-orchestrator",
|
|
33403
|
+
capScope: "system",
|
|
33404
|
+
addonId: null,
|
|
33405
|
+
access: "view"
|
|
33406
|
+
},
|
|
33277
33407
|
"pipelineOrchestrator.listAgentSettings": {
|
|
33278
33408
|
capName: "pipeline-orchestrator",
|
|
33279
33409
|
capScope: "system",
|
|
@@ -33298,6 +33428,12 @@ Object.freeze({
|
|
|
33298
33428
|
addonId: null,
|
|
33299
33429
|
access: "create"
|
|
33300
33430
|
},
|
|
33431
|
+
"pipelineOrchestrator.reconcileFromDisk": {
|
|
33432
|
+
capName: "pipeline-orchestrator",
|
|
33433
|
+
capScope: "system",
|
|
33434
|
+
addonId: null,
|
|
33435
|
+
access: "create"
|
|
33436
|
+
},
|
|
33301
33437
|
"pipelineOrchestrator.removeAgentSettings": {
|
|
33302
33438
|
capName: "pipeline-orchestrator",
|
|
33303
33439
|
capScope: "system",
|
|
@@ -36289,6 +36425,11 @@ Object.freeze({
|
|
|
36289
36425
|
form: "single",
|
|
36290
36426
|
optional: true
|
|
36291
36427
|
}],
|
|
36428
|
+
"pipelineAnalytics.reconcileFromDisk": [{
|
|
36429
|
+
name: "deviceId",
|
|
36430
|
+
form: "single",
|
|
36431
|
+
optional: false
|
|
36432
|
+
}],
|
|
36292
36433
|
"pipelineAnalytics.restageRetrainTrack": [{
|
|
36293
36434
|
name: "deviceId",
|
|
36294
36435
|
form: "single",
|
|
@@ -37406,6 +37547,8 @@ function bestLocationMatch(externalName, existing, threshold = .8) {
|
|
|
37406
37547
|
}
|
|
37407
37548
|
return best;
|
|
37408
37549
|
}
|
|
37550
|
+
var MB = 1024 * 1024;
|
|
37551
|
+
1024 * MB, 3072 * MB;
|
|
37409
37552
|
//#endregion
|
|
37410
37553
|
//#region src/callback-host.ts
|
|
37411
37554
|
/**
|
package/dist/addon.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import { join } from "path";
|
|
|
9
9
|
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
10
10
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
11
11
|
//#endregion
|
|
12
|
-
//#region ../types/dist/event-category-
|
|
12
|
+
//#region ../types/dist/event-category-XfKNtfCc.mjs
|
|
13
13
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
14
14
|
EventCategory["SystemBoot"] = "system.boot";
|
|
15
15
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -26,9 +26,10 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
26
26
|
EventCategory["SystemRestartCompleted"] = "system.restart-completed";
|
|
27
27
|
/**
|
|
28
28
|
* A newer addon or server-root package version was found by the
|
|
29
|
-
* authoritative registry check. Emitted once
|
|
30
|
-
* `(
|
|
31
|
-
*
|
|
29
|
+
* authoritative registry check. Emitted once when any observed
|
|
30
|
+
* `latestVersion` changes (or a package/node first appears behind);
|
|
31
|
+
* the payload carries the full currently-available list. Repeated
|
|
32
|
+
* polling of the same latests is silent.
|
|
32
33
|
*/
|
|
33
34
|
EventCategory["UpdateAvailable"] = "update.available";
|
|
34
35
|
/**
|
|
@@ -47,6 +48,22 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
47
48
|
EventCategory["AddonInstalled"] = "addon.installed";
|
|
48
49
|
EventCategory["AddonUninstalled"] = "addon.uninstalled";
|
|
49
50
|
EventCategory["AddonCrashed"] = "addon.crashed";
|
|
51
|
+
/**
|
|
52
|
+
* A RUNNER the D6 crash circuit-breaker gave up on — terminal.
|
|
53
|
+
*
|
|
54
|
+
* `AddonCrashed` is the routine, self-healing fact ("it crashed; it is being
|
|
55
|
+
* respawned"); this is the one that never resolves itself. Emitted exactly
|
|
56
|
+
* once per trip, by `process-service.ts`, carrying the node, the runner, the
|
|
57
|
+
* addons it hosted and the crash count that tripped the breaker.
|
|
58
|
+
*
|
|
59
|
+
* It exists because a runner marked terminally `failed` used to be SILENT:
|
|
60
|
+
* on 2026-08-18 the `recorder` runner died of three unhandled ffmpeg spawn
|
|
61
|
+
* errors, the breaker stopped respawning it (correctly), `/health` kept
|
|
62
|
+
* returning 200, and fleet-wide recording was gone for 3h15 before the
|
|
63
|
+
* operator's phone told him. The Notification Center's system-event intake
|
|
64
|
+
* consumes this category and maps it to the `addon-crash-loop` kind.
|
|
65
|
+
*/
|
|
66
|
+
EventCategory["AddonRunnerFailed"] = "addon.runner-failed";
|
|
50
67
|
EventCategory["AddonError"] = "addon.error";
|
|
51
68
|
EventCategory["AddonPageReady"] = "addon.page-ready";
|
|
52
69
|
EventCategory["AddonWidgetReady"] = "addon.widget-ready";
|
|
@@ -7558,6 +7575,10 @@ var RecordingBandSchema = object({
|
|
|
7558
7575
|
preBufferSec: number().min(0).optional(),
|
|
7559
7576
|
postBufferSec: number().min(0).optional()
|
|
7560
7577
|
});
|
|
7578
|
+
({
|
|
7579
|
+
preBufferSec: 10,
|
|
7580
|
+
postBufferSec: 30
|
|
7581
|
+
}).postBufferSec * 1e3;
|
|
7561
7582
|
/**
|
|
7562
7583
|
* Per-device retention overrides. Every field is optional; an unset or `0`
|
|
7563
7584
|
* value inherits the node-wide recorder default. Only footage-lifetime limits
|
|
@@ -7605,6 +7626,12 @@ var RecordingConfigSchema = object({
|
|
|
7605
7626
|
/** DERIVED summary of `bands`, stamped by the recorder on every save.
|
|
7606
7627
|
* Authoring it has no effect — see {@link RecordingStorageModeSchema}. */
|
|
7607
7628
|
mode: RecordingStorageModeSchema.optional(),
|
|
7629
|
+
/**
|
|
7630
|
+
* Which assigned broker slots to record. Absent / empty = {@link
|
|
7631
|
+
* DEFAULT_RECORDING_PROFILES} (`high`+`low`) intersected with the
|
|
7632
|
+
* camera's currently assigned slots — never `mid` unless the operator
|
|
7633
|
+
* picks it, and never a slot the broker has not assigned.
|
|
7634
|
+
*/
|
|
7608
7635
|
profiles: array(CamProfileSchema).optional(),
|
|
7609
7636
|
segmentSeconds: number().int().positive().optional(),
|
|
7610
7637
|
/**
|
|
@@ -7685,7 +7712,11 @@ var RelocateFootageInputSchema = object({
|
|
|
7685
7712
|
profiles: array(string()).optional(),
|
|
7686
7713
|
/** Copy throttle in MB/s (default 40) — the drain is a background chore,
|
|
7687
7714
|
* never allowed to starve live writers. */
|
|
7688
|
-
throttleMbps: number().min(1).max(1e3).optional()
|
|
7715
|
+
throttleMbps: number().min(1).max(1e3).optional(),
|
|
7716
|
+
/** Move only segments whose startMs is >= this. Absent = the whole source
|
|
7717
|
+
* pile. Used when a full drain is too expensive and the operator only
|
|
7718
|
+
* wants the recent window on the new disk. */
|
|
7719
|
+
sinceMs: number().int().optional()
|
|
7689
7720
|
});
|
|
7690
7721
|
/** Internal, lease-scoped participant operation. It is intentionally separate
|
|
7691
7722
|
* from persistent recording settings: a migration never changes
|
|
@@ -14616,6 +14647,7 @@ var NcSystemEventKindSchema = _enum([
|
|
|
14616
14647
|
"node-offline",
|
|
14617
14648
|
"node-inference-unavailable",
|
|
14618
14649
|
"detection-blind",
|
|
14650
|
+
"addon-crash-loop",
|
|
14619
14651
|
"addon-update-available",
|
|
14620
14652
|
"server-update-available",
|
|
14621
14653
|
"alarm-triggered",
|
|
@@ -14623,6 +14655,9 @@ var NcSystemEventKindSchema = _enum([
|
|
|
14623
14655
|
"alarm-disarmed",
|
|
14624
14656
|
"alarm-arming",
|
|
14625
14657
|
"alarm-arm-refused",
|
|
14658
|
+
"addon-updated",
|
|
14659
|
+
"server-updated",
|
|
14660
|
+
"export-completed",
|
|
14626
14661
|
"camera-online",
|
|
14627
14662
|
"camera-offline",
|
|
14628
14663
|
"camera-disabled",
|
|
@@ -16516,13 +16551,19 @@ var RetrainStatusSchema = _enum([
|
|
|
16516
16551
|
* "never marked" from "already trained" must read `retrainStatus`.
|
|
16517
16552
|
*
|
|
16518
16553
|
* `debug` does NOT pin; it is attention, not durability.
|
|
16554
|
+
*
|
|
16555
|
+
* `favourited` IS a BOOLEAN pin (durability bit), not a retrain lifecycle.
|
|
16556
|
+
* A favourited track is skipped by retention the same way `staging` is, but
|
|
16557
|
+
* it does not enter `none|staging|trained` and has no staging budget.
|
|
16519
16558
|
*/
|
|
16520
16559
|
var TrackFlagFields = {
|
|
16521
16560
|
/** Operator marked this track as training material — i.e. `retrainStatus` is
|
|
16522
16561
|
* `'staging'`. */
|
|
16523
16562
|
markForTrain: boolean().optional(),
|
|
16524
16563
|
/** Operator marked this track for diagnostic attention. */
|
|
16525
|
-
debug: boolean().optional()
|
|
16564
|
+
debug: boolean().optional(),
|
|
16565
|
+
/** Operator favourited this track. Pins it against pruning. */
|
|
16566
|
+
favourited: boolean().optional()
|
|
16526
16567
|
};
|
|
16527
16568
|
/**
|
|
16528
16569
|
* The lifecycle field itself, on the READ surfaces only (`Track`, `KeyEvent`).
|
|
@@ -16547,6 +16588,7 @@ var TrackFlagsSchema = object({
|
|
|
16547
16588
|
trackId: string(),
|
|
16548
16589
|
markForTrain: boolean(),
|
|
16549
16590
|
debug: boolean(),
|
|
16591
|
+
favourited: boolean(),
|
|
16550
16592
|
/** The lifecycle state the boolean was derived from. Required here (unlike on
|
|
16551
16593
|
* a track row) because this shape is only ever produced by the write body,
|
|
16552
16594
|
* which always knows it — and a surface that has just written needs to render
|
|
@@ -17179,6 +17221,12 @@ var TrackCascadeCountsSchema = object({
|
|
|
17179
17221
|
/** Per-track CLIP search vectors removed (best-effort). */
|
|
17180
17222
|
embeddings: number().int()
|
|
17181
17223
|
});
|
|
17224
|
+
/** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
|
|
17225
|
+
var DiskReconcileCountsSchema = object({
|
|
17226
|
+
mediaDropped: number().int(),
|
|
17227
|
+
tracks: number().int(),
|
|
17228
|
+
events: number().int()
|
|
17229
|
+
});
|
|
17182
17230
|
/** Event-store footprint for one camera. */
|
|
17183
17231
|
var EventStoreDeviceFootprintSchema = object({
|
|
17184
17232
|
deviceId: number(),
|
|
@@ -17355,6 +17403,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
17355
17403
|
}), method(object({ deviceId: number() }), TrackCascadeCountsSchema, {
|
|
17356
17404
|
kind: "mutation",
|
|
17357
17405
|
auth: "admin"
|
|
17406
|
+
}), method(object({ deviceId: number() }), DiskReconcileCountsSchema, {
|
|
17407
|
+
kind: "mutation",
|
|
17408
|
+
auth: "admin"
|
|
17358
17409
|
}), method(object({
|
|
17359
17410
|
deviceId: number(),
|
|
17360
17411
|
trackIds: array(string()).min(1)
|
|
@@ -18905,6 +18956,24 @@ var CameraStatusDegradationSchema = object({
|
|
|
18905
18956
|
*
|
|
18906
18957
|
* See spec: `docs/superpowers/specs/2026-06-24-camera-status-aggregator-cap.md`
|
|
18907
18958
|
*/
|
|
18959
|
+
var DiskReconcileJobSchema = object({
|
|
18960
|
+
state: _enum([
|
|
18961
|
+
"idle",
|
|
18962
|
+
"running",
|
|
18963
|
+
"done",
|
|
18964
|
+
"error"
|
|
18965
|
+
]),
|
|
18966
|
+
total: number().int().nonnegative(),
|
|
18967
|
+
completed: number().int().nonnegative(),
|
|
18968
|
+
currentDeviceId: number().int().nullable(),
|
|
18969
|
+
failed: array(number().int()).readonly(),
|
|
18970
|
+
mediaDropped: number().int().nonnegative(),
|
|
18971
|
+
tracks: number().int().nonnegative(),
|
|
18972
|
+
events: number().int().nonnegative(),
|
|
18973
|
+
startedAtMs: number().int().nullable(),
|
|
18974
|
+
finishedAtMs: number().int().nullable(),
|
|
18975
|
+
error: string().nullable()
|
|
18976
|
+
});
|
|
18908
18977
|
var CameraStatusSchema = object({
|
|
18909
18978
|
deviceId: number(),
|
|
18910
18979
|
assignment: CameraAssignmentStatusSchema,
|
|
@@ -19136,7 +19205,10 @@ method(StorageMigrationLeaseInputSchema, object({ paused: literal(true) }), {
|
|
|
19136
19205
|
}), CameraSwitchGroupSchema, {
|
|
19137
19206
|
kind: "mutation",
|
|
19138
19207
|
auth: "admin"
|
|
19139
|
-
}), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(),
|
|
19208
|
+
}), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), DiskReconcileJobSchema, {
|
|
19209
|
+
kind: "mutation",
|
|
19210
|
+
auth: "admin"
|
|
19211
|
+
}), method(_void(), DiskReconcileJobSchema, { auth: "admin" }), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
|
|
19140
19212
|
name: string(),
|
|
19141
19213
|
description: string().optional(),
|
|
19142
19214
|
config: CameraPipelineConfigSchema
|
|
@@ -20209,20 +20281,6 @@ var VectorStatsResultSchema = object({
|
|
|
20209
20281
|
exact: boolean()
|
|
20210
20282
|
});
|
|
20211
20283
|
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);
|
|
20212
|
-
/**
|
|
20213
|
-
* `videoclips` — the unified, navigable-clip surface for a camera.
|
|
20214
|
-
*
|
|
20215
|
-
* A device-scoped WRAPPER cap (like `pipeline-analytics`): exactly one active
|
|
20216
|
-
* provider per device, substitutable. The DEFAULT provider (registered by
|
|
20217
|
-
* `addon-post-analysis`, `defaultActive: true`) composes the analytics event
|
|
20218
|
-
* log (markers + thumbnails) with the recorder's `getPlaybackManifest` — a clip
|
|
20219
|
-
* is a time-WINDOW over existing footage, never a separate file. A camera that
|
|
20220
|
-
* exposes NATIVE onboard clips (Reolink/Hikvision NVR) can later substitute the
|
|
20221
|
-
* wrapper provider for its device and serve its own clip catalog + URLs.
|
|
20222
|
-
*
|
|
20223
|
-
* A `Clip` is purely time-based (subtree-blind): playback resolves segments by
|
|
20224
|
-
* temporal overlap, so the API never decides `continuous` vs `events`.
|
|
20225
|
-
*/
|
|
20226
20284
|
var ClipSchema = object({
|
|
20227
20285
|
/** Opaque, provider-namespaced id. The default provider encodes the time
|
|
20228
20286
|
* window so `getClipPlayback` is self-contained (no event re-query). */
|
|
@@ -20239,8 +20297,65 @@ var ClipSchema = object({
|
|
|
20239
20297
|
startMs: number(),
|
|
20240
20298
|
endMs: number()
|
|
20241
20299
|
}),
|
|
20242
|
-
/**
|
|
20243
|
-
|
|
20300
|
+
/**
|
|
20301
|
+
* Distinct object classes attached to this visit (`person`, `car`, …),
|
|
20302
|
+
* dominant first, capped at {@link MAX_CLIP_LABELS}. The ribbon renders these
|
|
20303
|
+
* instead of the bare kind — "Object" tells the operator nothing a colour bar
|
|
20304
|
+
* did not. Absent (never empty) when the visit attached no classified object
|
|
20305
|
+
* event, so a motion/audio-only visit keeps its kind label.
|
|
20306
|
+
*/
|
|
20307
|
+
labels: array(string()).max(3).optional(),
|
|
20308
|
+
/**
|
|
20309
|
+
* Lazy thumbnail URL, never inlined.
|
|
20310
|
+
*
|
|
20311
|
+
* Recording-derived clips (events-mode keep-window, and the prepared
|
|
20312
|
+
* continuous event+fragment visit) MUST use the snapshot of the **main
|
|
20313
|
+
* event of the interval** — `getEventMedia({ eventId, kind: 'snapshot' })`
|
|
20314
|
+
* of the event that owns `kind` (object > motion > audio). Do not extract
|
|
20315
|
+
* a keyframe from the recorded segments. Other providers (onboard, HKSV)
|
|
20316
|
+
* mint their own stills.
|
|
20317
|
+
*
|
|
20318
|
+
* **VOUCHED, never fabricated.** A provider emits this only for an event it
|
|
20319
|
+
* has CONFIRMED owns at least one media row (its own, or its owning track's).
|
|
20320
|
+
* Absent is meaningful — "this visit has no event still" — never "we did not
|
|
20321
|
+
* look". Stamping it from a URL template made 35% of one camera's clips point
|
|
20322
|
+
* at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
|
|
20323
|
+
* A read that FAILS drops the claim; it never invents it.
|
|
20324
|
+
*/
|
|
20325
|
+
thumbnail: string().optional(),
|
|
20326
|
+
/**
|
|
20327
|
+
* An instant INSIDE this visit's footage, hole-safe, where a recorded still
|
|
20328
|
+
* can be decoded. Present whenever the visit came from recorded availability;
|
|
20329
|
+
* absent on a per-event padded window (there is no footage to promise).
|
|
20330
|
+
*
|
|
20331
|
+
* This is not a thumbnail and not a second byte path: it is the argument to
|
|
20332
|
+
* the recorder's existing still route. The surface — never the provider —
|
|
20333
|
+
* decides whether to use it. It is the midpoint of the visit's LONGEST
|
|
20334
|
+
* contiguous range, not of the visit: a visit spans its holes by
|
|
20335
|
+
* construction, so a naive midpoint lands in dead air.
|
|
20336
|
+
*/
|
|
20337
|
+
stillAtMs: number().optional(),
|
|
20338
|
+
/**
|
|
20339
|
+
* Analytics event ids that overlap this visit — a BOUNDED sample, newest
|
|
20340
|
+
* first within kind (object → motion → audio), capped at
|
|
20341
|
+
* {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
|
|
20342
|
+
*
|
|
20343
|
+
* Bounded because it is not a payload the surface pages through: one visit on
|
|
20344
|
+
* device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
|
|
20345
|
+
* camera-day. Read {@link eventCount} for the true total.
|
|
20346
|
+
*/
|
|
20347
|
+
eventIds: array(string()).optional(),
|
|
20348
|
+
/** How many analytics events actually overlap this visit. Differs from
|
|
20349
|
+
* `eventIds.length` exactly when the sample was capped — so a truncated
|
|
20350
|
+
* list is never mistaken for a quiet visit. */
|
|
20351
|
+
eventCount: number().int().nonnegative().optional(),
|
|
20352
|
+
/** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
|
|
20353
|
+
* than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
|
|
20354
|
+
* bar keeps showing them via `recording.getAvailability`. */
|
|
20355
|
+
holes: array(object({
|
|
20356
|
+
startMs: number(),
|
|
20357
|
+
endMs: number()
|
|
20358
|
+
})).optional()
|
|
20244
20359
|
});
|
|
20245
20360
|
var ClipPlaybackSchema = object({
|
|
20246
20361
|
/** HLS master URL through the hub data-plane (Range + token in path). */
|
|
@@ -20704,7 +20819,14 @@ var SearchResultSchema = object({
|
|
|
20704
20819
|
});
|
|
20705
20820
|
var AutoUpdateSettingsSchema = object({
|
|
20706
20821
|
channel: ChannelSchema,
|
|
20707
|
-
intervalSeconds: number()
|
|
20822
|
+
intervalSeconds: number(),
|
|
20823
|
+
/**
|
|
20824
|
+
* Cadence of the "an update exists" POLLER, in seconds. Independent of
|
|
20825
|
+
* `channel`: the poller runs while auto-apply is `off`, because being told
|
|
20826
|
+
* about a publish and installing it are different decisions. Clamped
|
|
20827
|
+
* server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
|
|
20828
|
+
*/
|
|
20829
|
+
updateCheckIntervalSeconds: number()
|
|
20708
20830
|
});
|
|
20709
20831
|
var AddonAutoUpdateSchema = ChannelWithInheritSchema;
|
|
20710
20832
|
var RestartAddonResultSchema = unknown();
|
|
@@ -20845,7 +20967,9 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
|
|
|
20845
20967
|
auth: "admin"
|
|
20846
20968
|
}), method(_void(), AutoUpdateSettingsSchema, { auth: "admin" }), method(object({
|
|
20847
20969
|
channel: ChannelSchema,
|
|
20848
|
-
intervalSeconds: number().min(300).max(86400).optional()
|
|
20970
|
+
intervalSeconds: number().min(300).max(86400).optional(),
|
|
20971
|
+
/** Availability-poll cadence; see AutoUpdateSettingsSchema. */
|
|
20972
|
+
updateCheckIntervalSeconds: number().min(900).max(604800).optional()
|
|
20849
20973
|
}), unknown(), {
|
|
20850
20974
|
kind: "mutation",
|
|
20851
20975
|
auth: "admin"
|
|
@@ -26526,7 +26650,9 @@ var ExportOptionsSchema = object({
|
|
|
26526
26650
|
includeAudio: boolean(),
|
|
26527
26651
|
maxLifeMs: number().int().positive(),
|
|
26528
26652
|
deleteAfterDownload: boolean(),
|
|
26529
|
-
title: string().max(200).optional()
|
|
26653
|
+
title: string().max(200).optional(),
|
|
26654
|
+
/** Notification-output target ids to ping when this export becomes ready. */
|
|
26655
|
+
notifyTargetIds: array(string().min(1)).max(20).optional()
|
|
26530
26656
|
}).superRefine((v, ctx) => {
|
|
26531
26657
|
if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
|
|
26532
26658
|
code: ZodIssueCode.custom,
|
|
@@ -26585,10 +26711,18 @@ var ExportBytesSchema = object({
|
|
|
26585
26711
|
});
|
|
26586
26712
|
method(object({
|
|
26587
26713
|
deviceId: number(),
|
|
26588
|
-
|
|
26714
|
+
/** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
|
|
26715
|
+
profile: string().optional(),
|
|
26716
|
+
profiles: array(string()).min(1).optional(),
|
|
26589
26717
|
fromMs: number(),
|
|
26590
26718
|
toMs: number(),
|
|
26591
26719
|
options: ExportOptionsSchema
|
|
26720
|
+
}).superRefine((v, ctx) => {
|
|
26721
|
+
if ((v.profiles !== void 0 && v.profiles.length > 0 ? v.profiles : v.profile !== void 0 ? [v.profile] : []).length < 1) ctx.addIssue({
|
|
26722
|
+
code: ZodIssueCode.custom,
|
|
26723
|
+
message: "pass profiles[] (min 1) or legacy profile",
|
|
26724
|
+
path: ["profiles"]
|
|
26725
|
+
});
|
|
26592
26726
|
}), ExportRecordSchema, {
|
|
26593
26727
|
kind: "mutation",
|
|
26594
26728
|
auth: "protected"
|
|
@@ -29530,15 +29664,6 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
29530
29664
|
labels: ["probe not implemented"]
|
|
29531
29665
|
};
|
|
29532
29666
|
}
|
|
29533
|
-
/**
|
|
29534
|
-
* Top-level devices restored at once in {@link onRestoreDevices}.
|
|
29535
|
-
*
|
|
29536
|
-
* Four covers the fleets this ships to without turning a boot into a burst a
|
|
29537
|
-
* camera NVR answers with a refusal. A provider whose upstream is a single
|
|
29538
|
-
* session with a serial command channel (a Baichuan hub, an NVR that
|
|
29539
|
-
* serialises ISAPI) should lower it; nothing needs to raise it.
|
|
29540
|
-
*/
|
|
29541
|
-
restoreConcurrency = 4;
|
|
29542
29667
|
async restoreDevices(savedDevices) {
|
|
29543
29668
|
await this.onRestoreDevices(savedDevices);
|
|
29544
29669
|
if (savedDevices.length > 0) this.ctx.logger.info(`Restored ${savedDevices.length} ${this.providerName} device(s)`);
|
|
@@ -29593,14 +29718,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
29593
29718
|
});
|
|
29594
29719
|
}
|
|
29595
29720
|
};
|
|
29596
|
-
|
|
29597
|
-
await Promise.all(Array.from({ length: Math.min(Math.max(1, this.restoreConcurrency), topLevel.length) }, async () => {
|
|
29598
|
-
for (;;) {
|
|
29599
|
-
const saved = topLevel[nextTopLevel++];
|
|
29600
|
-
if (saved === void 0) return;
|
|
29601
|
-
await restoreOne(saved);
|
|
29602
|
-
}
|
|
29603
|
-
}));
|
|
29721
|
+
await Promise.all(topLevel.map((saved) => restoreOne(saved)));
|
|
29604
29722
|
const childRows = savedDevices.filter((s) => s.parentDeviceId !== null);
|
|
29605
29723
|
for (const saved of childRows) {
|
|
29606
29724
|
const Class = this.deviceClasses[saved.type];
|
|
@@ -32873,6 +32991,12 @@ Object.freeze({
|
|
|
32873
32991
|
addonId: null,
|
|
32874
32992
|
access: "create"
|
|
32875
32993
|
},
|
|
32994
|
+
"pipelineAnalytics.reconcileFromDisk": {
|
|
32995
|
+
capName: "pipeline-analytics",
|
|
32996
|
+
capScope: "device",
|
|
32997
|
+
addonId: null,
|
|
32998
|
+
access: "create"
|
|
32999
|
+
},
|
|
32876
33000
|
"pipelineAnalytics.refreshStorageLocationsForMigration": {
|
|
32877
33001
|
capName: "pipeline-analytics",
|
|
32878
33002
|
capScope: "device",
|
|
@@ -33275,6 +33399,12 @@ Object.freeze({
|
|
|
33275
33399
|
addonId: null,
|
|
33276
33400
|
access: "view"
|
|
33277
33401
|
},
|
|
33402
|
+
"pipelineOrchestrator.getReconcileFromDiskStatus": {
|
|
33403
|
+
capName: "pipeline-orchestrator",
|
|
33404
|
+
capScope: "system",
|
|
33405
|
+
addonId: null,
|
|
33406
|
+
access: "view"
|
|
33407
|
+
},
|
|
33278
33408
|
"pipelineOrchestrator.listAgentSettings": {
|
|
33279
33409
|
capName: "pipeline-orchestrator",
|
|
33280
33410
|
capScope: "system",
|
|
@@ -33299,6 +33429,12 @@ Object.freeze({
|
|
|
33299
33429
|
addonId: null,
|
|
33300
33430
|
access: "create"
|
|
33301
33431
|
},
|
|
33432
|
+
"pipelineOrchestrator.reconcileFromDisk": {
|
|
33433
|
+
capName: "pipeline-orchestrator",
|
|
33434
|
+
capScope: "system",
|
|
33435
|
+
addonId: null,
|
|
33436
|
+
access: "create"
|
|
33437
|
+
},
|
|
33302
33438
|
"pipelineOrchestrator.removeAgentSettings": {
|
|
33303
33439
|
capName: "pipeline-orchestrator",
|
|
33304
33440
|
capScope: "system",
|
|
@@ -36290,6 +36426,11 @@ Object.freeze({
|
|
|
36290
36426
|
form: "single",
|
|
36291
36427
|
optional: true
|
|
36292
36428
|
}],
|
|
36429
|
+
"pipelineAnalytics.reconcileFromDisk": [{
|
|
36430
|
+
name: "deviceId",
|
|
36431
|
+
form: "single",
|
|
36432
|
+
optional: false
|
|
36433
|
+
}],
|
|
36293
36434
|
"pipelineAnalytics.restageRetrainTrack": [{
|
|
36294
36435
|
name: "deviceId",
|
|
36295
36436
|
form: "single",
|
|
@@ -37407,6 +37548,8 @@ function bestLocationMatch(externalName, existing, threshold = .8) {
|
|
|
37407
37548
|
}
|
|
37408
37549
|
return best;
|
|
37409
37550
|
}
|
|
37551
|
+
var MB = 1024 * 1024;
|
|
37552
|
+
1024 * MB, 3072 * MB;
|
|
37410
37553
|
//#endregion
|
|
37411
37554
|
//#region src/callback-host.ts
|
|
37412
37555
|
/**
|
package/package.json
CHANGED