@camstack/addon-pipeline 1.2.272 → 1.2.274

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 (33) hide show
  1. package/dist/audio-analyzer/index.js +2 -2
  2. package/dist/audio-analyzer/index.mjs +2 -2
  3. package/dist/{default-detection-model-DqrqHCuw.mjs → default-detection-model-BXwE-Dvo.mjs} +1 -1
  4. package/dist/{default-detection-model-kQnvoRyv.js → default-detection-model-CAWhHQo2.js} +1 -1
  5. package/dist/detection-pipeline/index.js +4 -4
  6. package/dist/detection-pipeline/index.mjs +4 -4
  7. package/dist/{dist-DBHsAlUE.mjs → dist-g5MfpiBh.mjs} +363 -20
  8. package/dist/{dist-DygcLvgG.js → dist-k6Vu-AhZ.js} +386 -19
  9. package/dist/motion-wasm/index.js +1 -1
  10. package/dist/motion-wasm/index.mjs +1 -1
  11. package/dist/{node-CACCuE7t.js → node--u_jUObS.js} +1 -1
  12. package/dist/{node-j_3VvNQJ.mjs → node-BXsHQH5h.mjs} +1 -1
  13. package/dist/pipeline-runner/index.js +3 -3
  14. package/dist/pipeline-runner/index.mjs +3 -3
  15. package/dist/{process-memory-fse8NnRi.js → process-memory-DN7opum2.js} +1 -1
  16. package/dist/{process-memory-DcoIQGG5.mjs → process-memory-Gl3EowL-.mjs} +1 -1
  17. package/dist/recorder/index.js +572 -3
  18. package/dist/recorder/index.mjs +572 -3
  19. package/dist/{segment-demux-js-Ce24Y2G0.js → segment-demux-js-DHX8o8pI.js} +1 -1
  20. package/dist/{segment-demux-js-DSgNEMf6.mjs → segment-demux-js-sYP_jndp.mjs} +1 -1
  21. package/dist/stream-broker/_stub.js +2 -2
  22. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BSZUBMBY.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CEy8IryH.mjs} +2 -2
  23. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BlXyfeXU.mjs +26 -0
  24. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-MU3_q1MX.mjs +26 -0
  25. package/dist/stream-broker/demux-worker-child.js +1 -1
  26. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  27. package/dist/stream-broker/{hostInit-lVR0hopj.mjs → hostInit-UKNN4qes.mjs} +2 -2
  28. package/dist/stream-broker/index.js +3 -3
  29. package/dist/stream-broker/index.mjs +3 -3
  30. package/dist/stream-broker/remoteEntry.js +1 -1
  31. package/package.json +5 -1
  32. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-_bHa4uUp.mjs +0 -26
  33. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CeR_IxxU.mjs +0 -26
@@ -5360,7 +5360,7 @@ var ZodIssueCode = {
5360
5360
  var ZodFirstPartyTypeKind;
5361
5361
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5362
5362
  //#endregion
5363
- //#region ../types/dist/sleep-CiR4z7r9.mjs
5363
+ //#region ../types/dist/sleep-CaB_hL5g.mjs
5364
5364
  /**
5365
5365
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5366
5366
  * window to float samples (D455).
@@ -8341,6 +8341,8 @@ var EVENT_OWNER_TYPES = [
8341
8341
  * nothing failing until a caller asked.
8342
8342
  */
8343
8343
  var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
8344
+ /** The same list as a Zod enum, for the cap input that carries it. */
8345
+ var MediaPresenceOwnerKindSchema = _enum([...EVENT_OWNER_TYPES, "track"]);
8344
8346
  new Set(EVENT_OWNER_TYPES);
8345
8347
  var EncodeProfileSchema = object({
8346
8348
  video: object({
@@ -9321,6 +9323,24 @@ var DetectionCatalogClassMapSchema = object({
9321
9323
  preserveOriginal: boolean()
9322
9324
  });
9323
9325
  /**
9326
+ * THE canonical event-clip pad: the time window a single-timestamp analytics
9327
+ * event expands to when joined with footage (clip window = `[timestamp - preMs,
9328
+ * timestamp + postMs]`). Matches the admin-ui clip window (−5s/+10s).
9329
+ *
9330
+ * Every consumer derives from this ONE constant so event↔footage boundaries
9331
+ * agree everywhere (C1): today that is the `videoclips` default provider
9332
+ * (addon-post-analysis), which pads its clip windows with it. The recorder used
9333
+ * to pad in-RAM playback markers with it too; those markers had no reader
9334
+ * anywhere in the repo and were deleted on 2026-08-29.
9335
+ *
9336
+ * NOTE: this is a UI/JOIN convention, NOT the `events`-band keep/discard gate —
9337
+ * that uses the per-device `preBufferSec`/`postBufferSec` config.
9338
+ */
9339
+ var EVENT_PAD_MS = {
9340
+ preMs: 5e3,
9341
+ postMs: 1e4
9342
+ };
9343
+ /**
9324
9344
  * Numeric day-of-week: 0 = Sunday … 6 = Saturday (matches `Date.getDay`).
9325
9345
  * Named `RecordingWeekday` to avoid collision with the string-union
9326
9346
  * `Weekday` exported from `interfaces/timezones.ts`.
@@ -9537,7 +9557,20 @@ function stripRetiredBandPreBufferSec(config) {
9537
9557
  strippedBands
9538
9558
  };
9539
9559
  }
9540
- DEFAULT_EVENTS_BAND_BUFFER_SEC.postBufferSec * 1e3;
9560
+ /**
9561
+ * Adjacent recorded ranges closer than this belong to the SAME videoclip visit.
9562
+ *
9563
+ * This is NOT the recorder calendar merge (`RANGE_MERGE_GAP_MS = 5s`). That
9564
+ * threshold is for the timeline bar, which must show exact holes — including
9565
+ * the ~8s GOP/keyframe rolls measured on events-mode cameras (dispensa 1438,
9566
+ * 2026-08-16). A videoclip is the events-mode keep-window (writer session),
9567
+ * whose idle bound is `postBufferSec`. Using that default (30s) glues those
9568
+ * intra-visit holes without joining visits hours apart.
9569
+ *
9570
+ * Segments stay the source of truth on disk (`startMs-durMs-bytes.m4s`); a
9571
+ * visit is derived, not a second copy of the files.
9572
+ */
9573
+ var VISIT_MERGE_GAP_MS = DEFAULT_EVENTS_BAND_BUFFER_SEC.postBufferSec * 1e3;
9541
9574
  /**
9542
9575
  * Per-device retention overrides. Every field is optional; an unset or `0`
9543
9576
  * value inherits the node-wide recorder default. Only footage-lifetime limits
@@ -23927,7 +23960,11 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
23927
23960
  ownerType: EventOwnerTypeSchema,
23928
23961
  eventId: number().int(),
23929
23962
  deviceId: number()
23930
- }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
23963
+ }), array(MediaFileInfoSchema).readonly()), method(object({
23964
+ deviceId: number(),
23965
+ ownerKind: MediaPresenceOwnerKindSchema,
23966
+ ownerIds: array(string()).max(5e3)
23967
+ }), array(string()).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
23931
23968
  kind: "mutation",
23932
23969
  auth: "admin"
23933
23970
  }), method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
@@ -27155,6 +27192,18 @@ method(VectorDeclareIndexInputSchema, _void(), {
27155
27192
  kind: "mutation",
27156
27193
  auth: "admin"
27157
27194
  }), method(VectorStatsInputSchema, VectorStatsResultSchema, { auth: "admin" });
27195
+ _enum([
27196
+ "queue-full",
27197
+ "camera-backoff",
27198
+ "sleeping",
27199
+ "camera-refused",
27200
+ "no-keyframe",
27201
+ "no-catalog-row",
27202
+ "unsupported",
27203
+ "unknown-device",
27204
+ "uid-missing"
27205
+ ]);
27206
+ _enum(["deferred", "final"]);
27158
27207
  var ClipSchema = object({
27159
27208
  /** Opaque, provider-namespaced id. The default provider encodes the time
27160
27209
  * window so `getClipPlayback` is self-contained (no event re-query). */
@@ -27229,30 +27278,302 @@ var ClipSchema = object({
27229
27278
  holes: array(object({
27230
27279
  startMs: number(),
27231
27280
  endMs: number()
27232
- })).optional()
27281
+ })).optional(),
27282
+ /**
27283
+ * A URL that MAY produce this clip's still, asked only for rows actually on
27284
+ * screen. The opposite of {@link ClipSchema.thumbnail}: that one is VOUCHED
27285
+ * (a JPEG is already on disk), this one is an offer. The route answers 200
27286
+ * with the image, or 204 with `x-camstack-reason` when it could not mint one
27287
+ * — a surface latches that refusal to the instant rather than retrying.
27288
+ *
27289
+ * Never both: a clip with a vouched `thumbnail` needs no mint.
27290
+ */
27291
+ thumbnailMint: string().optional(),
27292
+ /**
27293
+ * Why no still will be produced for this clip RIGHT NOW — set when the
27294
+ * provider already knows, so the surface draws the glyph and the reason
27295
+ * instead of firing a mint that cannot succeed.
27296
+ *
27297
+ * `sleeping` — a standalone battery camera; a read would be a wake (D549 4).
27298
+ * `camera-refused` — the camera answered the CoverPreview with a refusal.
27299
+ * `no-keyframe` — the window holds no decodable I-frame (an event that sits
27300
+ * inside no file is the measured case).
27301
+ * `unsupported` — this source cannot mint stills at all.
27302
+ */
27303
+ thumbnailUnavailable: object({ reason: _enum([
27304
+ "sleeping",
27305
+ "camera-refused",
27306
+ "no-keyframe",
27307
+ "unsupported"
27308
+ ]) }).optional(),
27309
+ /**
27310
+ * When the catalog this row came from was last CONFIRMED against the device.
27311
+ * Absent means "this row was read live". A persisted catalog served while a
27312
+ * camera sleeps carries the age it really has — a cached list is never drawn
27313
+ * as current (D549 13).
27314
+ */
27315
+ catalogAsOf: number().optional(),
27316
+ /**
27317
+ * The camera's OWN type strings for this clip, all of them, unmapped
27318
+ * (`md`, `people`, `dog_cat`, `sched`, …). Kept beside {@link labels}
27319
+ * because a firmware inventing a type must not vanish: the mapping into our
27320
+ * filter vocabulary is lossy on purpose and this is the lossless copy
27321
+ * (D549 20).
27322
+ */
27323
+ nativeTypes: array(string()).optional(),
27324
+ /**
27325
+ * What the subject DID — `crossline`, `intrude`, `loitering`. A behaviour
27326
+ * travels BESIDE a class, never instead of one, and the class filter ignores
27327
+ * it: "a person crossed a line" is still a person (D549 20).
27328
+ */
27329
+ behaviours: array(string()).optional(),
27330
+ /**
27331
+ * The same recording as two files — the sub twin (what the row and its
27332
+ * thumbnail are) and its main twin, matched at listing time so a quality
27333
+ * change never re-searches the camera (D549 15). `getClipPlayback`'s
27334
+ * `profile` picks between them: `low | mid` → sub, `high` → main.
27335
+ */
27336
+ streams: object({
27337
+ sub: object({
27338
+ id: string(),
27339
+ bytes: number().optional()
27340
+ }).optional(),
27341
+ main: object({
27342
+ id: string(),
27343
+ bytes: number().optional()
27344
+ }).optional()
27345
+ }).optional(),
27346
+ /** The camera says it holds a sub-stream copy of this recording. */
27347
+ supportSub: boolean().optional(),
27348
+ /**
27349
+ * Whether this row has bytes behind it. ABSENT means yes — every clip that
27350
+ * IS a file is playable, and only a source that lists EVENTS can produce a
27351
+ * row with nothing to play (the measured case: 1 of 29 hub events on 3628
27352
+ * fell inside no file). Such a row is shown, never dropped and never offered
27353
+ * as playable-then-failing (D549 19).
27354
+ */
27355
+ playable: boolean().optional(),
27356
+ /** Why {@link playable} is false, verbatim (`no-file-for-window`). */
27357
+ unplayableReason: string().optional(),
27358
+ /**
27359
+ * This clip is STILL BEING WRITTEN, so {@link ClipSchema.timeRange}`.endMs`
27360
+ * is not its end.
27361
+ *
27362
+ * Absent — the common case — means the row is closed and its `endMs` is the
27363
+ * end of the recording. Present and `true` means the source told us the file
27364
+ * has no end yet, and the `endMs` we carry is whatever the camera's index
27365
+ * entry happened to hold: on a Reolink E1 Outdoor PoE (592, measured
27366
+ * 2026-09-20) the newest file `…_192927_000000_…_0.mp4` reported an `endTime`
27367
+ * of 19:29:56 and STILL reported it eight minutes later, while the file went
27368
+ * on growing. A surface that drew 29 s there was lying about a clip that
27369
+ * plays for minutes, on the one row the operator looks at first.
27370
+ *
27371
+ * `endMs` is deliberately still a number: it is the best bound anything has
27372
+ * for a mint window or a byte fetch, and every consumer already requires it.
27373
+ * This flag says what it is WORTH, and a duration is not drawn from it.
27374
+ */
27375
+ inProgress: boolean().optional()
27233
27376
  });
27234
27377
  var ClipPlaybackSchema = object({
27235
- /** HLS master URL through the hub data-plane (Range + token in path). */
27378
+ /**
27379
+ * A media URL through the hub data-plane. {@link ClipPlaybackSchema.format}
27380
+ * says what kind — an HLS master playlist for a recording-derived clip, a
27381
+ * progressive MP4 for a native file the vendor addon muxed and serves with
27382
+ * `Range`. The URL is NOT a credential: both planes are registered
27383
+ * `access: 'authenticated'` and the hub's `/addon/<id>/<prefix>` proxy takes
27384
+ * the session cookie, so nothing is appended to it (D549 22).
27385
+ */
27236
27386
  playbackUrl: string(),
27387
+ /**
27388
+ * How to play {@link playbackUrl}. Absent means `hls` — the shape every
27389
+ * existing consumer already assumes. A player that branches on this is the
27390
+ * one change a native clip needs; a player that ignores it will hand an MP4
27391
+ * to hls.js and fail parsing it as a manifest.
27392
+ */
27393
+ format: _enum(["hls", "mp4"]).optional(),
27394
+ /**
27395
+ * Which twin was actually served. A clip that holds only one stream answers
27396
+ * with the one it has, and the surface SAYS so — a missing main twin is
27397
+ * never served silently as if it were the asked-for quality (D549 15).
27398
+ */
27399
+ served: CamProfileSchema.optional(),
27237
27400
  /** Optional LAN/remote alternates for the same clip. */
27238
27401
  playbackEndpoints: array(string()).optional(),
27239
27402
  token: string().optional()
27240
27403
  });
27241
- DeviceType.Camera, method(object({
27242
- deviceId: number(),
27243
- since: number(),
27244
- until: number(),
27245
- limit: number().int().positive().optional()
27246
- }), array(ClipSchema).readonly(), {
27247
- kind: "query",
27248
- auth: "admin"
27249
- }), method(object({
27250
- deviceId: number(),
27251
- clipId: string()
27252
- }), ClipPlaybackSchema, {
27253
- kind: "query",
27254
- auth: "admin"
27404
+ /**
27405
+ * Why a source cannot answer right now — per SOURCE, never fleet-wide.
27406
+ *
27407
+ * `ok` is the only state whose clip list may be read as complete. The other
27408
+ * three exist because an empty list from a sleeping camera reads as "this
27409
+ * camera has no recordings", which is the defect this whole line of work is
27410
+ * about: a battery camera nobody woke (`sleeping`), a camera that could not be
27411
+ * reached at all (`unreachable`, also what a provider that THREW reports), a
27412
+ * camera whose SD card is not mounted (`no-storage`, measured on 640 —
27413
+ * `HddInfo mount 0`, honestly nothing to list rather than "no clips"), and a
27414
+ * camera whose OWN index disagrees with its OWN calendar (`index-empty`,
27415
+ * measured on 618: the calendar marks 3–20 September, days 6–20 list zero
27416
+ * files on both streams and both filters, with 177 GB free). That last one is
27417
+ * a defect ON THE CAMERA, and the only honest thing a provider can do is say
27418
+ * which days it asked for and got nothing — "no clips" would be a lie about a
27419
+ * card that is full of them.
27420
+ */
27421
+ var ClipSourceAvailabilitySchema = object({
27422
+ state: _enum([
27423
+ "ok",
27424
+ "sleeping",
27425
+ "unreachable",
27426
+ "no-storage",
27427
+ "index-empty"
27428
+ ]),
27429
+ /** Free text, shown verbatim. Names the camera's own refusal when there is one. */
27430
+ reason: string().optional(),
27431
+ /** When this source's catalog was last CONFIRMED. A cached list is never
27432
+ * drawn as current: the surface shows the age whenever it is older than the
27433
+ * refresh interval. */
27434
+ catalogAsOf: number().optional()
27435
+ });
27436
+ /**
27437
+ * One SOURCE of clips for a camera — a row of the picker, and the namespace
27438
+ * every clip id from it is prefixed with (`analytics`,
27439
+ * `native:reolink:onboard`, `hksv`, …).
27440
+ *
27441
+ * A provider lists the sources IT serves for that device, and answers for each
27442
+ * of them whether it can answer at all. A provider with nothing to offer on a
27443
+ * camera returns `[]` — it is not that camera's business.
27444
+ */
27445
+ var ClipSourceSchema = object({
27446
+ /** The value this source stamps on {@link ClipSchema.source}, and the prefix
27447
+ * of every clip id it mints. `getClipPlayback` routes on it. */
27448
+ source: string(),
27449
+ /** Operator-facing name of the source ("CamStack recordings", "SD card"). */
27450
+ label: string(),
27451
+ /**
27452
+ * The addon that SERVES this row.
27453
+ *
27454
+ * A surface cannot otherwise resolve a source to who answers it, and the
27455
+ * alternative — a `native:reolink:* → provider-reolink` table inside the
27456
+ * widget — is a second authority on provider identity living in the one
27457
+ * package with no business knowing it, wrong the day a third source appears
27458
+ * (D557). One addon may serve SEVERAL sources, which is why the per-device
27459
+ * switch is keyed by SOURCE and not by this (D555) — `addon-provider-reolink`
27460
+ * served a hub child two of them until the two views were measured to be one
27461
+ * store read twice (D555) and then read ONE way (D565).
27462
+ *
27463
+ * Optional for version skew only. The collection dispatcher stamps it from
27464
+ * the registry, so a row that travelled through the fan-out carries the
27465
+ * authoritative id whatever the provider filled in.
27466
+ */
27467
+ addonId: string().optional(),
27468
+ availability: ClipSourceAvailabilitySchema
27255
27469
  });
27470
+ var videoclipsCapability = {
27471
+ name: "videoclips",
27472
+ scope: "device",
27473
+ mode: "collection",
27474
+ kind: "wrapper",
27475
+ defaultActive: true,
27476
+ /** A clip is a window over a camera's footage — the cap is meaningless on a
27477
+ * sensor, a button or an event emitter, and the `defaultActive` auto-bind
27478
+ * reads this to decide which devices it may claim. */
27479
+ deviceTypes: [DeviceType.Camera],
27480
+ /**
27481
+ * The Clips section of a camera's device details is FRAMEWORK-DERIVED (D14):
27482
+ * the aggregator turns this declaration into the `type:'widget'` section and
27483
+ * `DeviceDetail.tsx` is never edited. `videoclips` is the first WRAPPER cap
27484
+ * to declare one — every previous `host/` widget cap is `deviceNative` — so
27485
+ * `device-config-widget-wrapped-binding.spec.ts` pins that a `kind:'wrapped'`
27486
+ * binding entry derives the same section a native one does.
27487
+ *
27488
+ * **It is a SECTION of the Recording tab, at the end of it — not a tab of
27489
+ * its own.** It shipped as a `clips` top-tab and the operator rejected the
27490
+ * placement: *"utilizzerei la stessa tab recordings, lì abbiamo già tutto il
27491
+ * necessario, una nuova sezione alla fine per le clips"*. The Recording tab
27492
+ * already holds the recorder's panel, the schedule bands and the unified
27493
+ * retention policy; footage the camera itself holds is the same question,
27494
+ * asked of a different store. `order: 100` puts it after all of them with
27495
+ * room left in front. The `clips` entry in `WELL_KNOWN_TABS` went with it —
27496
+ * a well-known id nobody declares is an invitation to mint the tab again.
27497
+ *
27498
+ * `topTab` stays: the browser owns a pane (a day's tiles, a source list and
27499
+ * a player) and the Config tab's inner bar has no room for one. The widget
27500
+ * itself is `host/clips-browser` in ui-library's `HOST_WIDGETS`, and because
27501
+ * the admin Recordings page renders every `location:'top-tab'` +
27502
+ * `tab:'recording'` section behind its camera picker
27503
+ * (`CameraRecordingSettingsSection`), this declaration lands the section on
27504
+ * BOTH surfaces with no second wiring.
27505
+ */
27506
+ deviceConfig: { ui: {
27507
+ kind: "widget",
27508
+ widgetId: "host/clips-browser",
27509
+ tab: "recording",
27510
+ topTab: true,
27511
+ label: "Clips",
27512
+ order: 100
27513
+ } },
27514
+ methods: {
27515
+ listClips: method(object({
27516
+ deviceId: number(),
27517
+ since: number(),
27518
+ until: number(),
27519
+ limit: number().int().positive().optional(),
27520
+ /**
27521
+ * WHICH provider to ask — the `addonId` a {@link ClipSourceSchema} row
27522
+ * carries, never a source id and never a list.
27523
+ *
27524
+ * Absent means the device's BOUND provider, which is CamStack on every
27525
+ * camera today because that is what `deviceManager.getBindings`
27526
+ * answers with. A provider the device is not bound to is refused by
27527
+ * name rather than answered by another one (D552's
27528
+ * `rejectUnresolvedAddonPin` rule).
27529
+ *
27530
+ * It replaced `sources?: string[]`, a VIEW filter over source ids that
27531
+ * assumed the answer was a fan-out over everything a camera has. The
27532
+ * operator settled otherwise on 2026-09-20 — *"il get videoclips
27533
+ * prendere un provider opzionale, di default va su camstack"*,
27534
+ * *"l'utilizzatore è uno solo"* — so the list is asked of one provider
27535
+ * at a time and there is nothing to filter out of it.
27536
+ */
27537
+ provider: string().optional()
27538
+ }), array(ClipSchema).readonly(), {
27539
+ kind: "query",
27540
+ auth: "protected"
27541
+ }),
27542
+ /**
27543
+ * The sources this camera has, WITH the reason any of them cannot answer.
27544
+ *
27545
+ * Asked separately from `listClips` because an empty clip list is
27546
+ * ambiguous and this is the only place the ambiguity is resolved: every
27547
+ * bound provider contributes its own rows, and a provider that could not
27548
+ * be reached at all still produces one row saying so. A surface that draws
27549
+ * "no clips" without reading this is drawing a guess.
27550
+ */
27551
+ listSources: method(object({ deviceId: number() }), array(ClipSourceSchema).readonly(), {
27552
+ kind: "query",
27553
+ auth: "protected"
27554
+ }),
27555
+ getClipPlayback: method(object({
27556
+ deviceId: number(),
27557
+ clipId: string(),
27558
+ /**
27559
+ * Which twin to serve, on the ONE quality scale the system already has
27560
+ * (`CamProfileSchema`). `low | mid` → the sub file, `high` → the main
27561
+ * twin; both ids are already on the row so this never re-searches the
27562
+ * camera. Absent means the provider's own default (the sub file, which
27563
+ * every source is measured to hold).
27564
+ *
27565
+ * `auto` is deliberately NOT accepted here: a stored file has no
27566
+ * broker session, so the adaptive tier cannot be resolved for it. The
27567
+ * viewer resolves `auto` to a profile the same way live does, before
27568
+ * it calls (D549 15).
27569
+ */
27570
+ profile: CamProfileSchema.optional()
27571
+ }), ClipPlaybackSchema, {
27572
+ kind: "query",
27573
+ auth: "protected"
27574
+ })
27575
+ }
27576
+ };
27256
27577
  /**
27257
27578
  * Optional client-side hints sent at session creation to help the provider
27258
27579
  * pick the best native source. All fields optional — a viewer that knows
@@ -39233,6 +39554,12 @@ Object.freeze({
39233
39554
  addonId: null,
39234
39555
  access: "view"
39235
39556
  },
39557
+ "pipelineAnalytics.ownersWithMedia": {
39558
+ capName: "pipeline-analytics",
39559
+ capScope: "device",
39560
+ addonId: null,
39561
+ access: "view"
39562
+ },
39236
39563
  "pipelineAnalytics.pauseForStorageMigration": {
39237
39564
  capName: "pipeline-analytics",
39238
39565
  capScope: "device",
@@ -41825,6 +42152,12 @@ Object.freeze({
41825
42152
  addonId: null,
41826
42153
  access: "view"
41827
42154
  },
42155
+ "videoclips.listSources": {
42156
+ capName: "videoclips",
42157
+ capScope: "device",
42158
+ addonId: null,
42159
+ access: "view"
42160
+ },
41828
42161
  "viewerUi.getStaticDir": {
41829
42162
  capName: "viewer-ui",
41830
42163
  capScope: "system",
@@ -43127,6 +43460,11 @@ Object.freeze({
43127
43460
  form: "single",
43128
43461
  optional: false
43129
43462
  }],
43463
+ "pipelineAnalytics.ownersWithMedia": [{
43464
+ name: "deviceId",
43465
+ form: "single",
43466
+ optional: false
43467
+ }],
43130
43468
  "pipelineAnalytics.proposeRetrainAnnotations": [{
43131
43469
  name: "deviceId",
43132
43470
  form: "single",
@@ -43833,6 +44171,11 @@ Object.freeze({
43833
44171
  form: "single",
43834
44172
  optional: false
43835
44173
  }],
44174
+ "videoclips.listSources": [{
44175
+ name: "deviceId",
44176
+ form: "single",
44177
+ optional: false
44178
+ }],
43836
44179
  "waterHeater.setAway": [{
43837
44180
  name: "deviceId",
43838
44181
  form: "single",
@@ -45712,6 +46055,18 @@ Object.defineProperty(exports, "DeviceType", {
45712
46055
  return DeviceType;
45713
46056
  }
45714
46057
  });
46058
+ Object.defineProperty(exports, "EVENT_OWNER_TYPES", {
46059
+ enumerable: true,
46060
+ get: function() {
46061
+ return EVENT_OWNER_TYPES;
46062
+ }
46063
+ });
46064
+ Object.defineProperty(exports, "EVENT_PAD_MS", {
46065
+ enumerable: true,
46066
+ get: function() {
46067
+ return EVENT_PAD_MS;
46068
+ }
46069
+ });
45715
46070
  Object.defineProperty(exports, "EncodeProfileSchema", {
45716
46071
  enumerable: true,
45717
46072
  get: function() {
@@ -45820,6 +46175,12 @@ Object.defineProperty(exports, "RingBuffer", {
45820
46175
  return RingBuffer;
45821
46176
  }
45822
46177
  });
46178
+ Object.defineProperty(exports, "VISIT_MERGE_GAP_MS", {
46179
+ enumerable: true,
46180
+ get: function() {
46181
+ return VISIT_MERGE_GAP_MS;
46182
+ }
46183
+ });
45823
46184
  Object.defineProperty(exports, "YAMNET_TO_MACRO", {
45824
46185
  enumerable: true,
45825
46186
  get: function() {
@@ -46354,6 +46715,12 @@ Object.defineProperty(exports, "union", {
46354
46715
  return union;
46355
46716
  }
46356
46717
  });
46718
+ Object.defineProperty(exports, "videoclipsCapability", {
46719
+ enumerable: true,
46720
+ get: function() {
46721
+ return videoclipsCapability;
46722
+ }
46723
+ });
46357
46724
  Object.defineProperty(exports, "webrtcSessionCapability", {
46358
46725
  enumerable: true,
46359
46726
  get: function() {
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-DygcLvgG.js");
5
+ const require_dist = require("../dist-k6Vu-AhZ.js");
6
6
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
7
7
  let node_fs = require("node:fs");
8
8
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { $t as hydrateSchema, Wt as BaseAddon, X as evaluateZoneRules, ft as motionDetectionCapability, qt as DeviceType } from "../dist-DBHsAlUE.mjs";
1
+ import { $ as evaluateZoneRules, Jt as BaseAddon, Zt as DeviceType, ht as motionDetectionCapability, rn as hydrateSchema } from "../dist-g5MfpiBh.mjs";
2
2
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-DygcLvgG.js");
2
+ const require_dist = require("./dist-k6Vu-AhZ.js");
3
3
  let node_crypto = require("node:crypto");
4
4
  let node_fs = require("node:fs");
5
5
  node_fs = require_chunk.__toESM(node_fs, 1);
@@ -1,4 +1,4 @@
1
- import { Bt as buildFfmpegArgs, Ht as isSoftwareDecode, Lt as errMsg, zt as Fmp4BoxSplitter } from "./dist-DBHsAlUE.mjs";
1
+ import { Kt as isSoftwareDecode, Ut as Fmp4BoxSplitter, Vt as errMsg, Wt as buildFfmpegArgs } from "./dist-g5MfpiBh.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { realpathSync } from "node:fs";
4
4
  import path from "node:path";
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-DygcLvgG.js");
7
- const require_node = require("../node-CACCuE7t.js");
8
- const require_default_detection_model = require("../default-detection-model-kQnvoRyv.js");
6
+ const require_dist = require("../dist-k6Vu-AhZ.js");
7
+ const require_node = require("../node--u_jUObS.js");
8
+ const require_default_detection_model = require("../default-detection-model-CAWhHQo2.js");
9
9
  const require_worker_protocol = require("../worker-protocol-B2MfQLlu.js");
10
10
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
11
11
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
@@ -1,6 +1,6 @@
1
- import { $ as failureContributionCapability, Lt as errMsg, S as NativeLeaseSettingsSchema, U as deriveDetailCropRect, Wt as BaseAddon, Xt as customAction, Yt as createEvent, Zt as defineCustomActions, _ as FailureCounters, _t as pickDetailCropConvention, b as MOTION_CLOSE_AFTER_MS, bn as EventCategory, bt as pipelineRunnerCapability, cn as _enum, fn as lazy, hn as object, it as loadContributionCapability, ln as array, m as DEVICE_BACKEND_TO_FORMAT, mn as number, p as DEFAULT_NATIVE_LEASE_SETTINGS, pn as literal, rn as nodePin, u as DEFAULT_DETAIL_CROP_CONVENTION, un as boolean, vn as string, vt as pickNativeLeaseOverride, x as NativeLeaseAdmissionSchema } from "../dist-DBHsAlUE.mjs";
2
- import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-j_3VvNQJ.mjs";
3
- import { a as localFrameRegistry, d as getStepDefinition, i as rootObjectGate, o as resolveFrameViewGeometry, r as macroGateVerdict } from "../default-detection-model-DqrqHCuw.mjs";
1
+ import { $t as createEvent, C as NativeLeaseAdmissionSchema, Ct as pipelineRunnerCapability, Jt as BaseAddon, K as deriveDetailCropRect, S as MOTION_CLOSE_AFTER_MS, Sn as string, Vt as errMsg, _n as literal, bt as pickDetailCropConvention, en as customAction, fn as _enum, gn as lazy, m as DEVICE_BACKEND_TO_FORMAT, mn as boolean, nt as failureContributionCapability, p as DEFAULT_NATIVE_LEASE_SETTINGS, pn as array, sn as nodePin, st as loadContributionCapability, tn as defineCustomActions, u as DEFAULT_DETAIL_CROP_CONVENTION, vn as number, w as NativeLeaseSettingsSchema, wn as EventCategory, xt as pickNativeLeaseOverride, y as FailureCounters, yn as object } from "../dist-g5MfpiBh.mjs";
2
+ import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-BXsHQH5h.mjs";
3
+ import { a as localFrameRegistry, d as getStepDefinition, i as rootObjectGate, o as resolveFrameViewGeometry, r as macroGateVerdict } from "../default-detection-model-BXwE-Dvo.mjs";
4
4
  import { t as isWorkerReply } from "../worker-protocol-C_W-P_g-.mjs";
5
5
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
6
6
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-DygcLvgG.js");
2
+ const require_dist = require("./dist-k6Vu-AhZ.js");
3
3
  let node_fs = require("node:fs");
4
4
  node_fs = require_chunk.__toESM(node_fs);
5
5
  let node_child_process = require("node:child_process");
@@ -1,4 +1,4 @@
1
- import { mt as parseProcStatus } from "./dist-DBHsAlUE.mjs";
1
+ import { _t as parseProcStatus } from "./dist-g5MfpiBh.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import { execFile } from "node:child_process";
4
4
  //#region src/shared/node-topology-platform.ts