@camstack/addon-post-analysis 1.2.17 → 1.2.19

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.
@@ -7243,35 +7243,22 @@ var EVENT_PAD_MS = {
7243
7243
  */
7244
7244
  var RecordingWeekdaySchema = number().int().min(0).max(6);
7245
7245
  var HHMM = /^([01]\d|2[0-3]):[0-5]\d$/;
7246
- var RecordingScheduleSchema = discriminatedUnion("kind", [object({ kind: literal("always") }), object({
7247
- kind: literal("timeOfDay"),
7248
- start: string().regex(HHMM),
7249
- end: string().regex(HHMM),
7250
- /** Restrict to these weekdays; omit = every day. */
7251
- days: array(RecordingWeekdaySchema).optional()
7252
- })]);
7253
- var RecordingModeSchema = _enum([
7254
- "continuous",
7255
- "onMotion",
7256
- "onAudioThreshold"
7257
- ]);
7258
7246
  /**
7259
- * First-class, authoritative per-camera storage mode — the explicit choice the
7260
- * UI reads directly (never inferred from `rules`):
7261
- * - `off` — not recording.
7262
- * - `events` — record only around triggers (motion / audio threshold),
7263
- * with pre/post-buffer.
7264
- * - `continuous` — record 24/7 within the schedule.
7247
+ * DERIVED per-camera storage summary — the single field cheap consumers read
7248
+ * (the viewer's status dot, the camera list) instead of walking `bands`:
7249
+ * - `off` — no band covers the camera (or it is disabled).
7250
+ * - `events` — every band records around triggers only.
7251
+ * - `continuous` — at least one band records continuously.
7265
7252
  *
7266
- * `mode` compiles one-way to the internal `rules[]` consumed by the policy
7267
- * engine (see `compileRules`); `rules[]` is never authored directly anymore.
7253
+ * NEVER authored: the recorder stamps it from the authoritative `bands` on
7254
+ * every save (`activeModeForConfig`). Writing it has no effect.
7268
7255
  */
7269
7256
  var RecordingStorageModeSchema = _enum([
7270
7257
  "off",
7271
7258
  "events",
7272
7259
  "continuous"
7273
7260
  ]);
7274
- /** Which detectors trigger an `events`-mode recording. */
7261
+ /** Which detectors trigger an `events`-mode band. */
7275
7262
  var RecordingTriggersSchema = object({
7276
7263
  motion: boolean().optional(),
7277
7264
  audioThresholdDbfs: number().optional()
@@ -7307,18 +7294,6 @@ var RecordingBandSchema = object({
7307
7294
  preBufferSec: number().min(0).optional(),
7308
7295
  postBufferSec: number().min(0).optional()
7309
7296
  });
7310
- var RecordingRuleSchema = object({
7311
- schedule: RecordingScheduleSchema,
7312
- mode: RecordingModeSchema,
7313
- /** Seconds of footage to retain BEFORE a trigger (applied at keep/discard). */
7314
- preBufferSec: number().min(0).default(0),
7315
- /** Keep recording until this many seconds after the last trigger. */
7316
- postBufferSec: number().min(0).default(0),
7317
- /** Each new trigger restarts the post-buffer window. */
7318
- resetTimeoutOnNewEvent: boolean().default(true),
7319
- /** onAudioThreshold only — dBFS level that counts as a trigger. */
7320
- thresholdDbfs: number().optional()
7321
- });
7322
7297
  /**
7323
7298
  * Per-device retention overrides. Every field is optional; an unset or `0`
7324
7299
  * value inherits the node-wide recorder default. Only footage-lifetime limits
@@ -7352,40 +7327,28 @@ var ScrubThumbnailPresetSchema = _enum([
7352
7327
  /**
7353
7328
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7354
7329
  *
7355
- * `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
7356
- * are its mode-specific parameters. `rules` is a DEPRECATED authoring input kept
7357
- * only for transition + migration (`migrateRulesToMode`); the policy engine
7358
- * consumes the compiled output of `compileRules(config)`, never `rules` directly.
7330
+ * `bands` is the ONLY authored recording intent: what to record, when, and on
7331
+ * which trigger. `mode` is a derived summary the recorder stamps on save; every
7332
+ * other field is a storage knob (profiles, segment length, retention, scrub).
7333
+ *
7334
+ * STRICT on purpose: the legacy authoring surface (`schedule`/`schedules`/
7335
+ * `triggers`/`preBufferSec`/`postBufferSec`/`rules`) was retired 2026-07-30.
7336
+ * A stale caller must fail loudly — silently stripping its legacy intent would
7337
+ * persist a band-less config, i.e. silently stop recording the camera.
7359
7338
  */
7360
7339
  var RecordingConfigSchema = object({
7361
7340
  enabled: boolean(),
7362
- /** Authoritative storage mode. Absent on legacy targets derived once via
7363
- * `migrateRulesToMode`, then persisted. */
7341
+ /** DERIVED summary of `bands`, stamped by the recorder on every save.
7342
+ * Authoring it has no effect — see {@link RecordingStorageModeSchema}. */
7364
7343
  mode: RecordingStorageModeSchema.optional(),
7365
7344
  profiles: array(CamProfileSchema).optional(),
7366
7345
  segmentSeconds: number().int().positive().optional(),
7367
- /** Shared recording time-bands for `events` & `continuous` — record only when
7368
- * the wall-clock falls inside one of these windows. Omit or empty = always.
7369
- * `continuous` compiles to one rule per band; `events` to band × trigger. */
7370
- schedules: array(RecordingScheduleSchema).optional(),
7371
- /** Legacy single-band predecessor of `schedules`. Read-compat only — it is
7372
- * normalized into `schedules` on read and never written going forward. (Not
7373
- * tagged `@deprecated`: the normalization paths must read it cast-free.) */
7374
- schedule: RecordingScheduleSchema.optional(),
7375
- /** `events`-mode only — which detectors trigger a recording. */
7376
- triggers: RecordingTriggersSchema.optional(),
7377
- /** `events`-mode only — seconds retained before / after a trigger. */
7378
- preBufferSec: number().min(0).optional(),
7379
- postBufferSec: number().min(0).optional(),
7380
- /** DEPRECATED authoring input; retained for migration/transition. */
7381
- rules: array(RecordingRuleSchema).optional(),
7382
- /**
7383
- * AUTHORITATIVE mode-per-band recording model (recorder). When present it
7384
- * is the single source of truth; the legacy `mode`/`schedules`/`schedule`/
7385
- * `triggers`/`rules` fields above are kept for READ-COMPAT only and are
7386
- * derived into bands once via `migrateConfigToBands`.
7346
+ /**
7347
+ * AUTHORITATIVE mode-per-band recording model the single source of truth
7348
+ * the recorder's band engine consumes. An empty array = record nothing;
7349
+ * "off" is the absence of a covering band, never a band value.
7387
7350
  */
7388
- bands: array(RecordingBandSchema).optional(),
7351
+ bands: array(RecordingBandSchema).default([]),
7389
7352
  retention: RecordingRetentionSchema.optional(),
7390
7353
  /**
7391
7354
  * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
@@ -7393,8 +7356,15 @@ var RecordingConfigSchema = object({
7393
7356
  * windows only — existing sheets are immutable, and each window's index
7394
7357
  * carries its own tile dims so mixed-preset history renders correctly.
7395
7358
  */
7396
- scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7397
- });
7359
+ scrubThumbnails: ScrubThumbnailPresetSchema.optional(),
7360
+ /**
7361
+ * OPT-IN thumbnail-strip generation for this camera: every keyframe of the
7362
+ * low recording saved as a JPEG (the fast-drag scrub depth), a derived
7363
+ * cache that eviction reclaims with the footage. Absent/false = no strips
7364
+ * are written and scrub reads exact keyframes at every velocity.
7365
+ */
7366
+ stripsEnabled: boolean().optional()
7367
+ }).strict();
7398
7368
  /**
7399
7369
  * Ops-log — the durable, append-only operations audit shared by the
7400
7370
  * recordings and events management surfaces.
@@ -7413,7 +7383,8 @@ var OpsLogOpSchema = _enum([
7413
7383
  "prune",
7414
7384
  "manual-delete",
7415
7385
  "rescan",
7416
- "retention-run"
7386
+ "retention-run",
7387
+ "relocate"
7417
7388
  ]);
7418
7389
  /** Why the operation ran. */
7419
7390
  var OpsLogReasonSchema = _enum([
@@ -7452,6 +7423,55 @@ var OpsLogQueryInputSchema = object({
7452
7423
  limit: number().int().min(1).max(1e3).optional()
7453
7424
  });
7454
7425
  /**
7426
+ * Entity-relocation job state (storage entity-routing spec, Phase 4).
7427
+ *
7428
+ * One shape shared by the recorder's `relocateFootage` (segments + strips) and
7429
+ * pipeline-analytics' `relocateMedia` (event media blobs) so the admin Data
7430
+ * page renders both movers with one component. Jobs are in-RAM (a restart
7431
+ * forgets them — re-running is safe by construction: copy-if-absent, delete
7432
+ * after verify) and each completed/failed run also lands one durable ops-log
7433
+ * row on the owning addon's surface.
7434
+ */
7435
+ var RelocateJobStateSchema = _enum([
7436
+ "running",
7437
+ "done",
7438
+ "failed",
7439
+ "cancelled"
7440
+ ]);
7441
+ var RelocateJobSchema = object({
7442
+ jobId: string(),
7443
+ state: RelocateJobStateSchema,
7444
+ /** Source location — for media relocation this is informational ('*': rows
7445
+ * move from wherever they are to the target). */
7446
+ fromLocationId: string(),
7447
+ toLocationId: string(),
7448
+ /** Scoped device, or null = every device. */
7449
+ deviceId: number().nullable(),
7450
+ /** What the job moves (owner-addon specific: segments/strips or media). */
7451
+ entities: array(string()),
7452
+ filesMoved: number().int(),
7453
+ bytesMoved: number().int(),
7454
+ /** Total files discovered up front; null while (or when) unknown. */
7455
+ filesTotal: number().int().nullable(),
7456
+ startedAt: number(),
7457
+ finishedAt: number().nullable(),
7458
+ error: string().nullable()
7459
+ });
7460
+ var RelocateFootageInputSchema = object({
7461
+ deviceId: number().optional(),
7462
+ fromLocationId: string(),
7463
+ toLocationId: string(),
7464
+ entities: array(_enum(["segments", "strips"])).optional(),
7465
+ /** Copy throttle in MB/s (default 40) — the drain is a background chore,
7466
+ * never allowed to starve live writers. */
7467
+ throttleMbps: number().min(1).max(1e3).optional()
7468
+ });
7469
+ var RelocateMediaInputSchema = object({
7470
+ deviceId: number().optional(),
7471
+ toLocationId: string(),
7472
+ throttleMbps: number().min(1).max(1e3).optional()
7473
+ });
7474
+ /**
7455
7475
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
7456
7476
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
7457
7477
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -7501,6 +7521,13 @@ var StorageLocationSchema = object({
7501
7521
  nodeId: string().optional(),
7502
7522
  isDefault: boolean().default(false),
7503
7523
  isSystem: boolean().default(false),
7524
+ /** COMPUTED at read time by the orchestrator (statfs of the backing volume
7525
+ * for node-local locations it can reach) — never persisted, absent when the
7526
+ * volume is remote/unreachable. The single capacity truth every UI reads. */
7527
+ capacity: object({
7528
+ totalBytes: number(),
7529
+ availableBytes: number()
7530
+ }).nullable().optional(),
7504
7531
  createdAt: number(),
7505
7532
  updatedAt: number()
7506
7533
  });
@@ -8973,6 +9000,7 @@ var AccessoryKind = {
8973
9000
  };
8974
9001
  AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
8975
9002
  DeviceFeature.BatteryOperated;
9003
+ new Set(["devices", "classes"]);
8976
9004
  /**
8977
9005
  * Shared geometry vocabulary for on-frame shape caps — privacy-mask,
8978
9006
  * motion-zones, and the detection zones/lines editor all speak this one
@@ -9289,12 +9317,60 @@ var NcRuleTargetSchema = object({
9289
9317
  * - `keyFrame` — the clean scene frame (no subject box).
9290
9318
  * - `none` — no attachment.
9291
9319
  */
9292
- var NcMediaPolicySchema = object({ attach: _enum([
9293
- "best",
9294
- "best-matching",
9295
- "keyFrame",
9296
- "none"
9297
- ]).default("best") });
9320
+ /**
9321
+ * WHAT THE PICTURE SHOWS — chosen explicitly, because the implicit ladders
9322
+ * conflate it with the selection strategy and betray the request: asking for
9323
+ * the clean scene frame on an object-event owner used to start at
9324
+ * `fullFrameBoxed`, i.e. the annotated frame (2026-07-30).
9325
+ * - `cropped` — the subject, tight. What "who is at the door" wants.
9326
+ * - `full` — the clean scene, no annotation. What "what is going on" wants.
9327
+ * - `boxed` — the scene WITH the detection boxes drawn, for verifying what
9328
+ * the pipeline actually saw.
9329
+ */
9330
+ var NcMediaFrameSchema = _enum([
9331
+ "cropped",
9332
+ "full",
9333
+ "boxed"
9334
+ ]);
9335
+ var NcMediaPolicySchema = object({
9336
+ attach: _enum([
9337
+ "best",
9338
+ "best-matching",
9339
+ "keyFrame",
9340
+ "none"
9341
+ ]).default("best"),
9342
+ /** What the still shows. Absent = `cropped` (the historical `best` shape). */
9343
+ frame: NcMediaFrameSchema.optional(),
9344
+ /** Crop the attached still to the rule's condition-zone bbox (padded) —
9345
+ * "show me the ZONE", not the whole scene or the subject crop. */
9346
+ zoneCrop: boolean().optional(),
9347
+ /**
9348
+ * Also attach a short GIF cut from the stream broker's clip ring around the
9349
+ * event — NOT from the recording, so the camera does not have to be
9350
+ * recording, and the window sits AROUND the moment instead of a segment
9351
+ * behind it. Fail-closed: a window the ring does not cover contributes no
9352
+ * gif, never a failed notification.
9353
+ */
9354
+ gif: boolean().optional(),
9355
+ /**
9356
+ * Also attach a short MP4 CLIP of the same cut. Same source and the same
9357
+ * fail-closed rule as `gif`. Prefer it where the backend takes video
9358
+ * (telegram, discord, zentik, webhook); backends that do not are handled by
9359
+ * the degrade engine, which drops the video and keeps the still.
9360
+ */
9361
+ clip: boolean().optional(),
9362
+ /** Seconds of footage BEFORE / AFTER the event instant. Defaults 3 / 7. */
9363
+ clipPreRollSec: number().int().min(0).max(30).optional(),
9364
+ clipPostRollSec: number().int().min(0).max(30).optional(),
9365
+ /**
9366
+ * Which stream profile the footage is cut from. Absent = the CHEAPEST
9367
+ * assigned profile: a notification is watched on a phone, so the 4K
9368
+ * rendition would burn CPU to produce a file the client downscales anyway.
9369
+ * A profile that is not assigned falls back to the cheapest, and the render
9370
+ * reports which one actually ran.
9371
+ */
9372
+ profile: CamProfileSchema.optional()
9373
+ });
9298
9374
  /** Throttle — cooldown survives restarts (rebuilt from the outbox on boot). */
9299
9375
  var NcThrottleSchema = object({
9300
9376
  cooldownSec: number().int().min(0).max(86400).default(60),
@@ -9308,7 +9384,19 @@ var NcRuleInputSchema = object({
9308
9384
  delivery: NcDeliverySchema,
9309
9385
  conditions: NcConditionsSchema.default({}),
9310
9386
  schedule: NcScheduleSchema.optional(),
9311
- targets: array(NcRuleTargetSchema).min(1),
9387
+ /** May be empty when `targetUsers` addresses at least one user — the
9388
+ * "at least one addressee" invariant is enforced by the provider, because
9389
+ * a cross-field refine here would break `NcRulePatchSchema.partial()`. */
9390
+ targets: array(NcRuleTargetSchema),
9391
+ /**
9392
+ * USERS this rule addresses in addition to `targets` (Phase 4). At fire
9393
+ * time each user fans out to the personal targets they own
9394
+ * (`config.ownerUserId`), filtered by that user's `allowedDevices` for the
9395
+ * firing camera — a user is never notified about a device they cannot open.
9396
+ * Per-user opt-outs (`disabledTargetIds`) still apply to the fanned-out
9397
+ * targets.
9398
+ */
9399
+ targetUsers: array(string()).optional(),
9312
9400
  media: NcMediaPolicySchema.default({ attach: "best" }),
9313
9401
  throttle: NcThrottleSchema.default({
9314
9402
  cooldownSec: 60,
@@ -9780,7 +9868,20 @@ var notificationRulesCapability = {
9780
9868
  kind: "mutation",
9781
9869
  auth: "admin"
9782
9870
  }),
9783
- getConditionCatalog: method(object({}), object({ catalog: array(NcConditionDescriptorSchema) })),
9871
+ /**
9872
+ * The machine-readable condition surface a rule editor renders from, plus
9873
+ * the picker VOCABULARY those conditions draw on.
9874
+ *
9875
+ * `taxonomy` is optional so an older provider that only returns `catalog`
9876
+ * still validates; a client that receives none keeps free-text inputs.
9877
+ * It was previously served ONLY on the `nc.getConditionCatalog` bridge
9878
+ * action, which is why the viewer had grouped pickers and the admin UI —
9879
+ * which reaches this cap — was stuck typing `person` from memory.
9880
+ */
9881
+ getConditionCatalog: method(object({}), object({
9882
+ catalog: array(NcConditionDescriptorSchema),
9883
+ taxonomy: NcTaxonomySchema.optional()
9884
+ })),
9784
9885
  /**
9785
9886
  * Queryable delivery history — a read-only view over the durable outbox
9786
9887
  * (fired rule, subject summary, target, status, timestamps, error on a
@@ -9864,7 +9965,8 @@ object({
9864
9965
  template: TimelapseTemplateSchema.nullable().optional(),
9865
9966
  priority: PriorityField.optional()
9866
9967
  });
9867
- TimelapseRuleInputSchema.extend({
9968
+ /** A persisted timelapse rule. */
9969
+ var TimelapseRuleSchema = TimelapseRuleInputSchema.extend({
9868
9970
  id: string(),
9869
9971
  /**
9870
9972
  * Ownership/visibility key. Absent = admin/global rule (visible to all).
@@ -10558,6 +10660,28 @@ method(object({
10558
10660
  }), object({ success: literal(true) }), {
10559
10661
  kind: "mutation",
10560
10662
  auth: "admin"
10663
+ }), method(object({
10664
+ deviceId: number(),
10665
+ /** Absent = the LOWEST assigned profile — a notification attachment is
10666
+ * watched on a phone, and the cheap rendition is the right default. */
10667
+ profile: CamProfileSchema.optional(),
10668
+ aroundMs: number(),
10669
+ preRollSec: number().min(0).max(20).default(3),
10670
+ postRollSec: number().min(0).max(20).default(5),
10671
+ format: _enum(["gif", "mp4"]).default("gif"),
10672
+ maxWidth: number().int().min(120).max(1920).default(480),
10673
+ /** GIF only — MP4 keeps the source cadence. */
10674
+ fps: number().int().min(1).max(15).default(5)
10675
+ }), object({
10676
+ base64: string(),
10677
+ mime: string(),
10678
+ bytes: number().int(),
10679
+ /** The profile actually rendered (what the default resolved to). */
10680
+ profile: CamProfileSchema,
10681
+ durationMs: number()
10682
+ }), {
10683
+ kind: "mutation",
10684
+ auth: "admin"
10561
10685
  }), method(_void(), array(CameraStreamSchema).readonly()), method(_void(), array(ProfileSlotSchema).readonly()), method(object({ brokerId: string() }), BrokerStatsSchema), method(object({ brokerId: string() }), object({
10562
10686
  probed: boolean(),
10563
10687
  summary: string()
@@ -17170,6 +17294,36 @@ var TargetKindSchema = object({
17170
17294
  icon: string(),
17171
17295
  /** Stamped by each provider so the concat-fanned catalog stays routable. */
17172
17296
  addonId: string(),
17297
+ /**
17298
+ * URL of the kind's bundled BRAND icon, served by the providing addon over
17299
+ * its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
17300
+ * when the addon bundles no icon for that kind — the client then falls back
17301
+ * to a neutral glyph rather than rendering the raw `icon` NAME as text.
17302
+ *
17303
+ * Root-relative on purpose: it resolves against whatever origin serves a web
17304
+ * client, and a native client joins it onto its own hub base.
17305
+ *
17306
+ * DECLARED here deliberately. It used to travel as an undeclared passthrough
17307
+ * field that survived only because the runtime cap-router forwards provider
17308
+ * output verbatim — so every consumer had to re-declare it by hand to stop
17309
+ * its own Zod parse from stripping it, and the whole arrangement would have
17310
+ * broken silently the moment output validation was tightened anywhere.
17311
+ */
17312
+ iconUrl: string().optional(),
17313
+ /**
17314
+ * Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
17315
+ *
17316
+ * The server knows this and therefore says it, because the client cannot
17317
+ * safely guess: a React-Native client renders SVG and raster through two
17318
+ * DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
17319
+ * not decode SVG on iOS/Android), so without this it silently fell back to a
17320
+ * placeholder glyph for every vector icon while the web build looked fine.
17321
+ *
17322
+ * Absent when {@link iconUrl} is absent, or for a legacy provider that has
17323
+ * not been updated — a client that cannot determine the type should prefer
17324
+ * its raster path, which is the safe default for an unknown image.
17325
+ */
17326
+ iconMediaType: string().optional(),
17173
17327
  configSchema: ConfigSchemaPassthrough,
17174
17328
  supportsDiscovery: boolean(),
17175
17329
  caps: TargetKindCapsSchema
@@ -18022,6 +18176,23 @@ var pipelineAnalyticsCapability = {
18022
18176
  }),
18023
18177
  /** The events ops-log rows (newest-first), optionally scoped to one camera.
18024
18178
  * Backed by a declared pipeline-analytics SQLite collection. */
18179
+ /**
18180
+ * Move event-media blobs onto `toLocationId` (entity-routing spec Phase
18181
+ * 4): per-row copy → stamp row.locationId → delete old blob. Rows already
18182
+ * at the target are skipped, so a re-run resumes. Single-flight.
18183
+ */
18184
+ relocateMedia: method(RelocateMediaInputSchema, object({ jobId: string() }), {
18185
+ kind: "mutation",
18186
+ auth: "admin"
18187
+ }),
18188
+ getMediaRelocateStatus: method(object({}), array(RelocateJobSchema).readonly(), {
18189
+ kind: "query",
18190
+ auth: "admin"
18191
+ }),
18192
+ cancelMediaRelocate: method(object({ jobId: string() }), object({ cancelled: boolean() }), {
18193
+ kind: "mutation",
18194
+ auth: "admin"
18195
+ }),
18025
18196
  listOpsLog: method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
18026
18197
  kind: "query",
18027
18198
  auth: "admin"
@@ -20601,6 +20772,17 @@ var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
20601
20772
  */
20602
20773
  priority: number()
20603
20774
  })).readonly() });
20775
+ /**
20776
+ * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
20777
+ * AUTO (resolved from the candidate ranking at send time); `resolved` reports
20778
+ * what AUTO currently picks, so the UI can show the effective value either way.
20779
+ */
20780
+ var NotificationEndpointSchema = object({
20781
+ /** The operator's explicit choice, or null for AUTO. */
20782
+ baseUrl: string().nullable(),
20783
+ /** What the ranking currently resolves to (null when nothing is reachable). */
20784
+ resolved: string().nullable()
20785
+ });
20604
20786
  var AllowedAddressesSchema = object({
20605
20787
  /**
20606
20788
  * Allowlist of interface addresses operators have explicitly opted
@@ -20623,7 +20805,7 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
20623
20805
  * to avoid mixed-content blocks in the browser. The public
20624
20806
  * tunnel always emits `https://` regardless. */
20625
20807
  scheme: _enum(["http", "https"]).optional()
20626
- }), GetConnectionEndpointsResultSchema), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" });
20808
+ }), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" });
20627
20809
  /**
20628
20810
  * mesh-network — collection cap for mesh-VPN providers.
20629
20811
  *
@@ -21456,7 +21638,12 @@ var RecordingDeviceUsageSchema = object({
21456
21638
  var RecordingLocationUsageSchema = object({
21457
21639
  /** StorageLocation id; null for the legacy/degraded single-root fallback. */
21458
21640
  locationId: string().nullable(),
21459
- /** Bytes of recordings stored on this location. */
21641
+ /** Every location id sharing this row's PHYSICAL volume (aliases). One row
21642
+ * is emitted per physical disk (2026-07-29): two locations on one root
21643
+ * previously rendered as two identical "disks" with a nonsensical used
21644
+ * split — hydrate attribution across aliases is arbitrary by nature. */
21645
+ locationIds: array(string()).optional(),
21646
+ /** Bytes of recordings stored on this PHYSICAL volume (all aliases). */
21460
21647
  usedBytes: number(),
21461
21648
  /** Free bytes on the location's volume; null when capacity is unknown (remote). */
21462
21649
  availableBytes: number().nullable(),
@@ -21568,6 +21755,44 @@ method(object({
21568
21755
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
21569
21756
  kind: "query",
21570
21757
  auth: "admin"
21758
+ }), method(object({
21759
+ deviceId: number(),
21760
+ aroundMs: number(),
21761
+ preRollSec: number().min(0).max(30).default(2),
21762
+ postRollSec: number().min(0).max(30).default(5),
21763
+ maxWidth: number().int().min(120).max(1280).default(480),
21764
+ fps: number().int().min(1).max(15).default(5)
21765
+ }), object({
21766
+ gifBase64: string(),
21767
+ fromMs: number(),
21768
+ toMs: number()
21769
+ }), {
21770
+ kind: "mutation",
21771
+ auth: "admin"
21772
+ }), method(object({
21773
+ deviceId: number(),
21774
+ aroundMs: number(),
21775
+ preRollSec: number().min(0).max(30).default(3),
21776
+ postRollSec: number().min(0).max(30).default(7),
21777
+ maxWidth: number().int().min(160).max(1920).default(640)
21778
+ }), object({
21779
+ clipBase64: string(),
21780
+ mime: string(),
21781
+ fromMs: number(),
21782
+ toMs: number(),
21783
+ bytes: number().int()
21784
+ }), {
21785
+ kind: "mutation",
21786
+ auth: "admin"
21787
+ }), method(RelocateFootageInputSchema, object({ jobId: string() }), {
21788
+ kind: "mutation",
21789
+ auth: "admin"
21790
+ }), method(object({}), array(RelocateJobSchema).readonly(), {
21791
+ kind: "query",
21792
+ auth: "admin"
21793
+ }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
21794
+ kind: "mutation",
21795
+ auth: "admin"
21571
21796
  });
21572
21797
  /**
21573
21798
  * `recordingExport` cap — render a footage time range into a single downloadable
@@ -24053,6 +24278,12 @@ Object.freeze({
24053
24278
  addonId: null,
24054
24279
  access: "view"
24055
24280
  },
24281
+ "localNetwork.getNotificationEndpoint": {
24282
+ capName: "local-network",
24283
+ capScope: "system",
24284
+ addonId: null,
24285
+ access: "view"
24286
+ },
24056
24287
  "localNetwork.getPreferred": {
24057
24288
  capName: "local-network",
24058
24289
  capScope: "system",
@@ -24077,6 +24308,12 @@ Object.freeze({
24077
24308
  addonId: null,
24078
24309
  access: "create"
24079
24310
  },
24311
+ "localNetwork.setNotificationEndpoint": {
24312
+ capName: "local-network",
24313
+ capScope: "system",
24314
+ addonId: null,
24315
+ access: "create"
24316
+ },
24080
24317
  "lockControl.lock": {
24081
24318
  capName: "lock-control",
24082
24319
  capScope: "device",
@@ -24719,6 +24956,12 @@ Object.freeze({
24719
24956
  addonId: null,
24720
24957
  access: "create"
24721
24958
  },
24959
+ "pipelineAnalytics.cancelMediaRelocate": {
24960
+ capName: "pipeline-analytics",
24961
+ capScope: "device",
24962
+ addonId: null,
24963
+ access: "create"
24964
+ },
24722
24965
  "pipelineAnalytics.clearTracks": {
24723
24966
  capName: "pipeline-analytics",
24724
24967
  capScope: "device",
@@ -24773,6 +25016,12 @@ Object.freeze({
24773
25016
  addonId: null,
24774
25017
  access: "view"
24775
25018
  },
25019
+ "pipelineAnalytics.getMediaRelocateStatus": {
25020
+ capName: "pipeline-analytics",
25021
+ capScope: "device",
25022
+ addonId: null,
25023
+ access: "view"
25024
+ },
24776
25025
  "pipelineAnalytics.getMotionEvents": {
24777
25026
  capName: "pipeline-analytics",
24778
25027
  capScope: "device",
@@ -24845,6 +25094,12 @@ Object.freeze({
24845
25094
  addonId: null,
24846
25095
  access: "create"
24847
25096
  },
25097
+ "pipelineAnalytics.relocateMedia": {
25098
+ capName: "pipeline-analytics",
25099
+ capScope: "device",
25100
+ addonId: null,
25101
+ access: "create"
25102
+ },
24848
25103
  "pipelineAnalytics.searchObjectEvents": {
24849
25104
  capName: "pipeline-analytics",
24850
25105
  capScope: "device",
@@ -25607,6 +25862,12 @@ Object.freeze({
25607
25862
  addonId: null,
25608
25863
  access: "create"
25609
25864
  },
25865
+ "recording.cancelRelocate": {
25866
+ capName: "recording",
25867
+ capScope: "system",
25868
+ addonId: null,
25869
+ access: "create"
25870
+ },
25610
25871
  "recording.deleteFootprint": {
25611
25872
  capName: "recording",
25612
25873
  capScope: "system",
@@ -25637,6 +25898,12 @@ Object.freeze({
25637
25898
  addonId: null,
25638
25899
  access: "view"
25639
25900
  },
25901
+ "recording.getRelocateStatus": {
25902
+ capName: "recording",
25903
+ capScope: "system",
25904
+ addonId: null,
25905
+ access: "view"
25906
+ },
25640
25907
  "recording.getStorageUsage": {
25641
25908
  capName: "recording",
25642
25909
  capScope: "system",
@@ -25667,6 +25934,24 @@ Object.freeze({
25667
25934
  addonId: null,
25668
25935
  access: "view"
25669
25936
  },
25937
+ "recording.relocateFootage": {
25938
+ capName: "recording",
25939
+ capScope: "system",
25940
+ addonId: null,
25941
+ access: "create"
25942
+ },
25943
+ "recording.renderClip": {
25944
+ capName: "recording",
25945
+ capScope: "system",
25946
+ addonId: null,
25947
+ access: "create"
25948
+ },
25949
+ "recording.renderGif": {
25950
+ capName: "recording",
25951
+ capScope: "system",
25952
+ addonId: null,
25953
+ access: "create"
25954
+ },
25670
25955
  "recording.rescanStorage": {
25671
25956
  capName: "recording",
25672
25957
  capScope: "system",
@@ -26261,6 +26546,12 @@ Object.freeze({
26261
26546
  addonId: null,
26262
26547
  access: "create"
26263
26548
  },
26549
+ "streamBroker.renderPreBufferClip": {
26550
+ capName: "stream-broker",
26551
+ capScope: "system",
26552
+ addonId: null,
26553
+ access: "create"
26554
+ },
26264
26555
  "streamBroker.restartProfile": {
26265
26556
  capName: "stream-broker",
26266
26557
  capScope: "system",
@@ -26878,4 +27169,4 @@ Object.freeze({
26878
27169
  "smtp-provider": "email"
26879
27170
  });
26880
27171
  //#endregion
26881
- export { DeviceType as A, EventCategory as B, pipelineAnalyticsCapability as C, zoneAnalyticsCapability as D, videoclipsCapability as E, boolean as F, literal as I, number as L, hydrateSchema as M, nodePin as N, errMsg as O, array as P, object as R, notificationRulesCapability as S, subKindsOf as T, customAction as _, NC_CONDITION_CATALOG as a, faceGalleryCapability as b, NcRuleInputSchema as c, NcTaxonomySchema as d, OpsLogEntrySchema as f, cosineSimilarity as g, buildEventKindDescriptor as h, MACRO_LABELS as i, createEvent as j, BaseAddon as k, NcRulePatchSchema as l, audioMetricsCapability as m, EVENT_KIND_BY_CAP as n, NC_TAXONOMY as o, addonWidgetsSourceCapability as p, EVENT_PAD_MS as r, NcConditionDescriptorSchema as s, DEFAULT_EVENT_COLOR as t, NcRuleSchema as u, defineCustomActions as v, plateGalleryCapability as w, hfModelUrl as x, embeddingEncoderCapability as y, string as z };
27172
+ export { errMsg as A, number as B, hfModelUrl as C, subKindsOf as D, plateGalleryCapability as E, nodePin as F, string as H, _enum as I, array as L, DeviceType as M, createEvent as N, videoclipsCapability as O, hydrateSchema as P, boolean as R, faceGalleryCapability as S, pipelineAnalyticsCapability as T, EventCategory as U, object as V, buildEventKindDescriptor as _, NC_CONDITION_CATALOG as a, defineCustomActions as b, NcRuleInputSchema as c, NcTaxonomySchema as d, OpsLogEntrySchema as f, audioMetricsCapability as g, addonWidgetsSourceCapability as h, MACRO_LABELS as i, BaseAddon as j, zoneAnalyticsCapability as k, NcRulePatchSchema as l, TimelapseRuleSchema as m, EVENT_KIND_BY_CAP as n, NC_TAXONOMY as o, TimelapseRuleInputSchema as p, EVENT_PAD_MS as r, NcConditionDescriptorSchema as s, DEFAULT_EVENT_COLOR as t, NcRuleSchema as u, cosineSimilarity as v, notificationRulesCapability as w, embeddingEncoderCapability as x, customAction as y, literal as z };