@camstack/types 1.2.233 → 1.2.235

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/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_sleep = require("./sleep-D7DPSeiv.js");
2
+ const require_sleep = require("./sleep-CsgPITp-.js");
3
3
  const require_event_category = require("./event-category-BVfsrBYA.js");
4
4
  const require_canonical_hash = require("./canonical-hash-CSE4ioRi.js");
5
5
  const require_enums = require("./enums.js");
@@ -9762,6 +9762,29 @@ var cameraStreamsCapability = {
9762
9762
  };
9763
9763
  //#endregion
9764
9764
  //#region src/capabilities/cap-router-predicates.ts
9765
+ /**
9766
+ * True when a cap's generated router carries the COLLECTION `addonId`
9767
+ * selector — "which provider of this collection answers".
9768
+ *
9769
+ * `resolveCapMount` is the authority, not `mode`: since D554 a device-scoped
9770
+ * wrapper that is a collection (`videoclips`) keeps the PER-DEVICE mount, and
9771
+ * its sources are resolved from the device, never picked by an addonId the
9772
+ * caller supplies. Emitting the selector for it would advertise a routing key
9773
+ * the runtime builder does not read — the exact type/runtime disagreement
9774
+ * D552 removed from `generate-cap-mounts.ts`.
9775
+ *
9776
+ * The one exception is preserved deliberately, and it is the same one
9777
+ * `generate-cap-mounts.ts` documents: a cap declaring `mount: { kind: 'skip' }`
9778
+ * keeps its mode-derived shape. The builder never mounts it, so the emitted
9779
+ * value is never called — four inert caps (`log-channels`, `login-method`,
9780
+ * `load-contribution`, `failure-contribution`) that would otherwise change
9781
+ * shape inside a slice that is about something else.
9782
+ */
9783
+ function emitsCollectionSelector(def) {
9784
+ const kind = require_sleep.resolveCapMount(def).kind;
9785
+ if (kind === "collection") return true;
9786
+ return kind === "skip" && def.mode === "collection";
9787
+ }
9765
9788
  /** `z.void()` — input carries no data. */
9766
9789
  function isVoidInput(schema) {
9767
9790
  const def = schema._def;
@@ -9871,6 +9894,33 @@ function objectInputDeclaresAddonId(schema) {
9871
9894
  const shape = direct !== void 0 && direct !== null ? direct : resolveDefShape(schema._def?.shape);
9872
9895
  return shape !== void 0 && shape !== null && typeof shape === "object" && Object.prototype.hasOwnProperty.call(shape, "addonId");
9873
9896
  }
9897
+ /**
9898
+ * True when an OBJECT input schema declares a top-level, REQUIRED, numeric
9899
+ * `deviceId` — i.e. the method names exactly one device on every call.
9900
+ *
9901
+ * This is the question the per-device dispatcher asks at runtime
9902
+ * (`requireDeviceScoped`: `typeof input.deviceId !== 'number'` → BAD_REQUEST),
9903
+ * asked once at build time instead. A `device-scoped` cap's method that cannot
9904
+ * answer it has no device to resolve for and therefore resolves through the
9905
+ * cap's SYSTEM provider (`cap-router-builder.ts`) — the same destination
9906
+ * `systemOnly` names explicitly. Without this, flipping the mount would 400
9907
+ * every batch (`deviceIds`), fleet-wide (`z.void()`) and job-keyed method on
9908
+ * the eight device-scoped wrapper caps.
9909
+ *
9910
+ * Structural, never a name heuristic over source text: the field must resolve
9911
+ * to `z.number()` and must NOT be wrapped in `optional` / `nullable` /
9912
+ * `default` — an absent value is exactly the input the dispatcher rejects.
9913
+ */
9914
+ function objectInputDeclaresRequiredDeviceId(schema) {
9915
+ if (schema === null || typeof schema !== "object" && typeof schema !== "function") return false;
9916
+ const direct = schema.shape;
9917
+ const shape = direct !== void 0 && direct !== null ? direct : resolveDefShape(schema._def?.shape);
9918
+ if (shape === void 0 || shape === null || typeof shape !== "object") return false;
9919
+ const field = shape["deviceId"];
9920
+ if (field === void 0 || field === null) return false;
9921
+ const def = field._def;
9922
+ return field.constructor?.name === "ZodNumber" || def?.type === "number" || def?.typeName === "ZodNumber";
9923
+ }
9874
9924
  /** `_def.shape` may be a thunk (Zod defers object-shape evaluation) or a plain record. */
9875
9925
  function resolveDefShape(defShape) {
9876
9926
  return typeof defShape === "function" ? defShape() : defShape;
@@ -12889,14 +12939,30 @@ var deviceManagerCapability = {
12889
12939
  */
12890
12940
  getAllBindings: require_sleep.method(zod.z.object({}), zod.z.array(DeviceBindingsForDeviceSchema)),
12891
12941
  /**
12892
- * Activate (or deactivate) a wrapper addon for a (device, cap) pair.
12893
- * Persists the binding via ctx.settings. active=false clears the wrapper
12894
- * (falls back to native if registered).
12942
+ * Activate (or deactivate) a wrapper addon — or ONE of its sources — for a
12943
+ * (device, cap) pair.
12944
+ *
12945
+ * Persists the binding via ctx.settings; `active: false` on a SINGLETON cap
12946
+ * clears the wrapper (falling back to native if one is registered). On a
12947
+ * COLLECTION cap it adds to the per-device DENY set instead, and the
12948
+ * wrapper binding is left exactly as it was — a cap with several sources
12949
+ * has no "which one" for `getBindings` to answer (D556).
12950
+ *
12951
+ * `sourceId` is what makes the switch match its label. One addon can serve
12952
+ * several of a device's sources (`addon-provider-reolink` answers a hub
12953
+ * child with `native:reolink:onboard` AND `native:reolink:hub`), so a
12954
+ * toggle keyed by addon would turn off both while saying it turned off one
12955
+ * — the D62 defect with the polarity reversed (D557). Naming the source
12956
+ * denies exactly that source. OMITTING it keeps the cap-wide meaning
12957
+ * D556 shipped: every source this addon serves for this device, which is
12958
+ * also how a deny entry persisted before the re-key is still read.
12895
12959
  */
12896
12960
  setWrapperActive: require_sleep.method(zod.z.object({
12897
12961
  deviceId: zod.z.number(),
12898
12962
  capName: zod.z.string(),
12899
12963
  wrapperAddonId: zod.z.string(),
12964
+ /** The `ClipSource.source` id to toggle. Absent = all of the addon's. */
12965
+ sourceId: zod.z.string().optional(),
12900
12966
  active: zod.z.boolean()
12901
12967
  }), zod.z.void(), {
12902
12968
  kind: "mutation",
@@ -27053,13 +27119,29 @@ var vectorStoreCapability = {
27053
27119
  /**
27054
27120
  * `videoclips` — the unified, navigable-clip surface for a camera.
27055
27121
  *
27056
- * A device-scoped WRAPPER cap (like `pipeline-analytics`): exactly one active
27057
- * provider per device, substitutable. The DEFAULT provider (registered by
27122
+ * A device-scoped wrapper COLLECTION (D549 decision 2, D554): every provider a
27123
+ * camera has is a SOURCE, and they are listed beside each other — never one
27124
+ * substituted for another. The DEFAULT provider (registered by
27058
27125
  * `addon-post-analysis`, `defaultActive: true`) composes the analytics event
27059
27126
  * log (markers + thumbnails) with the recorder's `getPlaybackManifest` — a clip
27060
27127
  * is a time-WINDOW over existing footage, never a separate file. A camera that
27061
- * exposes NATIVE onboard clips (Reolink/Hikvision NVR) can later substitute the
27062
- * wrapper provider for its device and serve its own clip catalog + URLs.
27128
+ * exposes NATIVE onboard clips (Reolink SD card, a hub's storage, HKSV) adds
27129
+ * its own catalog ALONGSIDE that one.
27130
+ *
27131
+ * The mount stays per-device (`resolveCapMount` → `device-scoped`), and a
27132
+ * device's SOURCES are its BINDINGS: `deviceManager.getBindings(deviceId)` is
27133
+ * what says a provider serves this camera, never the set of addons that
27134
+ * happen to have registered the capability somewhere in the cluster. That is
27135
+ * what stopped one addon appearing three times on 592 — as the device's
27136
+ * native provider, as the hub-local registration and as
27137
+ * `pipeline-analytics@little-unraid` (D554 amended 2026-09-20).
27138
+ *
27139
+ * `listClips` is asked of ONE provider at a time
27140
+ * ({@link videoclipsCapability.methods.listClips} `provider`), defaulting to
27141
+ * the bound one, which is CamStack on every camera today. It is NOT a union
27142
+ * over everything a camera has: *"l'utilizzatore è uno solo"*.
27143
+ * {@link videoclipsCapability.methods.getClipPlayback} dispatches by the
27144
+ * source namespace inside the clip id, which is why ids are self-contained.
27063
27145
  *
27064
27146
  * A `Clip` is purely time-based (subtree-blind): playback resolves segments by
27065
27147
  * temporal overlap, so the API never decides `continuous` vs `events`.
@@ -27147,41 +27229,323 @@ var ClipSchema = zod.z.object({
27147
27229
  holes: zod.z.array(zod.z.object({
27148
27230
  startMs: zod.z.number(),
27149
27231
  endMs: zod.z.number()
27150
- })).optional()
27232
+ })).optional(),
27233
+ /**
27234
+ * A URL that MAY produce this clip's still, asked only for rows actually on
27235
+ * screen. The opposite of {@link ClipSchema.thumbnail}: that one is VOUCHED
27236
+ * (a JPEG is already on disk), this one is an offer. The route answers 200
27237
+ * with the image, or 204 with `x-camstack-reason` when it could not mint one
27238
+ * — a surface latches that refusal to the instant rather than retrying.
27239
+ *
27240
+ * Never both: a clip with a vouched `thumbnail` needs no mint.
27241
+ */
27242
+ thumbnailMint: zod.z.string().optional(),
27243
+ /**
27244
+ * Why no still will be produced for this clip RIGHT NOW — set when the
27245
+ * provider already knows, so the surface draws the glyph and the reason
27246
+ * instead of firing a mint that cannot succeed.
27247
+ *
27248
+ * `sleeping` — a standalone battery camera; a read would be a wake (D549 4).
27249
+ * `camera-refused` — the camera answered the CoverPreview with a refusal.
27250
+ * `no-keyframe` — the window holds no decodable I-frame (an event that sits
27251
+ * inside no file is the measured case).
27252
+ * `unsupported` — this source cannot mint stills at all.
27253
+ */
27254
+ thumbnailUnavailable: zod.z.object({ reason: zod.z.enum([
27255
+ "sleeping",
27256
+ "camera-refused",
27257
+ "no-keyframe",
27258
+ "unsupported"
27259
+ ]) }).optional(),
27260
+ /**
27261
+ * When the catalog this row came from was last CONFIRMED against the device.
27262
+ * Absent means "this row was read live". A persisted catalog served while a
27263
+ * camera sleeps carries the age it really has — a cached list is never drawn
27264
+ * as current (D549 13).
27265
+ */
27266
+ catalogAsOf: zod.z.number().optional(),
27267
+ /**
27268
+ * The camera's OWN type strings for this clip, all of them, unmapped
27269
+ * (`md`, `people`, `dog_cat`, `sched`, …). Kept beside {@link labels}
27270
+ * because a firmware inventing a type must not vanish: the mapping into our
27271
+ * filter vocabulary is lossy on purpose and this is the lossless copy
27272
+ * (D549 20).
27273
+ */
27274
+ nativeTypes: zod.z.array(zod.z.string()).optional(),
27275
+ /**
27276
+ * What the subject DID — `crossline`, `intrude`, `loitering`. A behaviour
27277
+ * travels BESIDE a class, never instead of one, and the class filter ignores
27278
+ * it: "a person crossed a line" is still a person (D549 20).
27279
+ */
27280
+ behaviours: zod.z.array(zod.z.string()).optional(),
27281
+ /**
27282
+ * The same recording as two files — the sub twin (what the row and its
27283
+ * thumbnail are) and its main twin, matched at listing time so a quality
27284
+ * change never re-searches the camera (D549 15). `getClipPlayback`'s
27285
+ * `profile` picks between them: `low | mid` → sub, `high` → main.
27286
+ */
27287
+ streams: zod.z.object({
27288
+ sub: zod.z.object({
27289
+ id: zod.z.string(),
27290
+ bytes: zod.z.number().optional()
27291
+ }).optional(),
27292
+ main: zod.z.object({
27293
+ id: zod.z.string(),
27294
+ bytes: zod.z.number().optional()
27295
+ }).optional()
27296
+ }).optional(),
27297
+ /** The camera says it holds a sub-stream copy of this recording. */
27298
+ supportSub: zod.z.boolean().optional(),
27299
+ /**
27300
+ * Whether this row has bytes behind it. ABSENT means yes — every clip that
27301
+ * IS a file is playable, and only a source that lists EVENTS can produce a
27302
+ * row with nothing to play (the measured case: 1 of 29 hub events on 3628
27303
+ * fell inside no file). Such a row is shown, never dropped and never offered
27304
+ * as playable-then-failing (D549 19).
27305
+ */
27306
+ playable: zod.z.boolean().optional(),
27307
+ /** Why {@link playable} is false, verbatim (`no-file-for-window`). */
27308
+ unplayableReason: zod.z.string().optional()
27151
27309
  });
27152
27310
  var ClipPlaybackSchema = zod.z.object({
27153
- /** HLS master URL through the hub data-plane (Range + token in path). */
27311
+ /**
27312
+ * A media URL through the hub data-plane. {@link ClipPlaybackSchema.format}
27313
+ * says what kind — an HLS master playlist for a recording-derived clip, a
27314
+ * progressive MP4 for a native file the vendor addon muxed and serves with
27315
+ * `Range`. The URL is NOT a credential: both planes are registered
27316
+ * `access: 'authenticated'` and the hub's `/addon/<id>/<prefix>` proxy takes
27317
+ * the session cookie, so nothing is appended to it (D549 22).
27318
+ */
27154
27319
  playbackUrl: zod.z.string(),
27320
+ /**
27321
+ * How to play {@link playbackUrl}. Absent means `hls` — the shape every
27322
+ * existing consumer already assumes. A player that branches on this is the
27323
+ * one change a native clip needs; a player that ignores it will hand an MP4
27324
+ * to hls.js and fail parsing it as a manifest.
27325
+ */
27326
+ format: zod.z.enum(["hls", "mp4"]).optional(),
27327
+ /**
27328
+ * Which twin was actually served. A clip that holds only one stream answers
27329
+ * with the one it has, and the surface SAYS so — a missing main twin is
27330
+ * never served silently as if it were the asked-for quality (D549 15).
27331
+ */
27332
+ served: require_sleep.CamProfileSchema.optional(),
27155
27333
  /** Optional LAN/remote alternates for the same clip. */
27156
27334
  playbackEndpoints: zod.z.array(zod.z.string()).optional(),
27157
27335
  token: zod.z.string().optional()
27158
27336
  });
27337
+ /**
27338
+ * Why a source cannot answer right now — per SOURCE, never fleet-wide.
27339
+ *
27340
+ * `ok` is the only state whose clip list may be read as complete. The other
27341
+ * three exist because an empty list from a sleeping camera reads as "this
27342
+ * camera has no recordings", which is the defect this whole line of work is
27343
+ * about: a battery camera nobody woke (`sleeping`), a camera that could not be
27344
+ * reached at all (`unreachable`, also what a provider that THREW reports), a
27345
+ * camera whose SD card is not mounted (`no-storage`, measured on 640 —
27346
+ * `HddInfo mount 0`, honestly nothing to list rather than "no clips"), and a
27347
+ * camera whose OWN index disagrees with its OWN calendar (`index-empty`,
27348
+ * measured on 618: the calendar marks 3–20 September, days 6–20 list zero
27349
+ * files on both streams and both filters, with 177 GB free). That last one is
27350
+ * a defect ON THE CAMERA, and the only honest thing a provider can do is say
27351
+ * which days it asked for and got nothing — "no clips" would be a lie about a
27352
+ * card that is full of them.
27353
+ */
27354
+ var ClipSourceAvailabilitySchema = zod.z.object({
27355
+ state: zod.z.enum([
27356
+ "ok",
27357
+ "sleeping",
27358
+ "unreachable",
27359
+ "no-storage",
27360
+ "index-empty"
27361
+ ]),
27362
+ /** Free text, shown verbatim. Names the camera's own refusal when there is one. */
27363
+ reason: zod.z.string().optional(),
27364
+ /** When this source's catalog was last CONFIRMED. A cached list is never
27365
+ * drawn as current: the surface shows the age whenever it is older than the
27366
+ * refresh interval. */
27367
+ catalogAsOf: zod.z.number().optional()
27368
+ });
27369
+ /**
27370
+ * One SOURCE of clips for a camera — a row of the picker, and the namespace
27371
+ * every clip id from it is prefixed with (`analytics`,
27372
+ * `native:reolink:onboard`, `hksv`, …).
27373
+ *
27374
+ * A provider lists the sources IT serves for that device, and answers for each
27375
+ * of them whether it can answer at all. A provider with nothing to offer on a
27376
+ * camera returns `[]` — it is not that camera's business.
27377
+ */
27378
+ var ClipSourceSchema = zod.z.object({
27379
+ /** The value this source stamps on {@link ClipSchema.source}, and the prefix
27380
+ * of every clip id it mints. `getClipPlayback` routes on it. */
27381
+ source: zod.z.string(),
27382
+ /** Operator-facing name of the source ("CamStack recordings", "SD card"). */
27383
+ label: zod.z.string(),
27384
+ /**
27385
+ * The addon that SERVES this row.
27386
+ *
27387
+ * A surface cannot otherwise resolve a source to who answers it, and the
27388
+ * alternative — a `native:reolink:* → provider-reolink` table inside the
27389
+ * widget — is a second authority on provider identity living in the one
27390
+ * package with no business knowing it, wrong the day a third source appears
27391
+ * (D557). One addon may serve SEVERAL sources: `addon-provider-reolink`
27392
+ * answers a hub child with both `native:reolink:onboard` and
27393
+ * `native:reolink:hub`, which is why the per-device switch is keyed by
27394
+ * SOURCE and not by this (D555).
27395
+ *
27396
+ * Optional for version skew only. The collection dispatcher stamps it from
27397
+ * the registry, so a row that travelled through the fan-out carries the
27398
+ * authoritative id whatever the provider filled in.
27399
+ */
27400
+ addonId: zod.z.string().optional(),
27401
+ /**
27402
+ * Which API this source resolved to FOR THIS CAMERA, when it has a choice.
27403
+ *
27404
+ * A source may cover one store through more than one surface — the Reolink
27405
+ * provider reads a hub child through the parent's event log and a standalone
27406
+ * through its own file list, because that is what each camera answers. The
27407
+ * CHOICE is the provider's, made from what the camera is, and is never a row
27408
+ * the operator has to understand; but it is REPORTED, because a source that
27409
+ * silently reads a different API on two cameras and then behaves differently
27410
+ * is the thing nobody can debug later. Absent when the source has only one
27411
+ * way to read its store.
27412
+ */
27413
+ via: zod.z.string().optional(),
27414
+ availability: ClipSourceAvailabilitySchema,
27415
+ /**
27416
+ * The operator switched this source OFF for this camera.
27417
+ *
27418
+ * Deliberately NOT a member of {@link ClipSourceAvailabilitySchema}'s
27419
+ * vocabulary. That enum models what the source CAN do — a sleeping camera, an
27420
+ * unmounted card, an index that disagrees with its own calendar — and a
27421
+ * switched-off source could answer perfectly well; the operator decided it
27422
+ * should not. Folding the choice in is how `disabled` and `broken` stop being
27423
+ * distinguishable, which is the D62 rule this repo has already paid for twice:
27424
+ * an off switch is REPORTED off (`CameraStatus.switchedOff`, the same word),
27425
+ * and disabled must never look like broken. It is also what lets every
27426
+ * exhaustive consumer of the availability enum keep compiling.
27427
+ *
27428
+ * A switched-off source contributes NO clips (`listClips` never calls it) and
27429
+ * its row carries no `catalogAsOf`: nothing confirms a catalog it is not
27430
+ * allowed to serve, and a frozen age that can only grow draws a stalling
27431
+ * source rather than an off switch.
27432
+ *
27433
+ * The row survives BECAUSE it is the control the operator switches the source
27434
+ * back on from — D554 decision 4's rule ("a source that cannot answer
27435
+ * produces a ROW, not an absence") applied to the one case D556 carved out of
27436
+ * it, and D557's own kept property ("a source is never hidden, only its
27437
+ * rows"). Absent means on.
27438
+ *
27439
+ * A provider never sets this — like {@link ClipSourceSchema.addonId} it is
27440
+ * stamped by the collection dispatcher, which holds the registry's projection
27441
+ * of the persisted authority (D556) and is the only place that knows it.
27442
+ */
27443
+ switchedOff: zod.z.boolean().optional()
27444
+ });
27159
27445
  var videoclipsCapability = {
27160
27446
  name: "videoclips",
27161
27447
  scope: "device",
27162
- mode: "singleton",
27448
+ mode: "collection",
27163
27449
  kind: "wrapper",
27164
27450
  defaultActive: true,
27165
27451
  /** A clip is a window over a camera's footage — the cap is meaningless on a
27166
27452
  * sensor, a button or an event emitter, and the `defaultActive` auto-bind
27167
27453
  * reads this to decide which devices it may claim. */
27168
27454
  deviceTypes: [require_sleep.DeviceType.Camera],
27455
+ /**
27456
+ * The Clips section of a camera's device details is FRAMEWORK-DERIVED (D14):
27457
+ * the aggregator turns this declaration into the `type:'widget'` section and
27458
+ * `DeviceDetail.tsx` is never edited. `videoclips` is the first WRAPPER cap
27459
+ * to declare one — every previous `host/` widget cap is `deviceNative` — so
27460
+ * `device-config-widget-wrapped-binding.spec.ts` pins that a `kind:'wrapped'`
27461
+ * binding entry derives the same section a native one does.
27462
+ *
27463
+ * **It is a SECTION of the Recording tab, at the end of it — not a tab of
27464
+ * its own.** It shipped as a `clips` top-tab and the operator rejected the
27465
+ * placement: *"utilizzerei la stessa tab recordings, lì abbiamo già tutto il
27466
+ * necessario, una nuova sezione alla fine per le clips"*. The Recording tab
27467
+ * already holds the recorder's panel, the schedule bands and the unified
27468
+ * retention policy; footage the camera itself holds is the same question,
27469
+ * asked of a different store. `order: 100` puts it after all of them with
27470
+ * room left in front. The `clips` entry in `WELL_KNOWN_TABS` went with it —
27471
+ * a well-known id nobody declares is an invitation to mint the tab again.
27472
+ *
27473
+ * `topTab` stays: the browser owns a pane (a day's tiles, a source list and
27474
+ * a player) and the Config tab's inner bar has no room for one. The widget
27475
+ * itself is `host/clips-browser` in ui-library's `HOST_WIDGETS`, and because
27476
+ * the admin Recordings page renders every `location:'top-tab'` +
27477
+ * `tab:'recording'` section behind its camera picker
27478
+ * (`CameraRecordingSettingsSection`), this declaration lands the section on
27479
+ * BOTH surfaces with no second wiring.
27480
+ */
27481
+ deviceConfig: { ui: {
27482
+ kind: "widget",
27483
+ widgetId: "host/clips-browser",
27484
+ tab: "recording",
27485
+ topTab: true,
27486
+ label: "Clips",
27487
+ order: 100
27488
+ } },
27169
27489
  methods: {
27170
27490
  listClips: require_sleep.method(zod.z.object({
27171
27491
  deviceId: zod.z.number(),
27172
27492
  since: zod.z.number(),
27173
27493
  until: zod.z.number(),
27174
- limit: zod.z.number().int().positive().optional()
27494
+ limit: zod.z.number().int().positive().optional(),
27495
+ /**
27496
+ * WHICH provider to ask — the `addonId` a {@link ClipSourceSchema} row
27497
+ * carries, never a source id and never a list.
27498
+ *
27499
+ * Absent means the device's BOUND provider, which is CamStack on every
27500
+ * camera today because that is what `deviceManager.getBindings`
27501
+ * answers with. A provider the device is not bound to is refused by
27502
+ * name rather than answered by another one (D552's
27503
+ * `rejectUnresolvedAddonPin` rule).
27504
+ *
27505
+ * It replaced `sources?: string[]`, a VIEW filter over source ids that
27506
+ * assumed the answer was a fan-out over everything a camera has. The
27507
+ * operator settled otherwise on 2026-09-20 — *"il get videoclips
27508
+ * prendere un provider opzionale, di default va su camstack"*,
27509
+ * *"l'utilizzatore è uno solo"* — so the list is asked of one provider
27510
+ * at a time and there is nothing to filter out of it.
27511
+ */
27512
+ provider: zod.z.string().optional()
27175
27513
  }), zod.z.array(ClipSchema).readonly(), {
27176
27514
  kind: "query",
27177
- auth: "admin"
27515
+ auth: "protected"
27516
+ }),
27517
+ /**
27518
+ * The sources this camera has, WITH the reason any of them cannot answer.
27519
+ *
27520
+ * Asked separately from `listClips` because an empty clip list is
27521
+ * ambiguous and this is the only place the ambiguity is resolved: every
27522
+ * bound provider contributes its own rows, and a provider that could not
27523
+ * be reached at all still produces one row saying so. A surface that draws
27524
+ * "no clips" without reading this is drawing a guess.
27525
+ */
27526
+ listSources: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), zod.z.array(ClipSourceSchema).readonly(), {
27527
+ kind: "query",
27528
+ auth: "protected"
27178
27529
  }),
27179
27530
  getClipPlayback: require_sleep.method(zod.z.object({
27180
27531
  deviceId: zod.z.number(),
27181
- clipId: zod.z.string()
27532
+ clipId: zod.z.string(),
27533
+ /**
27534
+ * Which twin to serve, on the ONE quality scale the system already has
27535
+ * (`CamProfileSchema`). `low | mid` → the sub file, `high` → the main
27536
+ * twin; both ids are already on the row so this never re-searches the
27537
+ * camera. Absent means the provider's own default (the sub file, which
27538
+ * every source is measured to hold).
27539
+ *
27540
+ * `auto` is deliberately NOT accepted here: a stored file has no
27541
+ * broker session, so the adaptive tier cannot be resolved for it. The
27542
+ * viewer resolves `auto` to a profile the same way live does, before
27543
+ * it calls (D549 15).
27544
+ */
27545
+ profile: require_sleep.CamProfileSchema.optional()
27182
27546
  }), ClipPlaybackSchema, {
27183
27547
  kind: "query",
27184
- auth: "admin"
27548
+ auth: "protected"
27185
27549
  })
27186
27550
  }
27187
27551
  };
@@ -50660,6 +51024,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
50660
51024
  addonId: null,
50661
51025
  access: "view"
50662
51026
  },
51027
+ "videoclips.listSources": {
51028
+ capName: "videoclips",
51029
+ capScope: "device",
51030
+ addonId: null,
51031
+ access: "view"
51032
+ },
50663
51033
  "viewerUi.getStaticDir": {
50664
51034
  capName: "viewer-ui",
50665
51035
  capScope: "system",
@@ -52941,6 +53311,11 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
52941
53311
  form: "single",
52942
53312
  optional: false
52943
53313
  }],
53314
+ "videoclips.listSources": [{
53315
+ name: "deviceId",
53316
+ form: "single",
53317
+ optional: false
53318
+ }],
52944
53319
  "waterHeater.setAway": [{
52945
53320
  name: "deviceId",
52946
53321
  form: "single",
@@ -58604,6 +58979,8 @@ exports.ClientNetworkStatsSchema = ClientNetworkStatsSchema;
58604
58979
  exports.ClimateControlStatusSchema = ClimateControlStatusSchema;
58605
58980
  exports.ClipPlaybackSchema = ClipPlaybackSchema;
58606
58981
  exports.ClipSchema = ClipSchema;
58982
+ exports.ClipSourceAvailabilitySchema = ClipSourceAvailabilitySchema;
58983
+ exports.ClipSourceSchema = ClipSourceSchema;
58607
58984
  exports.ClusterAddonNodeDeploymentSchema = ClusterAddonNodeDeploymentSchema;
58608
58985
  exports.ClusterAddonStatusEntrySchema = ClusterAddonStatusEntrySchema;
58609
58986
  exports.CollectionColumnSchema = CollectionColumnSchema;
@@ -59695,6 +60072,7 @@ exports.egressTransportFromRequest = egressTransportFromRequest;
59695
60072
  exports.embeddingEncoderCapability = embeddingEncoderCapability;
59696
60073
  exports.emitDownForOwnedCaps = require_sleep.emitDownForOwnedCaps;
59697
60074
  exports.emitReadiness = require_sleep.emitReadiness;
60075
+ exports.emitsCollectionSelector = emitsCollectionSelector;
59698
60076
  exports.encodeProfileFromStreamShape = encodeProfileFromStreamShape;
59699
60077
  exports.encodeVectorBase64 = encodeVectorBase64;
59700
60078
  exports.enumSensorCapability = enumSensorCapability;
@@ -59846,6 +60224,7 @@ exports.notifierCapability = notifierCapability;
59846
60224
  exports.numericSensorCapability = numericSensorCapability;
59847
60225
  exports.oauthIntegrationCapability = oauthIntegrationCapability;
59848
60226
  exports.objectInputDeclaresAddonId = objectInputDeclaresAddonId;
60227
+ exports.objectInputDeclaresRequiredDeviceId = objectInputDeclaresRequiredDeviceId;
59849
60228
  exports.occupancyScope = occupancyScope;
59850
60229
  exports.osdCapability = osdCapability;
59851
60230
  exports.osdManagerCapability = osdManagerCapability;