@camstack/system 1.2.274 → 1.2.276

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 (74) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/autotrack/index.js +1 -1
  8. package/dist/builtins/autotrack/index.mjs +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  11. package/dist/builtins/camera-grid/grid-last-frame-store.d.ts +39 -0
  12. package/dist/builtins/camera-grid/index.js +67 -12
  13. package/dist/builtins/camera-grid/index.mjs +67 -12
  14. package/dist/builtins/console-logging/index.js +1 -1
  15. package/dist/builtins/console-logging/index.mjs +1 -1
  16. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  17. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  18. package/dist/builtins/device-manager/device-aggregation.d.ts +0 -6
  19. package/dist/builtins/device-manager/device-bindings-store.d.ts +12 -2
  20. package/dist/builtins/device-manager/device-manager.addon.d.ts +4 -0
  21. package/dist/builtins/device-manager/device-manager.addon.js +177 -58
  22. package/dist/builtins/device-manager/device-manager.addon.mjs +174 -59
  23. package/dist/builtins/device-manager/device-meta-types.d.ts +36 -3
  24. package/dist/builtins/device-manager/device-source-activation.d.ts +70 -0
  25. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  26. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  27. package/dist/builtins/hub-forwarder/index.js +1 -1
  28. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  29. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  30. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  31. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  32. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  33. package/dist/builtins/local-network/local-network.addon.js +2 -2
  34. package/dist/builtins/local-network/local-network.addon.mjs +2 -2
  35. package/dist/builtins/loki-logging/index.js +1 -1
  36. package/dist/builtins/loki-logging/index.mjs +1 -1
  37. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  38. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  39. package/dist/builtins/platform-probe/index.js +1 -1
  40. package/dist/builtins/platform-probe/index.mjs +1 -1
  41. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  42. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  43. package/dist/builtins/snapshot/index.js +26 -6
  44. package/dist/builtins/snapshot/index.mjs +26 -6
  45. package/dist/builtins/snapshot/snapshot-durable-store.d.ts +4 -0
  46. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  47. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  48. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  49. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  50. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  51. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  52. package/dist/builtins/system-config/system-config.addon.js +1 -1
  53. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  54. package/dist/builtins/winston-logging/index.js +1 -1
  55. package/dist/builtins/winston-logging/index.mjs +1 -1
  56. package/dist/{dist-DchKwh_m.mjs → dist-AHdiIdJH.mjs} +412 -19
  57. package/dist/{dist-C_4Xt0Ya.js → dist-D_OlSf31.js} +417 -18
  58. package/dist/index.d.ts +7 -0
  59. package/dist/index.js +104 -38
  60. package/dist/index.mjs +102 -40
  61. package/dist/kernel/capability-registry.d.ts +59 -9
  62. package/dist/kernel/index.d.ts +1 -0
  63. package/dist/{retired-settings-keys-DPSHUHMu.mjs → retired-settings-keys-Cp3oG_aH.mjs} +1 -1
  64. package/dist/{retired-settings-keys-6wZNp7lr.js → retired-settings-keys-LwFyTKun.js} +1 -1
  65. package/dist/tls/cert-identity.d.ts +22 -0
  66. package/dist/tls/cert-issue.d.ts +31 -0
  67. package/dist/tls/cert-manager.d.ts +8 -2
  68. package/dist/tls/index.d.ts +2 -2
  69. package/dist/tls-D_wUeOTD.js +12122 -0
  70. package/dist/tls-IM-v7JV4.mjs +11984 -0
  71. package/package.json +2 -1
  72. package/dist/tls/cert-openssl.d.ts +0 -23
  73. package/dist/tls-2xbgg78V.mjs +0 -752
  74. package/dist/tls-BFs4PzoW.js +0 -890
@@ -691,7 +691,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
691
691
  return EventCategory;
692
692
  }({});
693
693
  //#endregion
694
- //#region ../types/dist/sleep-CiR4z7r9.mjs
694
+ //#region ../types/dist/sleep-CopaBJss.mjs
695
695
  /**
696
696
  * The audio chunk plane's byte format, and the ONE expansion from a coded
697
697
  * window to float samples (D455).
@@ -892,6 +892,12 @@ var WELL_KNOWN_TAB_MAP = Object.fromEntries([
892
892
  icon: "circle-dot",
893
893
  order: 40
894
894
  },
895
+ {
896
+ id: "clips",
897
+ label: "Clips",
898
+ icon: "clapperboard",
899
+ order: 41
900
+ },
895
901
  {
896
902
  id: "engine",
897
903
  label: "Inference Engine",
@@ -2724,6 +2730,7 @@ function toNodeId(idOrRunnerId) {
2724
2730
  function resolveCapMount(def) {
2725
2731
  if (def.mount) return def.mount;
2726
2732
  if (def.deviceNative === true) return { kind: "device-native" };
2733
+ if (def.scope === "device" && def.kind === "wrapper") return { kind: "device-scoped" };
2727
2734
  if (def.mode === "collection") return { kind: "collection" };
2728
2735
  return { kind: "singleton" };
2729
2736
  }
@@ -10380,6 +10387,33 @@ function objectInputDeclaresAddonId(schema) {
10380
10387
  const shape = direct !== void 0 && direct !== null ? direct : resolveDefShape(schema._def?.shape);
10381
10388
  return shape !== void 0 && shape !== null && typeof shape === "object" && Object.prototype.hasOwnProperty.call(shape, "addonId");
10382
10389
  }
10390
+ /**
10391
+ * True when an OBJECT input schema declares a top-level, REQUIRED, numeric
10392
+ * `deviceId` — i.e. the method names exactly one device on every call.
10393
+ *
10394
+ * This is the question the per-device dispatcher asks at runtime
10395
+ * (`requireDeviceScoped`: `typeof input.deviceId !== 'number'` → BAD_REQUEST),
10396
+ * asked once at build time instead. A `device-scoped` cap's method that cannot
10397
+ * answer it has no device to resolve for and therefore resolves through the
10398
+ * cap's SYSTEM provider (`cap-router-builder.ts`) — the same destination
10399
+ * `systemOnly` names explicitly. Without this, flipping the mount would 400
10400
+ * every batch (`deviceIds`), fleet-wide (`z.void()`) and job-keyed method on
10401
+ * the eight device-scoped wrapper caps.
10402
+ *
10403
+ * Structural, never a name heuristic over source text: the field must resolve
10404
+ * to `z.number()` and must NOT be wrapped in `optional` / `nullable` /
10405
+ * `default` — an absent value is exactly the input the dispatcher rejects.
10406
+ */
10407
+ function objectInputDeclaresRequiredDeviceId(schema) {
10408
+ if (schema === null || typeof schema !== "object" && typeof schema !== "function") return false;
10409
+ const direct = schema.shape;
10410
+ const shape = direct !== void 0 && direct !== null ? direct : resolveDefShape(schema._def?.shape);
10411
+ if (shape === void 0 || shape === null || typeof shape !== "object") return false;
10412
+ const field = shape["deviceId"];
10413
+ if (field === void 0 || field === null) return false;
10414
+ const def = field._def;
10415
+ return field.constructor?.name === "ZodNumber" || def?.type === "number" || def?.typeName === "ZodNumber";
10416
+ }
10383
10417
  /** `_def.shape` may be a thunk (Zod defers object-shape evaluation) or a plain record. */
10384
10418
  function resolveDefShape(defShape) {
10385
10419
  return typeof defShape === "function" ? defShape() : defShape;
@@ -13210,14 +13244,30 @@ var deviceManagerCapability = {
13210
13244
  */
13211
13245
  getAllBindings: method(z.object({}), z.array(DeviceBindingsForDeviceSchema)),
13212
13246
  /**
13213
- * Activate (or deactivate) a wrapper addon for a (device, cap) pair.
13214
- * Persists the binding via ctx.settings. active=false clears the wrapper
13215
- * (falls back to native if registered).
13247
+ * Activate (or deactivate) a wrapper addon — or ONE of its sources — for a
13248
+ * (device, cap) pair.
13249
+ *
13250
+ * Persists the binding via ctx.settings; `active: false` on a SINGLETON cap
13251
+ * clears the wrapper (falling back to native if one is registered). On a
13252
+ * COLLECTION cap it adds to the per-device DENY set instead, and the
13253
+ * wrapper binding is left exactly as it was — a cap with several sources
13254
+ * has no "which one" for `getBindings` to answer (D556).
13255
+ *
13256
+ * `sourceId` is what makes the switch match its label. One addon can serve
13257
+ * several of a device's sources (`addon-provider-reolink` answers a hub
13258
+ * child with `native:reolink:onboard` AND `native:reolink:hub`), so a
13259
+ * toggle keyed by addon would turn off both while saying it turned off one
13260
+ * — the D62 defect with the polarity reversed (D557). Naming the source
13261
+ * denies exactly that source. OMITTING it keeps the cap-wide meaning
13262
+ * D556 shipped: every source this addon serves for this device, which is
13263
+ * also how a deny entry persisted before the re-key is still read.
13216
13264
  */
13217
13265
  setWrapperActive: method(z.object({
13218
13266
  deviceId: z.number(),
13219
13267
  capName: z.string(),
13220
13268
  wrapperAddonId: z.string(),
13269
+ /** The `ClipSource.source` id to toggle. Absent = all of the addon's. */
13270
+ sourceId: z.string().optional(),
13221
13271
  active: z.boolean()
13222
13272
  }), z.void(), {
13223
13273
  kind: "mutation",
@@ -23998,10 +24048,9 @@ var ServerBootModeSchema = z.enum([
23998
24048
  * - `idle` / `checking` / `staging` — steady / in-flight registry work.
23999
24049
  * - `pending-restart` — a version is staged and the node has NOT yet
24000
24050
  * restarted onto it (still running the OLD version).
24001
- * - `awaiting-confirmation` — the node HAS restarted onto the staged version
24002
- * (it is the active probation boot) and is waiting to confirm boot-health.
24003
- * Apply/rollback are refused in this state and the node must NOT be
24004
- * manually restarted, or the probation boot auto-rolls-back.
24051
+ * - `awaiting-confirmation` — RETIRED with the probation boot (single-copy
24052
+ * collapse, 2026-07-18). Kept in the enum so an older client still parses;
24053
+ * no provider emits it.
24005
24054
  */
24006
24055
  var ServerUpdateStateSchema = z.enum([
24007
24056
  "idle",
@@ -24158,12 +24207,35 @@ var ServerRollbackInfoSchema = z.object({
24158
24207
  var ServerPackageStatusSchema = z.object({
24159
24208
  /** Root package name (`@camstack/server` on the hub). */
24160
24209
  packageName: z.string(),
24161
- /** Version of the code the running process ACTUALLY loaded. */
24210
+ /**
24211
+ * Version of the code THIS PROCESS loaded — captured once, at boot, from the
24212
+ * package.json of the copy the code was loaded from, and never re-read. A
24213
+ * copy replaced on disk under a live process must not move this number;
24214
+ * that divergence is what `installedVersion` is for. Before 2026-09-20 this
24215
+ * was a lazy disk read that would have followed such a swap silently.
24216
+ */
24162
24217
  runningVersion: z.string().nullable(),
24163
24218
  /** Node.js runtime version the node's process runs on (`process.versions.node`). */
24164
24219
  nodeRuntimeVersion: z.string().nullable(),
24165
24220
  /** Active data-dir root version; null when booted from seed/workspace. */
24166
24221
  activeVersion: z.string().nullable(),
24222
+ /**
24223
+ * Version of the root package in the data-root `current/` copy RIGHT NOW —
24224
+ * what the next boot would load if nothing is staged. Normally equal to
24225
+ * `runningVersion`; differs only when the copy on disk changed under the
24226
+ * live process. `null` when there is no data-root copy (baked seed or
24227
+ * workspace boot). Optional for version skew: an older provider omits it.
24228
+ */
24229
+ installedVersion: z.string().nullable().optional(),
24230
+ /**
24231
+ * True when the code that would run after a restart is NOT the code running
24232
+ * now: a version is staged (`pendingVersion`), or `installedVersion` differs
24233
+ * from `runningVersion`. Derived on every read, never stored. This is the
24234
+ * one field that lets an operator or a script say "1.2.353 is installed and
24235
+ * will run after a restart" instead of reading it off a number that means
24236
+ * "installed". Optional for version skew.
24237
+ */
24238
+ restartRequired: z.boolean().optional(),
24167
24239
  /** N-1 version kept for rollback; null when no previous version exists. */
24168
24240
  previousVersion: z.string().nullable(),
24169
24241
  /** Version of the immutable baked seed closure (image fallback). */
@@ -24198,19 +24270,71 @@ var ServerPackageStatusSchema = z.object({
24198
24270
  });
24199
24271
  var ServerUpdateCheckResultSchema = z.object({
24200
24272
  packageName: z.string(),
24273
+ /** Same meaning as on the status payload: what THIS process loaded. */
24201
24274
  runningVersion: z.string().nullable(),
24202
24275
  latestVersion: z.string().nullable(),
24276
+ /**
24277
+ * `latestVersion` is newer than `runningVersion` AND is not the version
24278
+ * already staged on this node. A staged-and-waiting version is not "an
24279
+ * update available" — applying it again is refused (`already-staged`), and
24280
+ * a button that announced "updating…" on this boolean then got refused
24281
+ * would be lying. It is reported through `pendingVersion` instead.
24282
+ */
24203
24283
  updateAvailable: z.boolean(),
24204
24284
  checkedAtMs: z.number(),
24205
24285
  /** Non-null when the registry lookup failed (offline, bad registry, …). */
24206
- error: z.string().nullable()
24286
+ error: z.string().nullable(),
24287
+ /**
24288
+ * Version staged on this node and awaiting its restart, when one is. Before
24289
+ * 2026-09-20 a check answered `{runningVersion, updateAvailable:false}` for a
24290
+ * node whose staged closure had never booted, and the caller had no way to
24291
+ * see the difference. Optional for version skew.
24292
+ */
24293
+ pendingVersion: z.string().nullable().optional(),
24294
+ /** See the status payload: staged, or installed-on-disk ≠ running. Optional for skew. */
24295
+ restartRequired: z.boolean().optional()
24207
24296
  });
24297
+ /**
24298
+ * What an action DID — the discriminant `accepted` cannot carry. Before
24299
+ * 2026-09-20 `applyServerUpdate` answered `accepted:false` both for a refusal
24300
+ * where nothing was wrong (already current, already staged) and for a staging
24301
+ * install that died on `npm error code ECONNRESET`; the only difference was
24302
+ * prose in `message`, and an operator restarted a container on the strength of
24303
+ * it, booting the previous version.
24304
+ *
24305
+ * - `staged` — the closure is installed and validated; the node
24306
+ * restarts to apply it (`restarting:true`). NOTHING
24307
+ * runs the new code until that restart completes.
24308
+ * - `restart-scheduled` — a plain, version-preserving restart was accepted.
24309
+ * - `already-current` — the target IS the running version; nothing done.
24310
+ * - `refused` — dropped by a gate; `reason` names which.
24311
+ * - `failed` — attempted and did not land; `error` says why.
24312
+ */
24313
+ var ServerUpdateOutcomeSchema = z.enum([
24314
+ "staged",
24315
+ "restart-scheduled",
24316
+ "already-current",
24317
+ "refused",
24318
+ "failed"
24319
+ ]);
24208
24320
  var ServerUpdateActionResultSchema = z.object({
24209
24321
  accepted: z.boolean(),
24210
24322
  targetVersion: z.string().nullable(),
24211
24323
  /** True when a graceful restart was scheduled to apply the change. */
24212
24324
  restarting: z.boolean(),
24213
- message: z.string()
24325
+ message: z.string(),
24326
+ /** See {@link ServerUpdateOutcomeSchema}. Optional for version skew. */
24327
+ outcome: ServerUpdateOutcomeSchema.optional(),
24328
+ /**
24329
+ * Stable, greppable cause for `refused` / `failed` (the engine's
24330
+ * `UpdateRefusalReason`, `staging-failed`, …). `null` on success.
24331
+ */
24332
+ reason: z.string().nullable().optional(),
24333
+ /**
24334
+ * The failure itself, verbatim enough to act on (the npm output for a
24335
+ * staging install). Non-null only for `failed`.
24336
+ */
24337
+ error: z.string().nullable().optional()
24214
24338
  });
24215
24339
  var serverManagementCapability = {
24216
24340
  name: "server-management",
@@ -25901,41 +26025,299 @@ var ClipSchema = z.object({
25901
26025
  holes: z.array(z.object({
25902
26026
  startMs: z.number(),
25903
26027
  endMs: z.number()
25904
- })).optional()
26028
+ })).optional(),
26029
+ /**
26030
+ * A URL that MAY produce this clip's still, asked only for rows actually on
26031
+ * screen. The opposite of {@link ClipSchema.thumbnail}: that one is VOUCHED
26032
+ * (a JPEG is already on disk), this one is an offer. The route answers 200
26033
+ * with the image, or 204 with `x-camstack-reason` when it could not mint one
26034
+ * — a surface latches that refusal to the instant rather than retrying.
26035
+ *
26036
+ * Never both: a clip with a vouched `thumbnail` needs no mint.
26037
+ */
26038
+ thumbnailMint: z.string().optional(),
26039
+ /**
26040
+ * Why no still will be produced for this clip RIGHT NOW — set when the
26041
+ * provider already knows, so the surface draws the glyph and the reason
26042
+ * instead of firing a mint that cannot succeed.
26043
+ *
26044
+ * `sleeping` — a standalone battery camera; a read would be a wake (D549 4).
26045
+ * `camera-refused` — the camera answered the CoverPreview with a refusal.
26046
+ * `no-keyframe` — the window holds no decodable I-frame (an event that sits
26047
+ * inside no file is the measured case).
26048
+ * `unsupported` — this source cannot mint stills at all.
26049
+ */
26050
+ thumbnailUnavailable: z.object({ reason: z.enum([
26051
+ "sleeping",
26052
+ "camera-refused",
26053
+ "no-keyframe",
26054
+ "unsupported"
26055
+ ]) }).optional(),
26056
+ /**
26057
+ * When the catalog this row came from was last CONFIRMED against the device.
26058
+ * Absent means "this row was read live". A persisted catalog served while a
26059
+ * camera sleeps carries the age it really has — a cached list is never drawn
26060
+ * as current (D549 13).
26061
+ */
26062
+ catalogAsOf: z.number().optional(),
26063
+ /**
26064
+ * The camera's OWN type strings for this clip, all of them, unmapped
26065
+ * (`md`, `people`, `dog_cat`, `sched`, …). Kept beside {@link labels}
26066
+ * because a firmware inventing a type must not vanish: the mapping into our
26067
+ * filter vocabulary is lossy on purpose and this is the lossless copy
26068
+ * (D549 20).
26069
+ */
26070
+ nativeTypes: z.array(z.string()).optional(),
26071
+ /**
26072
+ * What the subject DID — `crossline`, `intrude`, `loitering`. A behaviour
26073
+ * travels BESIDE a class, never instead of one, and the class filter ignores
26074
+ * it: "a person crossed a line" is still a person (D549 20).
26075
+ */
26076
+ behaviours: z.array(z.string()).optional(),
26077
+ /**
26078
+ * The same recording as two files — the sub twin (what the row and its
26079
+ * thumbnail are) and its main twin, matched at listing time so a quality
26080
+ * change never re-searches the camera (D549 15). `getClipPlayback`'s
26081
+ * `profile` picks between them: `low | mid` → sub, `high` → main.
26082
+ */
26083
+ streams: z.object({
26084
+ sub: z.object({
26085
+ id: z.string(),
26086
+ bytes: z.number().optional()
26087
+ }).optional(),
26088
+ main: z.object({
26089
+ id: z.string(),
26090
+ bytes: z.number().optional()
26091
+ }).optional()
26092
+ }).optional(),
26093
+ /** The camera says it holds a sub-stream copy of this recording. */
26094
+ supportSub: z.boolean().optional(),
26095
+ /**
26096
+ * Whether this row has bytes behind it. ABSENT means yes — every clip that
26097
+ * IS a file is playable, and only a source that lists EVENTS can produce a
26098
+ * row with nothing to play (the measured case: 1 of 29 hub events on 3628
26099
+ * fell inside no file). Such a row is shown, never dropped and never offered
26100
+ * as playable-then-failing (D549 19).
26101
+ */
26102
+ playable: z.boolean().optional(),
26103
+ /** Why {@link playable} is false, verbatim (`no-file-for-window`). */
26104
+ unplayableReason: z.string().optional()
25905
26105
  });
25906
26106
  var ClipPlaybackSchema = z.object({
25907
- /** HLS master URL through the hub data-plane (Range + token in path). */
26107
+ /**
26108
+ * A media URL through the hub data-plane. {@link ClipPlaybackSchema.format}
26109
+ * says what kind — an HLS master playlist for a recording-derived clip, a
26110
+ * progressive MP4 for a native file the vendor addon muxed and serves with
26111
+ * `Range`. The URL is NOT a credential: both planes are registered
26112
+ * `access: 'authenticated'` and the hub's `/addon/<id>/<prefix>` proxy takes
26113
+ * the session cookie, so nothing is appended to it (D549 22).
26114
+ */
25908
26115
  playbackUrl: z.string(),
26116
+ /**
26117
+ * How to play {@link playbackUrl}. Absent means `hls` — the shape every
26118
+ * existing consumer already assumes. A player that branches on this is the
26119
+ * one change a native clip needs; a player that ignores it will hand an MP4
26120
+ * to hls.js and fail parsing it as a manifest.
26121
+ */
26122
+ format: z.enum(["hls", "mp4"]).optional(),
26123
+ /**
26124
+ * Which twin was actually served. A clip that holds only one stream answers
26125
+ * with the one it has, and the surface SAYS so — a missing main twin is
26126
+ * never served silently as if it were the asked-for quality (D549 15).
26127
+ */
26128
+ served: CamProfileSchema.optional(),
25909
26129
  /** Optional LAN/remote alternates for the same clip. */
25910
26130
  playbackEndpoints: z.array(z.string()).optional(),
25911
26131
  token: z.string().optional()
25912
26132
  });
26133
+ /**
26134
+ * Why a source cannot answer right now — per SOURCE, never fleet-wide.
26135
+ *
26136
+ * `ok` is the only state whose clip list may be read as complete. The other
26137
+ * three exist because an empty list from a sleeping camera reads as "this
26138
+ * camera has no recordings", which is the defect this whole line of work is
26139
+ * about: a battery camera nobody woke (`sleeping`), a camera that could not be
26140
+ * reached at all (`unreachable`, also what a provider that THREW reports), a
26141
+ * camera whose SD card is not mounted (`no-storage`, measured on 640 —
26142
+ * `HddInfo mount 0`, honestly nothing to list rather than "no clips"), and a
26143
+ * camera whose OWN index disagrees with its OWN calendar (`index-empty`,
26144
+ * measured on 618: the calendar marks 3–20 September, days 6–20 list zero
26145
+ * files on both streams and both filters, with 177 GB free). That last one is
26146
+ * a defect ON THE CAMERA, and the only honest thing a provider can do is say
26147
+ * which days it asked for and got nothing — "no clips" would be a lie about a
26148
+ * card that is full of them.
26149
+ */
26150
+ var ClipSourceAvailabilitySchema = z.object({
26151
+ state: z.enum([
26152
+ "ok",
26153
+ "sleeping",
26154
+ "unreachable",
26155
+ "no-storage",
26156
+ "index-empty"
26157
+ ]),
26158
+ /** Free text, shown verbatim. Names the camera's own refusal when there is one. */
26159
+ reason: z.string().optional(),
26160
+ /** When this source's catalog was last CONFIRMED. A cached list is never
26161
+ * drawn as current: the surface shows the age whenever it is older than the
26162
+ * refresh interval. */
26163
+ catalogAsOf: z.number().optional()
26164
+ });
26165
+ /**
26166
+ * One SOURCE of clips for a camera — a row of the picker, and the namespace
26167
+ * every clip id from it is prefixed with (`analytics`,
26168
+ * `native:reolink:onboard`, `hksv`, …).
26169
+ *
26170
+ * A provider lists the sources IT serves for that device, and answers for each
26171
+ * of them whether it can answer at all. A provider with nothing to offer on a
26172
+ * camera returns `[]` — it is not that camera's business.
26173
+ */
26174
+ var ClipSourceSchema = z.object({
26175
+ /** The value this source stamps on {@link ClipSchema.source}, and the prefix
26176
+ * of every clip id it mints. `getClipPlayback` routes on it. */
26177
+ source: z.string(),
26178
+ /** Operator-facing name of the source ("CamStack recordings", "SD card"). */
26179
+ label: z.string(),
26180
+ /**
26181
+ * The addon that SERVES this row.
26182
+ *
26183
+ * A surface cannot otherwise resolve a source to who answers it, and the
26184
+ * alternative — a `native:reolink:* → provider-reolink` table inside the
26185
+ * widget — is a second authority on provider identity living in the one
26186
+ * package with no business knowing it, wrong the day a third source appears
26187
+ * (D557). One addon may serve SEVERAL sources: `addon-provider-reolink`
26188
+ * answers a hub child with both `native:reolink:onboard` and
26189
+ * `native:reolink:hub`, which is why the per-device switch is keyed by
26190
+ * SOURCE and not by this (D555).
26191
+ *
26192
+ * Optional for version skew only. The collection dispatcher stamps it from
26193
+ * the registry, so a row that travelled through the fan-out carries the
26194
+ * authoritative id whatever the provider filled in.
26195
+ */
26196
+ addonId: z.string().optional(),
26197
+ /**
26198
+ * Which API this source resolved to FOR THIS CAMERA, when it has a choice.
26199
+ *
26200
+ * A source may cover one store through more than one surface — the Reolink
26201
+ * provider reads a hub child through the parent's event log and a standalone
26202
+ * through its own file list, because that is what each camera answers. The
26203
+ * CHOICE is the provider's, made from what the camera is, and is never a row
26204
+ * the operator has to understand; but it is REPORTED, because a source that
26205
+ * silently reads a different API on two cameras and then behaves differently
26206
+ * is the thing nobody can debug later. Absent when the source has only one
26207
+ * way to read its store.
26208
+ */
26209
+ via: z.string().optional(),
26210
+ availability: ClipSourceAvailabilitySchema,
26211
+ /**
26212
+ * The operator switched this source OFF for this camera.
26213
+ *
26214
+ * Deliberately NOT a member of {@link ClipSourceAvailabilitySchema}'s
26215
+ * vocabulary. That enum models what the source CAN do — a sleeping camera, an
26216
+ * unmounted card, an index that disagrees with its own calendar — and a
26217
+ * switched-off source could answer perfectly well; the operator decided it
26218
+ * should not. Folding the choice in is how `disabled` and `broken` stop being
26219
+ * distinguishable, which is the D62 rule this repo has already paid for twice:
26220
+ * an off switch is REPORTED off (`CameraStatus.switchedOff`, the same word),
26221
+ * and disabled must never look like broken. It is also what lets every
26222
+ * exhaustive consumer of the availability enum keep compiling.
26223
+ *
26224
+ * A switched-off source contributes NO clips (`listClips` never calls it) and
26225
+ * its row carries no `catalogAsOf`: nothing confirms a catalog it is not
26226
+ * allowed to serve, and a frozen age that can only grow draws a stalling
26227
+ * source rather than an off switch.
26228
+ *
26229
+ * The row survives BECAUSE it is the control the operator switches the source
26230
+ * back on from — D554 decision 4's rule ("a source that cannot answer
26231
+ * produces a ROW, not an absence") applied to the one case D556 carved out of
26232
+ * it, and D557's own kept property ("a source is never hidden, only its
26233
+ * rows"). Absent means on.
26234
+ *
26235
+ * A provider never sets this — like {@link ClipSourceSchema.addonId} it is
26236
+ * stamped by the collection dispatcher, which holds the registry's projection
26237
+ * of the persisted authority (D556) and is the only place that knows it.
26238
+ */
26239
+ switchedOff: z.boolean().optional()
26240
+ });
25913
26241
  var videoclipsCapability = {
25914
26242
  name: "videoclips",
25915
26243
  scope: "device",
25916
- mode: "singleton",
26244
+ mode: "collection",
25917
26245
  kind: "wrapper",
25918
26246
  defaultActive: true,
25919
26247
  /** A clip is a window over a camera's footage — the cap is meaningless on a
25920
26248
  * sensor, a button or an event emitter, and the `defaultActive` auto-bind
25921
26249
  * reads this to decide which devices it may claim. */
25922
26250
  deviceTypes: [DeviceType.Camera],
26251
+ /**
26252
+ * The Clips section of a camera's device details is FRAMEWORK-DERIVED (D14):
26253
+ * the aggregator turns this declaration into the `type:'widget'` section and
26254
+ * `DeviceDetail.tsx` is never edited. `videoclips` is the first WRAPPER cap
26255
+ * to declare one — every previous `host/` widget cap is `deviceNative` — so
26256
+ * `device-config-widget-wrapped-binding.spec.ts` pins that a `kind:'wrapped'`
26257
+ * binding entry derives the same section a native one does.
26258
+ *
26259
+ * `topTab` because the browser owns the whole pane (a day's clips, a source
26260
+ * list and a player), exactly as `ptz` does; the widget itself is
26261
+ * `host/clips-browser` in ui-library's `HOST_WIDGETS`.
26262
+ */
26263
+ deviceConfig: { ui: {
26264
+ kind: "widget",
26265
+ widgetId: "host/clips-browser",
26266
+ tab: "clips",
26267
+ topTab: true,
26268
+ label: "Clips",
26269
+ order: 0
26270
+ } },
25923
26271
  methods: {
25924
26272
  listClips: method(z.object({
25925
26273
  deviceId: z.number(),
25926
26274
  since: z.number(),
25927
26275
  until: z.number(),
25928
- limit: z.number().int().positive().optional()
26276
+ limit: z.number().int().positive().optional(),
26277
+ /**
26278
+ * View filter over {@link ClipSourceSchema.source} values — the
26279
+ * picker's selection, forwarded so a provider need not list what
26280
+ * nobody is looking at. ABSENT means every source this camera has,
26281
+ * which is the honest default for a surface whose whole point is that
26282
+ * nothing is hidden (D554 3). A provider with one source ignores it.
26283
+ */
26284
+ sources: z.array(z.string()).optional()
25929
26285
  }), z.array(ClipSchema).readonly(), {
25930
26286
  kind: "query",
25931
- auth: "admin"
26287
+ auth: "protected"
26288
+ }),
26289
+ /**
26290
+ * The sources this camera has, WITH the reason any of them cannot answer.
26291
+ *
26292
+ * Asked separately from `listClips` because an empty clip list is
26293
+ * ambiguous and this is the only place the ambiguity is resolved: every
26294
+ * bound provider contributes its own rows, and a provider that could not
26295
+ * be reached at all still produces one row saying so. A surface that draws
26296
+ * "no clips" without reading this is drawing a guess.
26297
+ */
26298
+ listSources: method(z.object({ deviceId: z.number() }), z.array(ClipSourceSchema).readonly(), {
26299
+ kind: "query",
26300
+ auth: "protected"
25932
26301
  }),
25933
26302
  getClipPlayback: method(z.object({
25934
26303
  deviceId: z.number(),
25935
- clipId: z.string()
26304
+ clipId: z.string(),
26305
+ /**
26306
+ * Which twin to serve, on the ONE quality scale the system already has
26307
+ * (`CamProfileSchema`). `low | mid` → the sub file, `high` → the main
26308
+ * twin; both ids are already on the row so this never re-searches the
26309
+ * camera. Absent means the provider's own default (the sub file, which
26310
+ * every source is measured to hold).
26311
+ *
26312
+ * `auto` is deliberately NOT accepted here: a stored file has no
26313
+ * broker session, so the adaptive tier cannot be resolved for it. The
26314
+ * viewer resolves `auto` to a profile the same way live does, before
26315
+ * it calls (D549 15).
26316
+ */
26317
+ profile: CamProfileSchema.optional()
25936
26318
  }), ClipPlaybackSchema, {
25937
26319
  kind: "query",
25938
- auth: "admin"
26320
+ auth: "protected"
25939
26321
  })
25940
26322
  }
25941
26323
  };
@@ -45915,6 +46297,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
45915
46297
  addonId: null,
45916
46298
  access: "view"
45917
46299
  },
46300
+ "videoclips.listSources": {
46301
+ capName: "videoclips",
46302
+ capScope: "device",
46303
+ addonId: null,
46304
+ access: "view"
46305
+ },
45918
46306
  "viewerUi.getStaticDir": {
45919
46307
  capName: "viewer-ui",
45920
46308
  capScope: "system",
@@ -47923,6 +48311,11 @@ Object.freeze({
47923
48311
  form: "single",
47924
48312
  optional: false
47925
48313
  }],
48314
+ "videoclips.listSources": [{
48315
+ name: "deviceId",
48316
+ form: "single",
48317
+ optional: false
48318
+ }],
47926
48319
  "waterHeater.setAway": [{
47927
48320
  name: "deviceId",
47928
48321
  form: "single",
@@ -49045,4 +49438,4 @@ function enumerateInferenceDevices(hw) {
49045
49438
  return out;
49046
49439
  }
49047
49440
  //#endregion
49048
- export { deviceStateCapability as $, streamCatalogCapability as $t, StorageLocationModeSchema as A, readinessKey as An, motionCapability as At, bareAddonId as B, resolveLocationMode as Bt, RecordingStatusSchema as C, expandCapMethods as Cn, logDestinationCapability as Ct, STREAM_PROFILE_META as D, nodePin as Dn, mayWriteToLocation as Dt, STORAGE_ACCESS_FALLBACK as E, isEvent as En, mayReadLocation as Et, addonSettingsCapability as F, procedureAuthKey as Ft, coreBlockAddonId as G, scoreRuntimes as Gt, buildStreamParamsConfigSchema as H, runtimeStatePolicyFor as Ht, addonWidgetsCapability as I, ptzAutotrackCapability as It, dataStoreProviderCapability as J, snapshotCapability as Jt, coreBlockIdFromAddonId as K, settingsStoreCapability as Kt, alertsCapability as L, reducePoints as Lt, StorageMigrationJobSchema as M, scopeKey as Mn, objectInputDeclaresAddonId as Mt, UserRecordSchema as N, sleep as Nn, parseStreamParamsFormPatch as Nt, ScopedTokenSchema as O, parseJsonObject as On, metricsProviderCapability as Ot, addonPagesCapability as P, EventCategory as Pn, platformProbeCapability as Pt, deviceManagerCapability as Q, storageProviderCapability as Qt, authProviderCapability as R, resolveBucketMs as Rt, RUNTIME_DEFAULTS as S, emitReadiness as Sn, locationIconKey as St, SOURCE_DEVICE_TYPES as T, isDeviceConfigCap as Tn, looseSchema as Tt, cameraGridLayoutCapability as U, scopesAllowAddon as Ut, batteryCapability as V, resolveMethodAuth as Vt, collectSecretConfigKeys as W, scopesAllowDeviceCap as Wt, decodeVectorBase64 as X, storageMigrationCapability as Xt, declarationOwnerNodeId as Y, storageCapability as Yt, deriveBatteryPresence as Z, storageOccupancyCapability as Zt, LegacyStorageLocationDefaultSchema as _, asJsonObject as _n, isVoidInput as _t, BatteryStatusSchema as a, errMsg as an, evaluateSensorEdge as at, MOTION_CLOSE_AFTER_MS as b, createEvent as bn, lifecycleJobSchema as bt, CORE_BLOCKS_ADDON_ID as c, DATAPLANE_SECRET_HEADER as cn, filesystemBrowseCapability as ct, DEFAULT_FIRST_SIGHTING_FRESHNESS_MS as d, DeviceFeature as dn, isArrayOutputSchema as dt, streamQualityLabel as en, deviceStatusCapability as et, DERIVED_CAMERA_SILENCED_CAP_NAMES as f, DeviceRole as fn, isCollectionArrayMethod as ft, LOG_LEVEL_RANK as g, WELL_KNOWN_TAB_MAP as gn, isSourceCap as gt, FIRST_LEVEL_MACRO_CLASSES as h, ReadinessTimeoutError as hn, isSameAddonId as ht, BaseDevice as i, withLocationMode as in, enumerateSchemaFields as it, StorageLocationTypeSchema as j, resolveCapMount as jn, normalizeUnit as jt, StorageCleanupJobSchema as k, parseJsonUnknown as kn, modeMayWrite as kt, CORE_BLOCK_ADDON_PREFIX as l, DEVICE_SETTINGS_CONTRIBUTION_METHODS as ln, foldSnapshotByFunction as lt, DeviceStatusSchema as m, ReadinessRegistry as mn, isObjectInput as mt, AlertSchema as n, vectorDimFromBase64 as nn, enumerateInferenceDevices as nt, CAMERA_SWITCH_ORDER as o, buildFfmpegArgs as on, evictionPolicyOfLocation as ot, DeclaredDevices as p, DeviceType as pn, isLocationEnabled as pt, coreBlocksCapability as q, siteZoneKey as qt, ApiKeyRecordSchema as r, vectorStoreCapability as rn, enumerateItemArrayFields as rt, CAP_NAMES_WITH_STATUS as s, BaseAddon as sn, extractNestedAddonId as st, ALL_CAPABILITY_DEFINITIONS as t, userManagementCapability as tn, doorbellCapability as tt, CoreBlockSchema as u, DEVICE_STATUS_METHOD as un, gbToBytes as ut, LocationIconIdSchema as v, asNumber as vn, kebabToCamel as vt, SOURCE_CAPS as w, hydrateSchema as wn, logLevelAtMost as wt, REDACTED_SECRET as x, emitDownForOwnedCaps as xn, localNetworkCapability as xt, METHOD_ACCESS_MAP as y, asString as yn, legacyModeOf as yt, backupCapability as z, resolveContainerPrimaryChild as zt };
49441
+ export { deviceStateCapability as $, storageProviderCapability as $t, StorageLocationModeSchema as A, parseJsonUnknown as An, motionCapability as At, bareAddonId as B, resolveContainerPrimaryChild as Bt, RecordingStatusSchema as C, emitReadiness as Cn, logDestinationCapability as Ct, STREAM_PROFILE_META as D, isEvent as Dn, mayWriteToLocation as Dt, STORAGE_ACCESS_FALLBACK as E, isDeviceConfigCap as En, mayReadLocation as Et, addonSettingsCapability as F, EventCategory as Fn, platformProbeCapability as Ft, coreBlockAddonId as G, scopesAllowDeviceCap as Gt, buildStreamParamsConfigSchema as H, resolveMethodAuth as Ht, addonWidgetsCapability as I, procedureAuthKey as It, dataStoreProviderCapability as J, siteZoneKey as Jt, coreBlockIdFromAddonId as K, scoreRuntimes as Kt, alertsCapability as L, ptzAutotrackCapability as Lt, StorageMigrationJobSchema as M, resolveCapMount as Mn, objectInputDeclaresAddonId as Mt, UserRecordSchema as N, scopeKey as Nn, objectInputDeclaresRequiredDeviceId as Nt, ScopedTokenSchema as O, nodePin as On, metricsProviderCapability as Ot, addonPagesCapability as P, sleep as Pn, parseStreamParamsFormPatch as Pt, deviceManagerCapability as Q, storageOccupancyCapability as Qt, authProviderCapability as R, reducePoints as Rt, RUNTIME_DEFAULTS as S, emitDownForOwnedCaps as Sn, locationIconKey as St, SOURCE_DEVICE_TYPES as T, hydrateSchema as Tn, looseSchema as Tt, cameraGridLayoutCapability as U, runtimeStatePolicyFor as Ut, batteryCapability as V, resolveLocationMode as Vt, collectSecretConfigKeys as W, scopesAllowAddon as Wt, decodeVectorBase64 as X, storageCapability as Xt, declarationOwnerNodeId as Y, snapshotCapability as Yt, deriveBatteryPresence as Z, storageMigrationCapability as Zt, LegacyStorageLocationDefaultSchema as _, WELL_KNOWN_TAB_MAP as _n, isVoidInput as _t, BatteryStatusSchema as a, withLocationMode as an, evaluateSensorEdge as at, MOTION_CLOSE_AFTER_MS as b, asString as bn, lifecycleJobSchema as bt, CORE_BLOCKS_ADDON_ID as c, BaseAddon as cn, filesystemBrowseCapability as ct, DEFAULT_FIRST_SIGHTING_FRESHNESS_MS as d, DEVICE_STATUS_METHOD as dn, isArrayOutputSchema as dt, streamCatalogCapability as en, deviceStatusCapability as et, DERIVED_CAMERA_SILENCED_CAP_NAMES as f, DeviceFeature as fn, isCollectionArrayMethod as ft, LOG_LEVEL_RANK as g, ReadinessTimeoutError as gn, isSourceCap as gt, FIRST_LEVEL_MACRO_CLASSES as h, ReadinessRegistry as hn, isSameAddonId as ht, BaseDevice as i, vectorStoreCapability as in, enumerateSchemaFields as it, StorageLocationTypeSchema as j, readinessKey as jn, normalizeUnit as jt, StorageCleanupJobSchema as k, parseJsonObject as kn, modeMayWrite as kt, CORE_BLOCK_ADDON_PREFIX as l, DATAPLANE_SECRET_HEADER as ln, foldSnapshotByFunction as lt, DeviceStatusSchema as m, DeviceType as mn, isObjectInput as mt, AlertSchema as n, userManagementCapability as nn, enumerateInferenceDevices as nt, CAMERA_SWITCH_ORDER as o, errMsg as on, evictionPolicyOfLocation as ot, DeclaredDevices as p, DeviceRole as pn, isLocationEnabled as pt, coreBlocksCapability as q, settingsStoreCapability as qt, ApiKeyRecordSchema as r, vectorDimFromBase64 as rn, enumerateItemArrayFields as rt, CAP_NAMES_WITH_STATUS as s, buildFfmpegArgs as sn, extractNestedAddonId as st, ALL_CAPABILITY_DEFINITIONS as t, streamQualityLabel as tn, doorbellCapability as tt, CoreBlockSchema as u, DEVICE_SETTINGS_CONTRIBUTION_METHODS as un, gbToBytes as ut, LocationIconIdSchema as v, asJsonObject as vn, kebabToCamel as vt, SOURCE_CAPS as w, expandCapMethods as wn, logLevelAtMost as wt, REDACTED_SECRET as x, createEvent as xn, localNetworkCapability as xt, METHOD_ACCESS_MAP as y, asNumber as yn, legacyModeOf as yt, backupCapability as z, resolveBucketMs as zt };