@camstack/addon-provider-homematic 1.2.20 → 1.2.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon.js +187 -45
- package/dist/addon.mjs +187 -45
- 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
|
|
@@ -19540,7 +19612,14 @@ targets: array(object({
|
|
|
19540
19612
|
"sleeping",
|
|
19541
19613
|
"unreachable",
|
|
19542
19614
|
"waking"
|
|
19543
|
-
]).nullable()
|
|
19615
|
+
]).nullable(),
|
|
19616
|
+
/** A battery camera (whatever its current state). An AWAKE battery
|
|
19617
|
+
* camera is deliberately NOT recaptured on the poll cadence — every
|
|
19618
|
+
* capture is a camera hit that would keep it out of sleep — so its
|
|
19619
|
+
* cached frame legitimately ages past the currency ceiling while
|
|
19620
|
+
* nothing is streaming. A surface must keep painting it (a fresh
|
|
19621
|
+
* frame is captured at each wake), not blank to "unavailable". */
|
|
19622
|
+
battery: boolean()
|
|
19544
19623
|
})));
|
|
19545
19624
|
/**
|
|
19546
19625
|
* `sso-bridge` — internal hub-only cap that lets SSO-style auth
|
|
@@ -20201,20 +20280,6 @@ var VectorStatsResultSchema = object({
|
|
|
20201
20280
|
exact: boolean()
|
|
20202
20281
|
});
|
|
20203
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);
|
|
20204
|
-
/**
|
|
20205
|
-
* `videoclips` — the unified, navigable-clip surface for a camera.
|
|
20206
|
-
*
|
|
20207
|
-
* A device-scoped WRAPPER cap (like `pipeline-analytics`): exactly one active
|
|
20208
|
-
* provider per device, substitutable. The DEFAULT provider (registered by
|
|
20209
|
-
* `addon-post-analysis`, `defaultActive: true`) composes the analytics event
|
|
20210
|
-
* log (markers + thumbnails) with the recorder's `getPlaybackManifest` — a clip
|
|
20211
|
-
* is a time-WINDOW over existing footage, never a separate file. A camera that
|
|
20212
|
-
* exposes NATIVE onboard clips (Reolink/Hikvision NVR) can later substitute the
|
|
20213
|
-
* wrapper provider for its device and serve its own clip catalog + URLs.
|
|
20214
|
-
*
|
|
20215
|
-
* A `Clip` is purely time-based (subtree-blind): playback resolves segments by
|
|
20216
|
-
* temporal overlap, so the API never decides `continuous` vs `events`.
|
|
20217
|
-
*/
|
|
20218
20283
|
var ClipSchema = object({
|
|
20219
20284
|
/** Opaque, provider-namespaced id. The default provider encodes the time
|
|
20220
20285
|
* window so `getClipPlayback` is self-contained (no event re-query). */
|
|
@@ -20231,8 +20296,57 @@ var ClipSchema = object({
|
|
|
20231
20296
|
startMs: number(),
|
|
20232
20297
|
endMs: number()
|
|
20233
20298
|
}),
|
|
20234
|
-
/**
|
|
20235
|
-
thumbnail
|
|
20299
|
+
/**
|
|
20300
|
+
* Lazy thumbnail URL, never inlined.
|
|
20301
|
+
*
|
|
20302
|
+
* Recording-derived clips (events-mode keep-window, and the prepared
|
|
20303
|
+
* continuous event+fragment visit) MUST use the snapshot of the **main
|
|
20304
|
+
* event of the interval** — `getEventMedia({ eventId, kind: 'snapshot' })`
|
|
20305
|
+
* of the event that owns `kind` (object > motion > audio). Do not extract
|
|
20306
|
+
* a keyframe from the recorded segments. Other providers (onboard, HKSV)
|
|
20307
|
+
* mint their own stills.
|
|
20308
|
+
*
|
|
20309
|
+
* **VOUCHED, never fabricated.** A provider emits this only for an event it
|
|
20310
|
+
* has CONFIRMED owns at least one media row (its own, or its owning track's).
|
|
20311
|
+
* Absent is meaningful — "this visit has no event still" — never "we did not
|
|
20312
|
+
* look". Stamping it from a URL template made 35% of one camera's clips point
|
|
20313
|
+
* at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
|
|
20314
|
+
* A read that FAILS drops the claim; it never invents it.
|
|
20315
|
+
*/
|
|
20316
|
+
thumbnail: string().optional(),
|
|
20317
|
+
/**
|
|
20318
|
+
* An instant INSIDE this visit's footage, hole-safe, where a recorded still
|
|
20319
|
+
* can be decoded. Present whenever the visit came from recorded availability;
|
|
20320
|
+
* absent on a per-event padded window (there is no footage to promise).
|
|
20321
|
+
*
|
|
20322
|
+
* This is not a thumbnail and not a second byte path: it is the argument to
|
|
20323
|
+
* the recorder's existing still route. The surface — never the provider —
|
|
20324
|
+
* decides whether to use it. It is the midpoint of the visit's LONGEST
|
|
20325
|
+
* contiguous range, not of the visit: a visit spans its holes by
|
|
20326
|
+
* construction, so a naive midpoint lands in dead air.
|
|
20327
|
+
*/
|
|
20328
|
+
stillAtMs: number().optional(),
|
|
20329
|
+
/**
|
|
20330
|
+
* Analytics event ids that overlap this visit — a BOUNDED sample, newest
|
|
20331
|
+
* first within kind (object → motion → audio), capped at
|
|
20332
|
+
* {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
|
|
20333
|
+
*
|
|
20334
|
+
* Bounded because it is not a payload the surface pages through: one visit on
|
|
20335
|
+
* device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
|
|
20336
|
+
* camera-day. Read {@link eventCount} for the true total.
|
|
20337
|
+
*/
|
|
20338
|
+
eventIds: array(string()).optional(),
|
|
20339
|
+
/** How many analytics events actually overlap this visit. Differs from
|
|
20340
|
+
* `eventIds.length` exactly when the sample was capped — so a truncated
|
|
20341
|
+
* list is never mistaken for a quiet visit. */
|
|
20342
|
+
eventCount: number().int().nonnegative().optional(),
|
|
20343
|
+
/** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
|
|
20344
|
+
* than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
|
|
20345
|
+
* bar keeps showing them via `recording.getAvailability`. */
|
|
20346
|
+
holes: array(object({
|
|
20347
|
+
startMs: number(),
|
|
20348
|
+
endMs: number()
|
|
20349
|
+
})).optional()
|
|
20236
20350
|
});
|
|
20237
20351
|
var ClipPlaybackSchema = object({
|
|
20238
20352
|
/** HLS master URL through the hub data-plane (Range + token in path). */
|
|
@@ -20696,7 +20810,14 @@ var SearchResultSchema = object({
|
|
|
20696
20810
|
});
|
|
20697
20811
|
var AutoUpdateSettingsSchema = object({
|
|
20698
20812
|
channel: ChannelSchema,
|
|
20699
|
-
intervalSeconds: number()
|
|
20813
|
+
intervalSeconds: number(),
|
|
20814
|
+
/**
|
|
20815
|
+
* Cadence of the "an update exists" POLLER, in seconds. Independent of
|
|
20816
|
+
* `channel`: the poller runs while auto-apply is `off`, because being told
|
|
20817
|
+
* about a publish and installing it are different decisions. Clamped
|
|
20818
|
+
* server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
|
|
20819
|
+
*/
|
|
20820
|
+
updateCheckIntervalSeconds: number()
|
|
20700
20821
|
});
|
|
20701
20822
|
var AddonAutoUpdateSchema = ChannelWithInheritSchema;
|
|
20702
20823
|
var RestartAddonResultSchema = unknown();
|
|
@@ -20837,7 +20958,9 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
|
|
|
20837
20958
|
auth: "admin"
|
|
20838
20959
|
}), method(_void(), AutoUpdateSettingsSchema, { auth: "admin" }), method(object({
|
|
20839
20960
|
channel: ChannelSchema,
|
|
20840
|
-
intervalSeconds: number().min(300).max(86400).optional()
|
|
20961
|
+
intervalSeconds: number().min(300).max(86400).optional(),
|
|
20962
|
+
/** Availability-poll cadence; see AutoUpdateSettingsSchema. */
|
|
20963
|
+
updateCheckIntervalSeconds: number().min(900).max(604800).optional()
|
|
20841
20964
|
}), unknown(), {
|
|
20842
20965
|
kind: "mutation",
|
|
20843
20966
|
auth: "admin"
|
|
@@ -26518,7 +26641,9 @@ var ExportOptionsSchema = object({
|
|
|
26518
26641
|
includeAudio: boolean(),
|
|
26519
26642
|
maxLifeMs: number().int().positive(),
|
|
26520
26643
|
deleteAfterDownload: boolean(),
|
|
26521
|
-
title: string().max(200).optional()
|
|
26644
|
+
title: string().max(200).optional(),
|
|
26645
|
+
/** Notification-output target ids to ping when this export becomes ready. */
|
|
26646
|
+
notifyTargetIds: array(string().min(1)).max(20).optional()
|
|
26522
26647
|
}).superRefine((v, ctx) => {
|
|
26523
26648
|
if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
|
|
26524
26649
|
code: ZodIssueCode.custom,
|
|
@@ -26577,10 +26702,18 @@ var ExportBytesSchema = object({
|
|
|
26577
26702
|
});
|
|
26578
26703
|
method(object({
|
|
26579
26704
|
deviceId: number(),
|
|
26580
|
-
|
|
26705
|
+
/** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
|
|
26706
|
+
profile: string().optional(),
|
|
26707
|
+
profiles: array(string()).min(1).optional(),
|
|
26581
26708
|
fromMs: number(),
|
|
26582
26709
|
toMs: number(),
|
|
26583
26710
|
options: ExportOptionsSchema
|
|
26711
|
+
}).superRefine((v, ctx) => {
|
|
26712
|
+
if ((v.profiles !== void 0 && v.profiles.length > 0 ? v.profiles : v.profile !== void 0 ? [v.profile] : []).length < 1) ctx.addIssue({
|
|
26713
|
+
code: ZodIssueCode.custom,
|
|
26714
|
+
message: "pass profiles[] (min 1) or legacy profile",
|
|
26715
|
+
path: ["profiles"]
|
|
26716
|
+
});
|
|
26584
26717
|
}), ExportRecordSchema, {
|
|
26585
26718
|
kind: "mutation",
|
|
26586
26719
|
auth: "protected"
|
|
@@ -29522,15 +29655,6 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
29522
29655
|
labels: ["probe not implemented"]
|
|
29523
29656
|
};
|
|
29524
29657
|
}
|
|
29525
|
-
/**
|
|
29526
|
-
* Top-level devices restored at once in {@link onRestoreDevices}.
|
|
29527
|
-
*
|
|
29528
|
-
* Four covers the fleets this ships to without turning a boot into a burst a
|
|
29529
|
-
* camera NVR answers with a refusal. A provider whose upstream is a single
|
|
29530
|
-
* session with a serial command channel (a Baichuan hub, an NVR that
|
|
29531
|
-
* serialises ISAPI) should lower it; nothing needs to raise it.
|
|
29532
|
-
*/
|
|
29533
|
-
restoreConcurrency = 4;
|
|
29534
29658
|
async restoreDevices(savedDevices) {
|
|
29535
29659
|
await this.onRestoreDevices(savedDevices);
|
|
29536
29660
|
if (savedDevices.length > 0) this.ctx.logger.info(`Restored ${savedDevices.length} ${this.providerName} device(s)`);
|
|
@@ -29585,14 +29709,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
29585
29709
|
});
|
|
29586
29710
|
}
|
|
29587
29711
|
};
|
|
29588
|
-
|
|
29589
|
-
await Promise.all(Array.from({ length: Math.min(Math.max(1, this.restoreConcurrency), topLevel.length) }, async () => {
|
|
29590
|
-
for (;;) {
|
|
29591
|
-
const saved = topLevel[nextTopLevel++];
|
|
29592
|
-
if (saved === void 0) return;
|
|
29593
|
-
await restoreOne(saved);
|
|
29594
|
-
}
|
|
29595
|
-
}));
|
|
29712
|
+
await Promise.all(topLevel.map((saved) => restoreOne(saved)));
|
|
29596
29713
|
const childRows = savedDevices.filter((s) => s.parentDeviceId !== null);
|
|
29597
29714
|
for (const saved of childRows) {
|
|
29598
29715
|
const Class = this.deviceClasses[saved.type];
|
|
@@ -32865,6 +32982,12 @@ Object.freeze({
|
|
|
32865
32982
|
addonId: null,
|
|
32866
32983
|
access: "create"
|
|
32867
32984
|
},
|
|
32985
|
+
"pipelineAnalytics.reconcileFromDisk": {
|
|
32986
|
+
capName: "pipeline-analytics",
|
|
32987
|
+
capScope: "device",
|
|
32988
|
+
addonId: null,
|
|
32989
|
+
access: "create"
|
|
32990
|
+
},
|
|
32868
32991
|
"pipelineAnalytics.refreshStorageLocationsForMigration": {
|
|
32869
32992
|
capName: "pipeline-analytics",
|
|
32870
32993
|
capScope: "device",
|
|
@@ -33267,6 +33390,12 @@ Object.freeze({
|
|
|
33267
33390
|
addonId: null,
|
|
33268
33391
|
access: "view"
|
|
33269
33392
|
},
|
|
33393
|
+
"pipelineOrchestrator.getReconcileFromDiskStatus": {
|
|
33394
|
+
capName: "pipeline-orchestrator",
|
|
33395
|
+
capScope: "system",
|
|
33396
|
+
addonId: null,
|
|
33397
|
+
access: "view"
|
|
33398
|
+
},
|
|
33270
33399
|
"pipelineOrchestrator.listAgentSettings": {
|
|
33271
33400
|
capName: "pipeline-orchestrator",
|
|
33272
33401
|
capScope: "system",
|
|
@@ -33291,6 +33420,12 @@ Object.freeze({
|
|
|
33291
33420
|
addonId: null,
|
|
33292
33421
|
access: "create"
|
|
33293
33422
|
},
|
|
33423
|
+
"pipelineOrchestrator.reconcileFromDisk": {
|
|
33424
|
+
capName: "pipeline-orchestrator",
|
|
33425
|
+
capScope: "system",
|
|
33426
|
+
addonId: null,
|
|
33427
|
+
access: "create"
|
|
33428
|
+
},
|
|
33294
33429
|
"pipelineOrchestrator.removeAgentSettings": {
|
|
33295
33430
|
capName: "pipeline-orchestrator",
|
|
33296
33431
|
capScope: "system",
|
|
@@ -36282,6 +36417,11 @@ Object.freeze({
|
|
|
36282
36417
|
form: "single",
|
|
36283
36418
|
optional: true
|
|
36284
36419
|
}],
|
|
36420
|
+
"pipelineAnalytics.reconcileFromDisk": [{
|
|
36421
|
+
name: "deviceId",
|
|
36422
|
+
form: "single",
|
|
36423
|
+
optional: false
|
|
36424
|
+
}],
|
|
36285
36425
|
"pipelineAnalytics.restageRetrainTrack": [{
|
|
36286
36426
|
name: "deviceId",
|
|
36287
36427
|
form: "single",
|
|
@@ -37399,6 +37539,8 @@ function bestLocationMatch(externalName, existing, threshold = .8) {
|
|
|
37399
37539
|
}
|
|
37400
37540
|
return best;
|
|
37401
37541
|
}
|
|
37542
|
+
var MB = 1024 * 1024;
|
|
37543
|
+
1024 * MB, 3072 * MB;
|
|
37402
37544
|
//#endregion
|
|
37403
37545
|
//#region src/callback-host.ts
|
|
37404
37546
|
/**
|
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
|
|
@@ -19541,7 +19613,14 @@ targets: array(object({
|
|
|
19541
19613
|
"sleeping",
|
|
19542
19614
|
"unreachable",
|
|
19543
19615
|
"waking"
|
|
19544
|
-
]).nullable()
|
|
19616
|
+
]).nullable(),
|
|
19617
|
+
/** A battery camera (whatever its current state). An AWAKE battery
|
|
19618
|
+
* camera is deliberately NOT recaptured on the poll cadence — every
|
|
19619
|
+
* capture is a camera hit that would keep it out of sleep — so its
|
|
19620
|
+
* cached frame legitimately ages past the currency ceiling while
|
|
19621
|
+
* nothing is streaming. A surface must keep painting it (a fresh
|
|
19622
|
+
* frame is captured at each wake), not blank to "unavailable". */
|
|
19623
|
+
battery: boolean()
|
|
19545
19624
|
})));
|
|
19546
19625
|
/**
|
|
19547
19626
|
* `sso-bridge` — internal hub-only cap that lets SSO-style auth
|
|
@@ -20202,20 +20281,6 @@ var VectorStatsResultSchema = object({
|
|
|
20202
20281
|
exact: boolean()
|
|
20203
20282
|
});
|
|
20204
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);
|
|
20205
|
-
/**
|
|
20206
|
-
* `videoclips` — the unified, navigable-clip surface for a camera.
|
|
20207
|
-
*
|
|
20208
|
-
* A device-scoped WRAPPER cap (like `pipeline-analytics`): exactly one active
|
|
20209
|
-
* provider per device, substitutable. The DEFAULT provider (registered by
|
|
20210
|
-
* `addon-post-analysis`, `defaultActive: true`) composes the analytics event
|
|
20211
|
-
* log (markers + thumbnails) with the recorder's `getPlaybackManifest` — a clip
|
|
20212
|
-
* is a time-WINDOW over existing footage, never a separate file. A camera that
|
|
20213
|
-
* exposes NATIVE onboard clips (Reolink/Hikvision NVR) can later substitute the
|
|
20214
|
-
* wrapper provider for its device and serve its own clip catalog + URLs.
|
|
20215
|
-
*
|
|
20216
|
-
* A `Clip` is purely time-based (subtree-blind): playback resolves segments by
|
|
20217
|
-
* temporal overlap, so the API never decides `continuous` vs `events`.
|
|
20218
|
-
*/
|
|
20219
20284
|
var ClipSchema = object({
|
|
20220
20285
|
/** Opaque, provider-namespaced id. The default provider encodes the time
|
|
20221
20286
|
* window so `getClipPlayback` is self-contained (no event re-query). */
|
|
@@ -20232,8 +20297,57 @@ var ClipSchema = object({
|
|
|
20232
20297
|
startMs: number(),
|
|
20233
20298
|
endMs: number()
|
|
20234
20299
|
}),
|
|
20235
|
-
/**
|
|
20236
|
-
thumbnail
|
|
20300
|
+
/**
|
|
20301
|
+
* Lazy thumbnail URL, never inlined.
|
|
20302
|
+
*
|
|
20303
|
+
* Recording-derived clips (events-mode keep-window, and the prepared
|
|
20304
|
+
* continuous event+fragment visit) MUST use the snapshot of the **main
|
|
20305
|
+
* event of the interval** — `getEventMedia({ eventId, kind: 'snapshot' })`
|
|
20306
|
+
* of the event that owns `kind` (object > motion > audio). Do not extract
|
|
20307
|
+
* a keyframe from the recorded segments. Other providers (onboard, HKSV)
|
|
20308
|
+
* mint their own stills.
|
|
20309
|
+
*
|
|
20310
|
+
* **VOUCHED, never fabricated.** A provider emits this only for an event it
|
|
20311
|
+
* has CONFIRMED owns at least one media row (its own, or its owning track's).
|
|
20312
|
+
* Absent is meaningful — "this visit has no event still" — never "we did not
|
|
20313
|
+
* look". Stamping it from a URL template made 35% of one camera's clips point
|
|
20314
|
+
* at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
|
|
20315
|
+
* A read that FAILS drops the claim; it never invents it.
|
|
20316
|
+
*/
|
|
20317
|
+
thumbnail: string().optional(),
|
|
20318
|
+
/**
|
|
20319
|
+
* An instant INSIDE this visit's footage, hole-safe, where a recorded still
|
|
20320
|
+
* can be decoded. Present whenever the visit came from recorded availability;
|
|
20321
|
+
* absent on a per-event padded window (there is no footage to promise).
|
|
20322
|
+
*
|
|
20323
|
+
* This is not a thumbnail and not a second byte path: it is the argument to
|
|
20324
|
+
* the recorder's existing still route. The surface — never the provider —
|
|
20325
|
+
* decides whether to use it. It is the midpoint of the visit's LONGEST
|
|
20326
|
+
* contiguous range, not of the visit: a visit spans its holes by
|
|
20327
|
+
* construction, so a naive midpoint lands in dead air.
|
|
20328
|
+
*/
|
|
20329
|
+
stillAtMs: number().optional(),
|
|
20330
|
+
/**
|
|
20331
|
+
* Analytics event ids that overlap this visit — a BOUNDED sample, newest
|
|
20332
|
+
* first within kind (object → motion → audio), capped at
|
|
20333
|
+
* {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
|
|
20334
|
+
*
|
|
20335
|
+
* Bounded because it is not a payload the surface pages through: one visit on
|
|
20336
|
+
* device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
|
|
20337
|
+
* camera-day. Read {@link eventCount} for the true total.
|
|
20338
|
+
*/
|
|
20339
|
+
eventIds: array(string()).optional(),
|
|
20340
|
+
/** How many analytics events actually overlap this visit. Differs from
|
|
20341
|
+
* `eventIds.length` exactly when the sample was capped — so a truncated
|
|
20342
|
+
* list is never mistaken for a quiet visit. */
|
|
20343
|
+
eventCount: number().int().nonnegative().optional(),
|
|
20344
|
+
/** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
|
|
20345
|
+
* than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
|
|
20346
|
+
* bar keeps showing them via `recording.getAvailability`. */
|
|
20347
|
+
holes: array(object({
|
|
20348
|
+
startMs: number(),
|
|
20349
|
+
endMs: number()
|
|
20350
|
+
})).optional()
|
|
20237
20351
|
});
|
|
20238
20352
|
var ClipPlaybackSchema = object({
|
|
20239
20353
|
/** HLS master URL through the hub data-plane (Range + token in path). */
|
|
@@ -20697,7 +20811,14 @@ var SearchResultSchema = object({
|
|
|
20697
20811
|
});
|
|
20698
20812
|
var AutoUpdateSettingsSchema = object({
|
|
20699
20813
|
channel: ChannelSchema,
|
|
20700
|
-
intervalSeconds: number()
|
|
20814
|
+
intervalSeconds: number(),
|
|
20815
|
+
/**
|
|
20816
|
+
* Cadence of the "an update exists" POLLER, in seconds. Independent of
|
|
20817
|
+
* `channel`: the poller runs while auto-apply is `off`, because being told
|
|
20818
|
+
* about a publish and installing it are different decisions. Clamped
|
|
20819
|
+
* server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
|
|
20820
|
+
*/
|
|
20821
|
+
updateCheckIntervalSeconds: number()
|
|
20701
20822
|
});
|
|
20702
20823
|
var AddonAutoUpdateSchema = ChannelWithInheritSchema;
|
|
20703
20824
|
var RestartAddonResultSchema = unknown();
|
|
@@ -20838,7 +20959,9 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
|
|
|
20838
20959
|
auth: "admin"
|
|
20839
20960
|
}), method(_void(), AutoUpdateSettingsSchema, { auth: "admin" }), method(object({
|
|
20840
20961
|
channel: ChannelSchema,
|
|
20841
|
-
intervalSeconds: number().min(300).max(86400).optional()
|
|
20962
|
+
intervalSeconds: number().min(300).max(86400).optional(),
|
|
20963
|
+
/** Availability-poll cadence; see AutoUpdateSettingsSchema. */
|
|
20964
|
+
updateCheckIntervalSeconds: number().min(900).max(604800).optional()
|
|
20842
20965
|
}), unknown(), {
|
|
20843
20966
|
kind: "mutation",
|
|
20844
20967
|
auth: "admin"
|
|
@@ -26519,7 +26642,9 @@ var ExportOptionsSchema = object({
|
|
|
26519
26642
|
includeAudio: boolean(),
|
|
26520
26643
|
maxLifeMs: number().int().positive(),
|
|
26521
26644
|
deleteAfterDownload: boolean(),
|
|
26522
|
-
title: string().max(200).optional()
|
|
26645
|
+
title: string().max(200).optional(),
|
|
26646
|
+
/** Notification-output target ids to ping when this export becomes ready. */
|
|
26647
|
+
notifyTargetIds: array(string().min(1)).max(20).optional()
|
|
26523
26648
|
}).superRefine((v, ctx) => {
|
|
26524
26649
|
if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
|
|
26525
26650
|
code: ZodIssueCode.custom,
|
|
@@ -26578,10 +26703,18 @@ var ExportBytesSchema = object({
|
|
|
26578
26703
|
});
|
|
26579
26704
|
method(object({
|
|
26580
26705
|
deviceId: number(),
|
|
26581
|
-
|
|
26706
|
+
/** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
|
|
26707
|
+
profile: string().optional(),
|
|
26708
|
+
profiles: array(string()).min(1).optional(),
|
|
26582
26709
|
fromMs: number(),
|
|
26583
26710
|
toMs: number(),
|
|
26584
26711
|
options: ExportOptionsSchema
|
|
26712
|
+
}).superRefine((v, ctx) => {
|
|
26713
|
+
if ((v.profiles !== void 0 && v.profiles.length > 0 ? v.profiles : v.profile !== void 0 ? [v.profile] : []).length < 1) ctx.addIssue({
|
|
26714
|
+
code: ZodIssueCode.custom,
|
|
26715
|
+
message: "pass profiles[] (min 1) or legacy profile",
|
|
26716
|
+
path: ["profiles"]
|
|
26717
|
+
});
|
|
26585
26718
|
}), ExportRecordSchema, {
|
|
26586
26719
|
kind: "mutation",
|
|
26587
26720
|
auth: "protected"
|
|
@@ -29523,15 +29656,6 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
29523
29656
|
labels: ["probe not implemented"]
|
|
29524
29657
|
};
|
|
29525
29658
|
}
|
|
29526
|
-
/**
|
|
29527
|
-
* Top-level devices restored at once in {@link onRestoreDevices}.
|
|
29528
|
-
*
|
|
29529
|
-
* Four covers the fleets this ships to without turning a boot into a burst a
|
|
29530
|
-
* camera NVR answers with a refusal. A provider whose upstream is a single
|
|
29531
|
-
* session with a serial command channel (a Baichuan hub, an NVR that
|
|
29532
|
-
* serialises ISAPI) should lower it; nothing needs to raise it.
|
|
29533
|
-
*/
|
|
29534
|
-
restoreConcurrency = 4;
|
|
29535
29659
|
async restoreDevices(savedDevices) {
|
|
29536
29660
|
await this.onRestoreDevices(savedDevices);
|
|
29537
29661
|
if (savedDevices.length > 0) this.ctx.logger.info(`Restored ${savedDevices.length} ${this.providerName} device(s)`);
|
|
@@ -29586,14 +29710,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
29586
29710
|
});
|
|
29587
29711
|
}
|
|
29588
29712
|
};
|
|
29589
|
-
|
|
29590
|
-
await Promise.all(Array.from({ length: Math.min(Math.max(1, this.restoreConcurrency), topLevel.length) }, async () => {
|
|
29591
|
-
for (;;) {
|
|
29592
|
-
const saved = topLevel[nextTopLevel++];
|
|
29593
|
-
if (saved === void 0) return;
|
|
29594
|
-
await restoreOne(saved);
|
|
29595
|
-
}
|
|
29596
|
-
}));
|
|
29713
|
+
await Promise.all(topLevel.map((saved) => restoreOne(saved)));
|
|
29597
29714
|
const childRows = savedDevices.filter((s) => s.parentDeviceId !== null);
|
|
29598
29715
|
for (const saved of childRows) {
|
|
29599
29716
|
const Class = this.deviceClasses[saved.type];
|
|
@@ -32866,6 +32983,12 @@ Object.freeze({
|
|
|
32866
32983
|
addonId: null,
|
|
32867
32984
|
access: "create"
|
|
32868
32985
|
},
|
|
32986
|
+
"pipelineAnalytics.reconcileFromDisk": {
|
|
32987
|
+
capName: "pipeline-analytics",
|
|
32988
|
+
capScope: "device",
|
|
32989
|
+
addonId: null,
|
|
32990
|
+
access: "create"
|
|
32991
|
+
},
|
|
32869
32992
|
"pipelineAnalytics.refreshStorageLocationsForMigration": {
|
|
32870
32993
|
capName: "pipeline-analytics",
|
|
32871
32994
|
capScope: "device",
|
|
@@ -33268,6 +33391,12 @@ Object.freeze({
|
|
|
33268
33391
|
addonId: null,
|
|
33269
33392
|
access: "view"
|
|
33270
33393
|
},
|
|
33394
|
+
"pipelineOrchestrator.getReconcileFromDiskStatus": {
|
|
33395
|
+
capName: "pipeline-orchestrator",
|
|
33396
|
+
capScope: "system",
|
|
33397
|
+
addonId: null,
|
|
33398
|
+
access: "view"
|
|
33399
|
+
},
|
|
33271
33400
|
"pipelineOrchestrator.listAgentSettings": {
|
|
33272
33401
|
capName: "pipeline-orchestrator",
|
|
33273
33402
|
capScope: "system",
|
|
@@ -33292,6 +33421,12 @@ Object.freeze({
|
|
|
33292
33421
|
addonId: null,
|
|
33293
33422
|
access: "create"
|
|
33294
33423
|
},
|
|
33424
|
+
"pipelineOrchestrator.reconcileFromDisk": {
|
|
33425
|
+
capName: "pipeline-orchestrator",
|
|
33426
|
+
capScope: "system",
|
|
33427
|
+
addonId: null,
|
|
33428
|
+
access: "create"
|
|
33429
|
+
},
|
|
33295
33430
|
"pipelineOrchestrator.removeAgentSettings": {
|
|
33296
33431
|
capName: "pipeline-orchestrator",
|
|
33297
33432
|
capScope: "system",
|
|
@@ -36283,6 +36418,11 @@ Object.freeze({
|
|
|
36283
36418
|
form: "single",
|
|
36284
36419
|
optional: true
|
|
36285
36420
|
}],
|
|
36421
|
+
"pipelineAnalytics.reconcileFromDisk": [{
|
|
36422
|
+
name: "deviceId",
|
|
36423
|
+
form: "single",
|
|
36424
|
+
optional: false
|
|
36425
|
+
}],
|
|
36286
36426
|
"pipelineAnalytics.restageRetrainTrack": [{
|
|
36287
36427
|
name: "deviceId",
|
|
36288
36428
|
form: "single",
|
|
@@ -37400,6 +37540,8 @@ function bestLocationMatch(externalName, existing, threshold = .8) {
|
|
|
37400
37540
|
}
|
|
37401
37541
|
return best;
|
|
37402
37542
|
}
|
|
37543
|
+
var MB = 1024 * 1024;
|
|
37544
|
+
1024 * MB, 3072 * MB;
|
|
37403
37545
|
//#endregion
|
|
37404
37546
|
//#region src/callback-host.ts
|
|
37405
37547
|
/**
|
package/package.json
CHANGED