@camstack/addon-provider-wyze 0.2.23 → 0.2.25

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.
Files changed (3) hide show
  1. package/dist/addon.js +136 -22
  2. package/dist/addon.mjs +136 -22
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -31,7 +31,7 @@ let net = require("net");
31
31
  net = __toESM(net, 1);
32
32
  let node_child_process = require("node:child_process");
33
33
  let node_tls = require("node:tls");
34
- //#region ../types/dist/event-category-C0lyLd5U.mjs
34
+ //#region ../types/dist/event-category-XfKNtfCc.mjs
35
35
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
36
36
  EventCategory["SystemBoot"] = "system.boot";
37
37
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -70,6 +70,22 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
70
70
  EventCategory["AddonInstalled"] = "addon.installed";
71
71
  EventCategory["AddonUninstalled"] = "addon.uninstalled";
72
72
  EventCategory["AddonCrashed"] = "addon.crashed";
73
+ /**
74
+ * A RUNNER the D6 crash circuit-breaker gave up on — terminal.
75
+ *
76
+ * `AddonCrashed` is the routine, self-healing fact ("it crashed; it is being
77
+ * respawned"); this is the one that never resolves itself. Emitted exactly
78
+ * once per trip, by `process-service.ts`, carrying the node, the runner, the
79
+ * addons it hosted and the crash count that tripped the breaker.
80
+ *
81
+ * It exists because a runner marked terminally `failed` used to be SILENT:
82
+ * on 2026-08-18 the `recorder` runner died of three unhandled ffmpeg spawn
83
+ * errors, the breaker stopped respawning it (correctly), `/health` kept
84
+ * returning 200, and fleet-wide recording was gone for 3h15 before the
85
+ * operator's phone told him. The Notification Center's system-event intake
86
+ * consumes this category and maps it to the `addon-crash-loop` kind.
87
+ */
88
+ EventCategory["AddonRunnerFailed"] = "addon.runner-failed";
73
89
  EventCategory["AddonError"] = "addon.error";
74
90
  EventCategory["AddonPageReady"] = "addon.page-ready";
75
91
  EventCategory["AddonWidgetReady"] = "addon.widget-ready";
@@ -7632,6 +7648,12 @@ var RecordingConfigSchema = object({
7632
7648
  /** DERIVED summary of `bands`, stamped by the recorder on every save.
7633
7649
  * Authoring it has no effect — see {@link RecordingStorageModeSchema}. */
7634
7650
  mode: RecordingStorageModeSchema.optional(),
7651
+ /**
7652
+ * Which assigned broker slots to record. Absent / empty = {@link
7653
+ * DEFAULT_RECORDING_PROFILES} (`high`+`low`) intersected with the
7654
+ * camera's currently assigned slots — never `mid` unless the operator
7655
+ * picks it, and never a slot the broker has not assigned.
7656
+ */
7635
7657
  profiles: array(CamProfileSchema).optional(),
7636
7658
  segmentSeconds: number().int().positive().optional(),
7637
7659
  /**
@@ -7712,7 +7734,11 @@ var RelocateFootageInputSchema = object({
7712
7734
  profiles: array(string()).optional(),
7713
7735
  /** Copy throttle in MB/s (default 40) — the drain is a background chore,
7714
7736
  * never allowed to starve live writers. */
7715
- throttleMbps: number().min(1).max(1e3).optional()
7737
+ throttleMbps: number().min(1).max(1e3).optional(),
7738
+ /** Move only segments whose startMs is >= this. Absent = the whole source
7739
+ * pile. Used when a full drain is too expensive and the operator only
7740
+ * wants the recent window on the new disk. */
7741
+ sinceMs: number().int().optional()
7716
7742
  });
7717
7743
  /** Internal, lease-scoped participant operation. It is intentionally separate
7718
7744
  * from persistent recording settings: a migration never changes
@@ -14594,6 +14620,7 @@ var NcSystemEventKindSchema = _enum([
14594
14620
  "node-offline",
14595
14621
  "node-inference-unavailable",
14596
14622
  "detection-blind",
14623
+ "addon-crash-loop",
14597
14624
  "addon-update-available",
14598
14625
  "server-update-available",
14599
14626
  "alarm-triggered",
@@ -17167,6 +17194,12 @@ var TrackCascadeCountsSchema = object({
17167
17194
  /** Per-track CLIP search vectors removed (best-effort). */
17168
17195
  embeddings: number().int()
17169
17196
  });
17197
+ /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
17198
+ var DiskReconcileCountsSchema = object({
17199
+ mediaDropped: number().int(),
17200
+ tracks: number().int(),
17201
+ events: number().int()
17202
+ });
17170
17203
  /** Event-store footprint for one camera. */
17171
17204
  var EventStoreDeviceFootprintSchema = object({
17172
17205
  deviceId: number(),
@@ -17343,6 +17376,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17343
17376
  }), method(object({ deviceId: number() }), TrackCascadeCountsSchema, {
17344
17377
  kind: "mutation",
17345
17378
  auth: "admin"
17379
+ }), method(object({ deviceId: number() }), DiskReconcileCountsSchema, {
17380
+ kind: "mutation",
17381
+ auth: "admin"
17346
17382
  }), method(object({
17347
17383
  deviceId: number(),
17348
17384
  trackIds: array(string()).min(1)
@@ -18893,6 +18929,24 @@ var CameraStatusDegradationSchema = object({
18893
18929
  *
18894
18930
  * See spec: `docs/superpowers/specs/2026-06-24-camera-status-aggregator-cap.md`
18895
18931
  */
18932
+ var DiskReconcileJobSchema = object({
18933
+ state: _enum([
18934
+ "idle",
18935
+ "running",
18936
+ "done",
18937
+ "error"
18938
+ ]),
18939
+ total: number().int().nonnegative(),
18940
+ completed: number().int().nonnegative(),
18941
+ currentDeviceId: number().int().nullable(),
18942
+ failed: array(number().int()).readonly(),
18943
+ mediaDropped: number().int().nonnegative(),
18944
+ tracks: number().int().nonnegative(),
18945
+ events: number().int().nonnegative(),
18946
+ startedAtMs: number().int().nullable(),
18947
+ finishedAtMs: number().int().nullable(),
18948
+ error: string().nullable()
18949
+ });
18896
18950
  var CameraStatusSchema = object({
18897
18951
  deviceId: number(),
18898
18952
  assignment: CameraAssignmentStatusSchema,
@@ -19124,7 +19178,10 @@ method(StorageMigrationLeaseInputSchema, object({ paused: literal(true) }), {
19124
19178
  }), CameraSwitchGroupSchema, {
19125
19179
  kind: "mutation",
19126
19180
  auth: "admin"
19127
- }), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
19181
+ }), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), DiskReconcileJobSchema, {
19182
+ kind: "mutation",
19183
+ auth: "admin"
19184
+ }), method(_void(), DiskReconcileJobSchema, { auth: "admin" }), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
19128
19185
  name: string(),
19129
19186
  description: string().optional(),
19130
19187
  config: CameraPipelineConfigSchema
@@ -20301,20 +20358,6 @@ var VectorStatsResultSchema = object({
20301
20358
  exact: boolean()
20302
20359
  });
20303
20360
  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);
20304
- /**
20305
- * `videoclips` — the unified, navigable-clip surface for a camera.
20306
- *
20307
- * A device-scoped WRAPPER cap (like `pipeline-analytics`): exactly one active
20308
- * provider per device, substitutable. The DEFAULT provider (registered by
20309
- * `addon-post-analysis`, `defaultActive: true`) composes the analytics event
20310
- * log (markers + thumbnails) with the recorder's `getPlaybackManifest` — a clip
20311
- * is a time-WINDOW over existing footage, never a separate file. A camera that
20312
- * exposes NATIVE onboard clips (Reolink/Hikvision NVR) can later substitute the
20313
- * wrapper provider for its device and serve its own clip catalog + URLs.
20314
- *
20315
- * A `Clip` is purely time-based (subtree-blind): playback resolves segments by
20316
- * temporal overlap, so the API never decides `continuous` vs `events`.
20317
- */
20318
20361
  var ClipSchema = object({
20319
20362
  /** Opaque, provider-namespaced id. The default provider encodes the time
20320
20363
  * window so `getClipPlayback` is self-contained (no event re-query). */
@@ -20332,6 +20375,14 @@ var ClipSchema = object({
20332
20375
  endMs: number()
20333
20376
  }),
20334
20377
  /**
20378
+ * Distinct object classes attached to this visit (`person`, `car`, …),
20379
+ * dominant first, capped at {@link MAX_CLIP_LABELS}. The ribbon renders these
20380
+ * instead of the bare kind — "Object" tells the operator nothing a colour bar
20381
+ * did not. Absent (never empty) when the visit attached no classified object
20382
+ * event, so a motion/audio-only visit keeps its kind label.
20383
+ */
20384
+ labels: array(string()).max(3).optional(),
20385
+ /**
20335
20386
  * Lazy thumbnail URL, never inlined.
20336
20387
  *
20337
20388
  * Recording-derived clips (events-mode keep-window, and the prepared
@@ -20340,12 +20391,41 @@ var ClipSchema = object({
20340
20391
  * of the event that owns `kind` (object > motion > audio). Do not extract
20341
20392
  * a keyframe from the recorded segments. Other providers (onboard, HKSV)
20342
20393
  * mint their own stills.
20394
+ *
20395
+ * **VOUCHED, never fabricated.** A provider emits this only for an event it
20396
+ * has CONFIRMED owns at least one media row (its own, or its owning track's).
20397
+ * Absent is meaningful — "this visit has no event still" — never "we did not
20398
+ * look". Stamping it from a URL template made 35% of one camera's clips point
20399
+ * at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
20400
+ * A read that FAILS drops the claim; it never invents it.
20343
20401
  */
20344
20402
  thumbnail: string().optional(),
20345
- /** Analytics event ids that overlap this visit. Empty on footage-only clips.
20346
- * The default provider's visit grain puts many motion heartbeats on one clip
20347
- * instead of minting one clip per marker. */
20403
+ /**
20404
+ * An instant INSIDE this visit's footage, hole-safe, where a recorded still
20405
+ * can be decoded. Present whenever the visit came from recorded availability;
20406
+ * absent on a per-event padded window (there is no footage to promise).
20407
+ *
20408
+ * This is not a thumbnail and not a second byte path: it is the argument to
20409
+ * the recorder's existing still route. The surface — never the provider —
20410
+ * decides whether to use it. It is the midpoint of the visit's LONGEST
20411
+ * contiguous range, not of the visit: a visit spans its holes by
20412
+ * construction, so a naive midpoint lands in dead air.
20413
+ */
20414
+ stillAtMs: number().optional(),
20415
+ /**
20416
+ * Analytics event ids that overlap this visit — a BOUNDED sample, newest
20417
+ * first within kind (object → motion → audio), capped at
20418
+ * {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
20419
+ *
20420
+ * Bounded because it is not a payload the surface pages through: one visit on
20421
+ * device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
20422
+ * camera-day. Read {@link eventCount} for the true total.
20423
+ */
20348
20424
  eventIds: array(string()).optional(),
20425
+ /** How many analytics events actually overlap this visit. Differs from
20426
+ * `eventIds.length` exactly when the sample was capped — so a truncated
20427
+ * list is never mistaken for a quiet visit. */
20428
+ eventCount: number().int().nonnegative().optional(),
20349
20429
  /** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
20350
20430
  * than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
20351
20431
  * bar keeps showing them via `recording.getAvailability`. */
@@ -20816,7 +20896,14 @@ var SearchResultSchema = object({
20816
20896
  });
20817
20897
  var AutoUpdateSettingsSchema = object({
20818
20898
  channel: ChannelSchema,
20819
- intervalSeconds: number()
20899
+ intervalSeconds: number(),
20900
+ /**
20901
+ * Cadence of the "an update exists" POLLER, in seconds. Independent of
20902
+ * `channel`: the poller runs while auto-apply is `off`, because being told
20903
+ * about a publish and installing it are different decisions. Clamped
20904
+ * server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
20905
+ */
20906
+ updateCheckIntervalSeconds: number()
20820
20907
  });
20821
20908
  var AddonAutoUpdateSchema = ChannelWithInheritSchema;
20822
20909
  var RestartAddonResultSchema = unknown();
@@ -20957,7 +21044,9 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
20957
21044
  auth: "admin"
20958
21045
  }), method(_void(), AutoUpdateSettingsSchema, { auth: "admin" }), method(object({
20959
21046
  channel: ChannelSchema,
20960
- intervalSeconds: number().min(300).max(86400).optional()
21047
+ intervalSeconds: number().min(300).max(86400).optional(),
21048
+ /** Availability-poll cadence; see AutoUpdateSettingsSchema. */
21049
+ updateCheckIntervalSeconds: number().min(900).max(604800).optional()
20961
21050
  }), unknown(), {
20962
21051
  kind: "mutation",
20963
21052
  auth: "admin"
@@ -32987,6 +33076,12 @@ Object.freeze({
32987
33076
  addonId: null,
32988
33077
  access: "create"
32989
33078
  },
33079
+ "pipelineAnalytics.reconcileFromDisk": {
33080
+ capName: "pipeline-analytics",
33081
+ capScope: "device",
33082
+ addonId: null,
33083
+ access: "create"
33084
+ },
32990
33085
  "pipelineAnalytics.refreshStorageLocationsForMigration": {
32991
33086
  capName: "pipeline-analytics",
32992
33087
  capScope: "device",
@@ -33389,6 +33484,12 @@ Object.freeze({
33389
33484
  addonId: null,
33390
33485
  access: "view"
33391
33486
  },
33487
+ "pipelineOrchestrator.getReconcileFromDiskStatus": {
33488
+ capName: "pipeline-orchestrator",
33489
+ capScope: "system",
33490
+ addonId: null,
33491
+ access: "view"
33492
+ },
33392
33493
  "pipelineOrchestrator.listAgentSettings": {
33393
33494
  capName: "pipeline-orchestrator",
33394
33495
  capScope: "system",
@@ -33413,6 +33514,12 @@ Object.freeze({
33413
33514
  addonId: null,
33414
33515
  access: "create"
33415
33516
  },
33517
+ "pipelineOrchestrator.reconcileFromDisk": {
33518
+ capName: "pipeline-orchestrator",
33519
+ capScope: "system",
33520
+ addonId: null,
33521
+ access: "create"
33522
+ },
33416
33523
  "pipelineOrchestrator.removeAgentSettings": {
33417
33524
  capName: "pipeline-orchestrator",
33418
33525
  capScope: "system",
@@ -36404,6 +36511,11 @@ Object.freeze({
36404
36511
  form: "single",
36405
36512
  optional: true
36406
36513
  }],
36514
+ "pipelineAnalytics.reconcileFromDisk": [{
36515
+ name: "deviceId",
36516
+ form: "single",
36517
+ optional: false
36518
+ }],
36407
36519
  "pipelineAnalytics.restageRetrainTrack": [{
36408
36520
  name: "deviceId",
36409
36521
  form: "single",
@@ -37469,6 +37581,8 @@ DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb;
37469
37581
  DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
37470
37582
  DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
37471
37583
  DEFAULT_NATIVE_LEASE_SETTINGS.admission;
37584
+ var MB = 1024 * 1024;
37585
+ 1024 * MB, 3072 * MB;
37472
37586
  //#endregion
37473
37587
  //#region ../../node_modules/@apocaliss92/wyze-bridge-js/dist/index.js
37474
37588
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) {
package/dist/addon.mjs CHANGED
@@ -10,7 +10,7 @@ import { getCACertificates, setDefaultCACertificates } from "node:tls";
10
10
  //#region \0rolldown/runtime.js
11
11
  var __require$1 = /* @__PURE__ */ createRequire(import.meta.url);
12
12
  //#endregion
13
- //#region ../types/dist/event-category-C0lyLd5U.mjs
13
+ //#region ../types/dist/event-category-XfKNtfCc.mjs
14
14
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
15
15
  EventCategory["SystemBoot"] = "system.boot";
16
16
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -49,6 +49,22 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
49
49
  EventCategory["AddonInstalled"] = "addon.installed";
50
50
  EventCategory["AddonUninstalled"] = "addon.uninstalled";
51
51
  EventCategory["AddonCrashed"] = "addon.crashed";
52
+ /**
53
+ * A RUNNER the D6 crash circuit-breaker gave up on — terminal.
54
+ *
55
+ * `AddonCrashed` is the routine, self-healing fact ("it crashed; it is being
56
+ * respawned"); this is the one that never resolves itself. Emitted exactly
57
+ * once per trip, by `process-service.ts`, carrying the node, the runner, the
58
+ * addons it hosted and the crash count that tripped the breaker.
59
+ *
60
+ * It exists because a runner marked terminally `failed` used to be SILENT:
61
+ * on 2026-08-18 the `recorder` runner died of three unhandled ffmpeg spawn
62
+ * errors, the breaker stopped respawning it (correctly), `/health` kept
63
+ * returning 200, and fleet-wide recording was gone for 3h15 before the
64
+ * operator's phone told him. The Notification Center's system-event intake
65
+ * consumes this category and maps it to the `addon-crash-loop` kind.
66
+ */
67
+ EventCategory["AddonRunnerFailed"] = "addon.runner-failed";
52
68
  EventCategory["AddonError"] = "addon.error";
53
69
  EventCategory["AddonPageReady"] = "addon.page-ready";
54
70
  EventCategory["AddonWidgetReady"] = "addon.widget-ready";
@@ -7611,6 +7627,12 @@ var RecordingConfigSchema = object({
7611
7627
  /** DERIVED summary of `bands`, stamped by the recorder on every save.
7612
7628
  * Authoring it has no effect — see {@link RecordingStorageModeSchema}. */
7613
7629
  mode: RecordingStorageModeSchema.optional(),
7630
+ /**
7631
+ * Which assigned broker slots to record. Absent / empty = {@link
7632
+ * DEFAULT_RECORDING_PROFILES} (`high`+`low`) intersected with the
7633
+ * camera's currently assigned slots — never `mid` unless the operator
7634
+ * picks it, and never a slot the broker has not assigned.
7635
+ */
7614
7636
  profiles: array(CamProfileSchema).optional(),
7615
7637
  segmentSeconds: number().int().positive().optional(),
7616
7638
  /**
@@ -7691,7 +7713,11 @@ var RelocateFootageInputSchema = object({
7691
7713
  profiles: array(string()).optional(),
7692
7714
  /** Copy throttle in MB/s (default 40) — the drain is a background chore,
7693
7715
  * never allowed to starve live writers. */
7694
- throttleMbps: number().min(1).max(1e3).optional()
7716
+ throttleMbps: number().min(1).max(1e3).optional(),
7717
+ /** Move only segments whose startMs is >= this. Absent = the whole source
7718
+ * pile. Used when a full drain is too expensive and the operator only
7719
+ * wants the recent window on the new disk. */
7720
+ sinceMs: number().int().optional()
7695
7721
  });
7696
7722
  /** Internal, lease-scoped participant operation. It is intentionally separate
7697
7723
  * from persistent recording settings: a migration never changes
@@ -14573,6 +14599,7 @@ var NcSystemEventKindSchema = _enum([
14573
14599
  "node-offline",
14574
14600
  "node-inference-unavailable",
14575
14601
  "detection-blind",
14602
+ "addon-crash-loop",
14576
14603
  "addon-update-available",
14577
14604
  "server-update-available",
14578
14605
  "alarm-triggered",
@@ -17146,6 +17173,12 @@ var TrackCascadeCountsSchema = object({
17146
17173
  /** Per-track CLIP search vectors removed (best-effort). */
17147
17174
  embeddings: number().int()
17148
17175
  });
17176
+ /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
17177
+ var DiskReconcileCountsSchema = object({
17178
+ mediaDropped: number().int(),
17179
+ tracks: number().int(),
17180
+ events: number().int()
17181
+ });
17149
17182
  /** Event-store footprint for one camera. */
17150
17183
  var EventStoreDeviceFootprintSchema = object({
17151
17184
  deviceId: number(),
@@ -17322,6 +17355,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17322
17355
  }), method(object({ deviceId: number() }), TrackCascadeCountsSchema, {
17323
17356
  kind: "mutation",
17324
17357
  auth: "admin"
17358
+ }), method(object({ deviceId: number() }), DiskReconcileCountsSchema, {
17359
+ kind: "mutation",
17360
+ auth: "admin"
17325
17361
  }), method(object({
17326
17362
  deviceId: number(),
17327
17363
  trackIds: array(string()).min(1)
@@ -18872,6 +18908,24 @@ var CameraStatusDegradationSchema = object({
18872
18908
  *
18873
18909
  * See spec: `docs/superpowers/specs/2026-06-24-camera-status-aggregator-cap.md`
18874
18910
  */
18911
+ var DiskReconcileJobSchema = object({
18912
+ state: _enum([
18913
+ "idle",
18914
+ "running",
18915
+ "done",
18916
+ "error"
18917
+ ]),
18918
+ total: number().int().nonnegative(),
18919
+ completed: number().int().nonnegative(),
18920
+ currentDeviceId: number().int().nullable(),
18921
+ failed: array(number().int()).readonly(),
18922
+ mediaDropped: number().int().nonnegative(),
18923
+ tracks: number().int().nonnegative(),
18924
+ events: number().int().nonnegative(),
18925
+ startedAtMs: number().int().nullable(),
18926
+ finishedAtMs: number().int().nullable(),
18927
+ error: string().nullable()
18928
+ });
18875
18929
  var CameraStatusSchema = object({
18876
18930
  deviceId: number(),
18877
18931
  assignment: CameraAssignmentStatusSchema,
@@ -19103,7 +19157,10 @@ method(StorageMigrationLeaseInputSchema, object({ paused: literal(true) }), {
19103
19157
  }), CameraSwitchGroupSchema, {
19104
19158
  kind: "mutation",
19105
19159
  auth: "admin"
19106
- }), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
19160
+ }), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), DiskReconcileJobSchema, {
19161
+ kind: "mutation",
19162
+ auth: "admin"
19163
+ }), method(_void(), DiskReconcileJobSchema, { auth: "admin" }), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
19107
19164
  name: string(),
19108
19165
  description: string().optional(),
19109
19166
  config: CameraPipelineConfigSchema
@@ -20280,20 +20337,6 @@ var VectorStatsResultSchema = object({
20280
20337
  exact: boolean()
20281
20338
  });
20282
20339
  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);
20283
- /**
20284
- * `videoclips` — the unified, navigable-clip surface for a camera.
20285
- *
20286
- * A device-scoped WRAPPER cap (like `pipeline-analytics`): exactly one active
20287
- * provider per device, substitutable. The DEFAULT provider (registered by
20288
- * `addon-post-analysis`, `defaultActive: true`) composes the analytics event
20289
- * log (markers + thumbnails) with the recorder's `getPlaybackManifest` — a clip
20290
- * is a time-WINDOW over existing footage, never a separate file. A camera that
20291
- * exposes NATIVE onboard clips (Reolink/Hikvision NVR) can later substitute the
20292
- * wrapper provider for its device and serve its own clip catalog + URLs.
20293
- *
20294
- * A `Clip` is purely time-based (subtree-blind): playback resolves segments by
20295
- * temporal overlap, so the API never decides `continuous` vs `events`.
20296
- */
20297
20340
  var ClipSchema = object({
20298
20341
  /** Opaque, provider-namespaced id. The default provider encodes the time
20299
20342
  * window so `getClipPlayback` is self-contained (no event re-query). */
@@ -20311,6 +20354,14 @@ var ClipSchema = object({
20311
20354
  endMs: number()
20312
20355
  }),
20313
20356
  /**
20357
+ * Distinct object classes attached to this visit (`person`, `car`, …),
20358
+ * dominant first, capped at {@link MAX_CLIP_LABELS}. The ribbon renders these
20359
+ * instead of the bare kind — "Object" tells the operator nothing a colour bar
20360
+ * did not. Absent (never empty) when the visit attached no classified object
20361
+ * event, so a motion/audio-only visit keeps its kind label.
20362
+ */
20363
+ labels: array(string()).max(3).optional(),
20364
+ /**
20314
20365
  * Lazy thumbnail URL, never inlined.
20315
20366
  *
20316
20367
  * Recording-derived clips (events-mode keep-window, and the prepared
@@ -20319,12 +20370,41 @@ var ClipSchema = object({
20319
20370
  * of the event that owns `kind` (object > motion > audio). Do not extract
20320
20371
  * a keyframe from the recorded segments. Other providers (onboard, HKSV)
20321
20372
  * mint their own stills.
20373
+ *
20374
+ * **VOUCHED, never fabricated.** A provider emits this only for an event it
20375
+ * has CONFIRMED owns at least one media row (its own, or its owning track's).
20376
+ * Absent is meaningful — "this visit has no event still" — never "we did not
20377
+ * look". Stamping it from a URL template made 35% of one camera's clips point
20378
+ * at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
20379
+ * A read that FAILS drops the claim; it never invents it.
20322
20380
  */
20323
20381
  thumbnail: string().optional(),
20324
- /** Analytics event ids that overlap this visit. Empty on footage-only clips.
20325
- * The default provider's visit grain puts many motion heartbeats on one clip
20326
- * instead of minting one clip per marker. */
20382
+ /**
20383
+ * An instant INSIDE this visit's footage, hole-safe, where a recorded still
20384
+ * can be decoded. Present whenever the visit came from recorded availability;
20385
+ * absent on a per-event padded window (there is no footage to promise).
20386
+ *
20387
+ * This is not a thumbnail and not a second byte path: it is the argument to
20388
+ * the recorder's existing still route. The surface — never the provider —
20389
+ * decides whether to use it. It is the midpoint of the visit's LONGEST
20390
+ * contiguous range, not of the visit: a visit spans its holes by
20391
+ * construction, so a naive midpoint lands in dead air.
20392
+ */
20393
+ stillAtMs: number().optional(),
20394
+ /**
20395
+ * Analytics event ids that overlap this visit — a BOUNDED sample, newest
20396
+ * first within kind (object → motion → audio), capped at
20397
+ * {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
20398
+ *
20399
+ * Bounded because it is not a payload the surface pages through: one visit on
20400
+ * device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
20401
+ * camera-day. Read {@link eventCount} for the true total.
20402
+ */
20327
20403
  eventIds: array(string()).optional(),
20404
+ /** How many analytics events actually overlap this visit. Differs from
20405
+ * `eventIds.length` exactly when the sample was capped — so a truncated
20406
+ * list is never mistaken for a quiet visit. */
20407
+ eventCount: number().int().nonnegative().optional(),
20328
20408
  /** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
20329
20409
  * than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
20330
20410
  * bar keeps showing them via `recording.getAvailability`. */
@@ -20795,7 +20875,14 @@ var SearchResultSchema = object({
20795
20875
  });
20796
20876
  var AutoUpdateSettingsSchema = object({
20797
20877
  channel: ChannelSchema,
20798
- intervalSeconds: number()
20878
+ intervalSeconds: number(),
20879
+ /**
20880
+ * Cadence of the "an update exists" POLLER, in seconds. Independent of
20881
+ * `channel`: the poller runs while auto-apply is `off`, because being told
20882
+ * about a publish and installing it are different decisions. Clamped
20883
+ * server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
20884
+ */
20885
+ updateCheckIntervalSeconds: number()
20799
20886
  });
20800
20887
  var AddonAutoUpdateSchema = ChannelWithInheritSchema;
20801
20888
  var RestartAddonResultSchema = unknown();
@@ -20936,7 +21023,9 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
20936
21023
  auth: "admin"
20937
21024
  }), method(_void(), AutoUpdateSettingsSchema, { auth: "admin" }), method(object({
20938
21025
  channel: ChannelSchema,
20939
- intervalSeconds: number().min(300).max(86400).optional()
21026
+ intervalSeconds: number().min(300).max(86400).optional(),
21027
+ /** Availability-poll cadence; see AutoUpdateSettingsSchema. */
21028
+ updateCheckIntervalSeconds: number().min(900).max(604800).optional()
20940
21029
  }), unknown(), {
20941
21030
  kind: "mutation",
20942
21031
  auth: "admin"
@@ -32966,6 +33055,12 @@ Object.freeze({
32966
33055
  addonId: null,
32967
33056
  access: "create"
32968
33057
  },
33058
+ "pipelineAnalytics.reconcileFromDisk": {
33059
+ capName: "pipeline-analytics",
33060
+ capScope: "device",
33061
+ addonId: null,
33062
+ access: "create"
33063
+ },
32969
33064
  "pipelineAnalytics.refreshStorageLocationsForMigration": {
32970
33065
  capName: "pipeline-analytics",
32971
33066
  capScope: "device",
@@ -33368,6 +33463,12 @@ Object.freeze({
33368
33463
  addonId: null,
33369
33464
  access: "view"
33370
33465
  },
33466
+ "pipelineOrchestrator.getReconcileFromDiskStatus": {
33467
+ capName: "pipeline-orchestrator",
33468
+ capScope: "system",
33469
+ addonId: null,
33470
+ access: "view"
33471
+ },
33371
33472
  "pipelineOrchestrator.listAgentSettings": {
33372
33473
  capName: "pipeline-orchestrator",
33373
33474
  capScope: "system",
@@ -33392,6 +33493,12 @@ Object.freeze({
33392
33493
  addonId: null,
33393
33494
  access: "create"
33394
33495
  },
33496
+ "pipelineOrchestrator.reconcileFromDisk": {
33497
+ capName: "pipeline-orchestrator",
33498
+ capScope: "system",
33499
+ addonId: null,
33500
+ access: "create"
33501
+ },
33395
33502
  "pipelineOrchestrator.removeAgentSettings": {
33396
33503
  capName: "pipeline-orchestrator",
33397
33504
  capScope: "system",
@@ -36383,6 +36490,11 @@ Object.freeze({
36383
36490
  form: "single",
36384
36491
  optional: true
36385
36492
  }],
36493
+ "pipelineAnalytics.reconcileFromDisk": [{
36494
+ name: "deviceId",
36495
+ form: "single",
36496
+ optional: false
36497
+ }],
36386
36498
  "pipelineAnalytics.restageRetrainTrack": [{
36387
36499
  name: "deviceId",
36388
36500
  form: "single",
@@ -37448,6 +37560,8 @@ DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb;
37448
37560
  DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
37449
37561
  DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
37450
37562
  DEFAULT_NATIVE_LEASE_SETTINGS.admission;
37563
+ var MB = 1024 * 1024;
37564
+ 1024 * MB, 3072 * MB;
37451
37565
  //#endregion
37452
37566
  //#region ../../node_modules/@apocaliss92/wyze-bridge-js/dist/index.js
37453
37567
  var __require = /* @__PURE__ */ ((x) => typeof __require$1 !== "undefined" ? __require$1 : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof __require$1 !== "undefined" ? __require$1 : a)[b] }) : x)(function(x) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-wyze",
3
- "version": "0.2.23",
3
+ "version": "0.2.25",
4
4
  "description": "Wyze camera device-provider addon for CamStack — wraps the @apocaliss92/wyze-bridge-js P2P/DTLS client, feeding the stream-broker via the pull-rfc4571 lazy-publish path (a structural twin of addon-provider-reolink)",
5
5
  "keywords": [
6
6
  "camstack",