@camstack/addon-remote-storage 1.2.18 → 1.2.20

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/s3.addon.js CHANGED
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_shared = require("./shared-C48hU09g.js");
5
+ const require_shared = require("./shared-CGc-WGwO.js");
6
6
  let node_stream = require("node:stream");
7
7
  let _aws_sdk_client_s3 = require("@aws-sdk/client-s3");
8
8
  let _aws_sdk_lib_storage = require("@aws-sdk/lib-storage");
package/dist/s3.addon.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { c as BaseAddon, i as rearmIdleAbort, n as getOptionalBasePath, o as scheduleIdleAbort, s as storageProviderCapability, t as createSessionId } from "./shared-Cud38F0U.mjs";
1
+ import { c as BaseAddon, i as rearmIdleAbort, n as getOptionalBasePath, o as scheduleIdleAbort, s as storageProviderCapability, t as createSessionId } from "./shared-C-bqmSqc.mjs";
2
2
  import { PassThrough } from "node:stream";
3
3
  import { DeleteObjectCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectsV2Command, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
4
4
  import { Upload } from "@aws-sdk/lib-storage";
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_shared = require("./shared-C48hU09g.js");
5
+ const require_shared = require("./shared-CGc-WGwO.js");
6
6
  let ssh2 = require("ssh2");
7
7
  let node_path = require("node:path");
8
8
  node_path = require_shared.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { a as safeJoinRemotePath, c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, r as getRequiredBasePath, s as storageProviderCapability, t as createSessionId } from "./shared-Cud38F0U.mjs";
1
+ import { a as safeJoinRemotePath, c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, r as getRequiredBasePath, s as storageProviderCapability, t as createSessionId } from "./shared-C-bqmSqc.mjs";
2
2
  import { Client } from "ssh2";
3
3
  import * as path from "node:path";
4
4
  //#region src/providers/sftp/sftp-config-schema.ts
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import * as path from "node:path";
3
- //#region ../types/dist/event-category-Bxo5yJjt.mjs
3
+ //#region ../types/dist/event-category-XfKNtfCc.mjs
4
4
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
5
5
  EventCategory["SystemBoot"] = "system.boot";
6
6
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -17,9 +17,10 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
17
17
  EventCategory["SystemRestartCompleted"] = "system.restart-completed";
18
18
  /**
19
19
  * A newer addon or server-root package version was found by the
20
- * authoritative registry check. Emitted once per
21
- * `(target, packageName, currentVersion, latestVersion)` transition; repeated
22
- * polling of the same result is deduplicated by the checker.
20
+ * authoritative registry check. Emitted once when any observed
21
+ * `latestVersion` changes (or a package/node first appears behind);
22
+ * the payload carries the full currently-available list. Repeated
23
+ * polling of the same latests is silent.
23
24
  */
24
25
  EventCategory["UpdateAvailable"] = "update.available";
25
26
  /**
@@ -38,6 +39,22 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
38
39
  EventCategory["AddonInstalled"] = "addon.installed";
39
40
  EventCategory["AddonUninstalled"] = "addon.uninstalled";
40
41
  EventCategory["AddonCrashed"] = "addon.crashed";
42
+ /**
43
+ * A RUNNER the D6 crash circuit-breaker gave up on — terminal.
44
+ *
45
+ * `AddonCrashed` is the routine, self-healing fact ("it crashed; it is being
46
+ * respawned"); this is the one that never resolves itself. Emitted exactly
47
+ * once per trip, by `process-service.ts`, carrying the node, the runner, the
48
+ * addons it hosted and the crash count that tripped the breaker.
49
+ *
50
+ * It exists because a runner marked terminally `failed` used to be SILENT:
51
+ * on 2026-08-18 the `recorder` runner died of three unhandled ffmpeg spawn
52
+ * errors, the breaker stopped respawning it (correctly), `/health` kept
53
+ * returning 200, and fleet-wide recording was gone for 3h15 before the
54
+ * operator's phone told him. The Notification Center's system-event intake
55
+ * consumes this category and maps it to the `addon-crash-loop` kind.
56
+ */
57
+ EventCategory["AddonRunnerFailed"] = "addon.runner-failed";
41
58
  EventCategory["AddonError"] = "addon.error";
42
59
  EventCategory["AddonPageReady"] = "addon.page-ready";
43
60
  EventCategory["AddonWidgetReady"] = "addon.widget-ready";
@@ -7533,6 +7550,10 @@ var RecordingBandSchema = object({
7533
7550
  preBufferSec: number().min(0).optional(),
7534
7551
  postBufferSec: number().min(0).optional()
7535
7552
  });
7553
+ ({
7554
+ preBufferSec: 10,
7555
+ postBufferSec: 30
7556
+ }).postBufferSec * 1e3;
7536
7557
  /**
7537
7558
  * Per-device retention overrides. Every field is optional; an unset or `0`
7538
7559
  * value inherits the node-wide recorder default. Only footage-lifetime limits
@@ -7580,6 +7601,12 @@ var RecordingConfigSchema = object({
7580
7601
  /** DERIVED summary of `bands`, stamped by the recorder on every save.
7581
7602
  * Authoring it has no effect — see {@link RecordingStorageModeSchema}. */
7582
7603
  mode: RecordingStorageModeSchema.optional(),
7604
+ /**
7605
+ * Which assigned broker slots to record. Absent / empty = {@link
7606
+ * DEFAULT_RECORDING_PROFILES} (`high`+`low`) intersected with the
7607
+ * camera's currently assigned slots — never `mid` unless the operator
7608
+ * picks it, and never a slot the broker has not assigned.
7609
+ */
7583
7610
  profiles: array(CamProfileSchema).optional(),
7584
7611
  segmentSeconds: number().int().positive().optional(),
7585
7612
  /**
@@ -7660,7 +7687,11 @@ var RelocateFootageInputSchema = object({
7660
7687
  profiles: array(string()).optional(),
7661
7688
  /** Copy throttle in MB/s (default 40) — the drain is a background chore,
7662
7689
  * never allowed to starve live writers. */
7663
- throttleMbps: number().min(1).max(1e3).optional()
7690
+ throttleMbps: number().min(1).max(1e3).optional(),
7691
+ /** Move only segments whose startMs is >= this. Absent = the whole source
7692
+ * pile. Used when a full drain is too expensive and the operator only
7693
+ * wants the recent window on the new disk. */
7694
+ sinceMs: number().int().optional()
7664
7695
  });
7665
7696
  /** Internal, lease-scoped participant operation. It is intentionally separate
7666
7697
  * from persistent recording settings: a migration never changes
@@ -14223,6 +14254,7 @@ var NcSystemEventKindSchema = _enum([
14223
14254
  "node-offline",
14224
14255
  "node-inference-unavailable",
14225
14256
  "detection-blind",
14257
+ "addon-crash-loop",
14226
14258
  "addon-update-available",
14227
14259
  "server-update-available",
14228
14260
  "alarm-triggered",
@@ -14230,6 +14262,9 @@ var NcSystemEventKindSchema = _enum([
14230
14262
  "alarm-disarmed",
14231
14263
  "alarm-arming",
14232
14264
  "alarm-arm-refused",
14265
+ "addon-updated",
14266
+ "server-updated",
14267
+ "export-completed",
14233
14268
  "camera-online",
14234
14269
  "camera-offline",
14235
14270
  "camera-disabled",
@@ -16123,13 +16158,19 @@ var RetrainStatusSchema = _enum([
16123
16158
  * "never marked" from "already trained" must read `retrainStatus`.
16124
16159
  *
16125
16160
  * `debug` does NOT pin; it is attention, not durability.
16161
+ *
16162
+ * `favourited` IS a BOOLEAN pin (durability bit), not a retrain lifecycle.
16163
+ * A favourited track is skipped by retention the same way `staging` is, but
16164
+ * it does not enter `none|staging|trained` and has no staging budget.
16126
16165
  */
16127
16166
  var TrackFlagFields = {
16128
16167
  /** Operator marked this track as training material — i.e. `retrainStatus` is
16129
16168
  * `'staging'`. */
16130
16169
  markForTrain: boolean().optional(),
16131
16170
  /** Operator marked this track for diagnostic attention. */
16132
- debug: boolean().optional()
16171
+ debug: boolean().optional(),
16172
+ /** Operator favourited this track. Pins it against pruning. */
16173
+ favourited: boolean().optional()
16133
16174
  };
16134
16175
  /**
16135
16176
  * The lifecycle field itself, on the READ surfaces only (`Track`, `KeyEvent`).
@@ -16154,6 +16195,7 @@ var TrackFlagsSchema = object({
16154
16195
  trackId: string(),
16155
16196
  markForTrain: boolean(),
16156
16197
  debug: boolean(),
16198
+ favourited: boolean(),
16157
16199
  /** The lifecycle state the boolean was derived from. Required here (unlike on
16158
16200
  * a track row) because this shape is only ever produced by the write body,
16159
16201
  * which always knows it — and a surface that has just written needs to render
@@ -16786,6 +16828,12 @@ var TrackCascadeCountsSchema = object({
16786
16828
  /** Per-track CLIP search vectors removed (best-effort). */
16787
16829
  embeddings: number().int()
16788
16830
  });
16831
+ /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
16832
+ var DiskReconcileCountsSchema = object({
16833
+ mediaDropped: number().int(),
16834
+ tracks: number().int(),
16835
+ events: number().int()
16836
+ });
16789
16837
  /** Event-store footprint for one camera. */
16790
16838
  var EventStoreDeviceFootprintSchema = object({
16791
16839
  deviceId: number(),
@@ -16962,6 +17010,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16962
17010
  }), method(object({ deviceId: number() }), TrackCascadeCountsSchema, {
16963
17011
  kind: "mutation",
16964
17012
  auth: "admin"
17013
+ }), method(object({ deviceId: number() }), DiskReconcileCountsSchema, {
17014
+ kind: "mutation",
17015
+ auth: "admin"
16965
17016
  }), method(object({
16966
17017
  deviceId: number(),
16967
17018
  trackIds: array(string()).min(1)
@@ -18472,6 +18523,24 @@ var CameraStatusDegradationSchema = object({
18472
18523
  *
18473
18524
  * See spec: `docs/superpowers/specs/2026-06-24-camera-status-aggregator-cap.md`
18474
18525
  */
18526
+ var DiskReconcileJobSchema = object({
18527
+ state: _enum([
18528
+ "idle",
18529
+ "running",
18530
+ "done",
18531
+ "error"
18532
+ ]),
18533
+ total: number().int().nonnegative(),
18534
+ completed: number().int().nonnegative(),
18535
+ currentDeviceId: number().int().nullable(),
18536
+ failed: array(number().int()).readonly(),
18537
+ mediaDropped: number().int().nonnegative(),
18538
+ tracks: number().int().nonnegative(),
18539
+ events: number().int().nonnegative(),
18540
+ startedAtMs: number().int().nullable(),
18541
+ finishedAtMs: number().int().nullable(),
18542
+ error: string().nullable()
18543
+ });
18475
18544
  var CameraStatusSchema = object({
18476
18545
  deviceId: number(),
18477
18546
  assignment: CameraAssignmentStatusSchema,
@@ -18703,7 +18772,10 @@ method(StorageMigrationLeaseInputSchema, object({ paused: literal(true) }), {
18703
18772
  }), CameraSwitchGroupSchema, {
18704
18773
  kind: "mutation",
18705
18774
  auth: "admin"
18706
- }), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
18775
+ }), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), DiskReconcileJobSchema, {
18776
+ kind: "mutation",
18777
+ auth: "admin"
18778
+ }), method(_void(), DiskReconcileJobSchema, { auth: "admin" }), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
18707
18779
  name: string(),
18708
18780
  description: string().optional(),
18709
18781
  config: CameraPipelineConfigSchema
@@ -19108,7 +19180,14 @@ targets: array(object({
19108
19180
  "sleeping",
19109
19181
  "unreachable",
19110
19182
  "waking"
19111
- ]).nullable()
19183
+ ]).nullable(),
19184
+ /** A battery camera (whatever its current state). An AWAKE battery
19185
+ * camera is deliberately NOT recaptured on the poll cadence — every
19186
+ * capture is a camera hit that would keep it out of sleep — so its
19187
+ * cached frame legitimately ages past the currency ceiling while
19188
+ * nothing is streaming. A surface must keep painting it (a fresh
19189
+ * frame is captured at each wake), not blank to "unavailable". */
19190
+ battery: boolean()
19112
19191
  })));
19113
19192
  /**
19114
19193
  * `sso-bridge` — internal hub-only cap that lets SSO-style auth
@@ -19814,20 +19893,6 @@ var VectorStatsResultSchema = object({
19814
19893
  exact: boolean()
19815
19894
  });
19816
19895
  method(VectorDeclareIndexInputSchema, _void(), { kind: "mutation" }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, { kind: "mutation" }), method(VectorQueryInputSchema, VectorQueryResultSchema), method(VectorGetInputSchema, VectorGetResultSchema), method(VectorDeleteInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorStatsInputSchema, VectorStatsResultSchema);
19817
- /**
19818
- * `videoclips` — the unified, navigable-clip surface for a camera.
19819
- *
19820
- * A device-scoped WRAPPER cap (like `pipeline-analytics`): exactly one active
19821
- * provider per device, substitutable. The DEFAULT provider (registered by
19822
- * `addon-post-analysis`, `defaultActive: true`) composes the analytics event
19823
- * log (markers + thumbnails) with the recorder's `getPlaybackManifest` — a clip
19824
- * is a time-WINDOW over existing footage, never a separate file. A camera that
19825
- * exposes NATIVE onboard clips (Reolink/Hikvision NVR) can later substitute the
19826
- * wrapper provider for its device and serve its own clip catalog + URLs.
19827
- *
19828
- * A `Clip` is purely time-based (subtree-blind): playback resolves segments by
19829
- * temporal overlap, so the API never decides `continuous` vs `events`.
19830
- */
19831
19896
  var ClipSchema = object({
19832
19897
  /** Opaque, provider-namespaced id. The default provider encodes the time
19833
19898
  * window so `getClipPlayback` is self-contained (no event re-query). */
@@ -19844,8 +19909,57 @@ var ClipSchema = object({
19844
19909
  startMs: number(),
19845
19910
  endMs: number()
19846
19911
  }),
19847
- /** Thumbnail URL (lazy; e.g. analytics `getEventMedia`). Never inlined. */
19848
- thumbnail: string().optional()
19912
+ /**
19913
+ * Lazy thumbnail URL, never inlined.
19914
+ *
19915
+ * Recording-derived clips (events-mode keep-window, and the prepared
19916
+ * continuous event+fragment visit) MUST use the snapshot of the **main
19917
+ * event of the interval** — `getEventMedia({ eventId, kind: 'snapshot' })`
19918
+ * of the event that owns `kind` (object > motion > audio). Do not extract
19919
+ * a keyframe from the recorded segments. Other providers (onboard, HKSV)
19920
+ * mint their own stills.
19921
+ *
19922
+ * **VOUCHED, never fabricated.** A provider emits this only for an event it
19923
+ * has CONFIRMED owns at least one media row (its own, or its owning track's).
19924
+ * Absent is meaningful — "this visit has no event still" — never "we did not
19925
+ * look". Stamping it from a URL template made 35% of one camera's clips point
19926
+ * at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
19927
+ * A read that FAILS drops the claim; it never invents it.
19928
+ */
19929
+ thumbnail: string().optional(),
19930
+ /**
19931
+ * An instant INSIDE this visit's footage, hole-safe, where a recorded still
19932
+ * can be decoded. Present whenever the visit came from recorded availability;
19933
+ * absent on a per-event padded window (there is no footage to promise).
19934
+ *
19935
+ * This is not a thumbnail and not a second byte path: it is the argument to
19936
+ * the recorder's existing still route. The surface — never the provider —
19937
+ * decides whether to use it. It is the midpoint of the visit's LONGEST
19938
+ * contiguous range, not of the visit: a visit spans its holes by
19939
+ * construction, so a naive midpoint lands in dead air.
19940
+ */
19941
+ stillAtMs: number().optional(),
19942
+ /**
19943
+ * Analytics event ids that overlap this visit — a BOUNDED sample, newest
19944
+ * first within kind (object → motion → audio), capped at
19945
+ * {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
19946
+ *
19947
+ * Bounded because it is not a payload the surface pages through: one visit on
19948
+ * device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
19949
+ * camera-day. Read {@link eventCount} for the true total.
19950
+ */
19951
+ eventIds: array(string()).optional(),
19952
+ /** How many analytics events actually overlap this visit. Differs from
19953
+ * `eventIds.length` exactly when the sample was capped — so a truncated
19954
+ * list is never mistaken for a quiet visit. */
19955
+ eventCount: number().int().nonnegative().optional(),
19956
+ /** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
19957
+ * than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
19958
+ * bar keeps showing them via `recording.getAvailability`. */
19959
+ holes: array(object({
19960
+ startMs: number(),
19961
+ endMs: number()
19962
+ })).optional()
19849
19963
  });
19850
19964
  var ClipPlaybackSchema = object({
19851
19965
  /** HLS master URL through the hub data-plane (Range + token in path). */
@@ -20309,7 +20423,14 @@ var SearchResultSchema = object({
20309
20423
  });
20310
20424
  var AutoUpdateSettingsSchema = object({
20311
20425
  channel: ChannelSchema,
20312
- intervalSeconds: number()
20426
+ intervalSeconds: number(),
20427
+ /**
20428
+ * Cadence of the "an update exists" POLLER, in seconds. Independent of
20429
+ * `channel`: the poller runs while auto-apply is `off`, because being told
20430
+ * about a publish and installing it are different decisions. Clamped
20431
+ * server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
20432
+ */
20433
+ updateCheckIntervalSeconds: number()
20313
20434
  });
20314
20435
  var AddonAutoUpdateSchema = ChannelWithInheritSchema;
20315
20436
  var RestartAddonResultSchema = unknown();
@@ -20450,7 +20571,9 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
20450
20571
  auth: "admin"
20451
20572
  }), method(_void(), AutoUpdateSettingsSchema, { auth: "admin" }), method(object({
20452
20573
  channel: ChannelSchema,
20453
- intervalSeconds: number().min(300).max(86400).optional()
20574
+ intervalSeconds: number().min(300).max(86400).optional(),
20575
+ /** Availability-poll cadence; see AutoUpdateSettingsSchema. */
20576
+ updateCheckIntervalSeconds: number().min(900).max(604800).optional()
20454
20577
  }), unknown(), {
20455
20578
  kind: "mutation",
20456
20579
  auth: "admin"
@@ -24443,7 +24566,9 @@ var ExportOptionsSchema = object({
24443
24566
  includeAudio: boolean(),
24444
24567
  maxLifeMs: number().int().positive(),
24445
24568
  deleteAfterDownload: boolean(),
24446
- title: string().max(200).optional()
24569
+ title: string().max(200).optional(),
24570
+ /** Notification-output target ids to ping when this export becomes ready. */
24571
+ notifyTargetIds: array(string().min(1)).max(20).optional()
24447
24572
  }).superRefine((v, ctx) => {
24448
24573
  if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
24449
24574
  code: ZodIssueCode.custom,
@@ -24502,10 +24627,18 @@ var ExportBytesSchema = object({
24502
24627
  });
24503
24628
  method(object({
24504
24629
  deviceId: number(),
24505
- profile: string(),
24630
+ /** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
24631
+ profile: string().optional(),
24632
+ profiles: array(string()).min(1).optional(),
24506
24633
  fromMs: number(),
24507
24634
  toMs: number(),
24508
24635
  options: ExportOptionsSchema
24636
+ }).superRefine((v, ctx) => {
24637
+ if ((v.profiles !== void 0 && v.profiles.length > 0 ? v.profiles : v.profile !== void 0 ? [v.profile] : []).length < 1) ctx.addIssue({
24638
+ code: ZodIssueCode.custom,
24639
+ message: "pass profiles[] (min 1) or legacy profile",
24640
+ path: ["profiles"]
24641
+ });
24509
24642
  }), ExportRecordSchema, {
24510
24643
  kind: "mutation",
24511
24644
  auth: "protected"
@@ -29105,6 +29238,12 @@ Object.freeze({
29105
29238
  addonId: null,
29106
29239
  access: "create"
29107
29240
  },
29241
+ "pipelineAnalytics.reconcileFromDisk": {
29242
+ capName: "pipeline-analytics",
29243
+ capScope: "device",
29244
+ addonId: null,
29245
+ access: "create"
29246
+ },
29108
29247
  "pipelineAnalytics.refreshStorageLocationsForMigration": {
29109
29248
  capName: "pipeline-analytics",
29110
29249
  capScope: "device",
@@ -29507,6 +29646,12 @@ Object.freeze({
29507
29646
  addonId: null,
29508
29647
  access: "view"
29509
29648
  },
29649
+ "pipelineOrchestrator.getReconcileFromDiskStatus": {
29650
+ capName: "pipeline-orchestrator",
29651
+ capScope: "system",
29652
+ addonId: null,
29653
+ access: "view"
29654
+ },
29510
29655
  "pipelineOrchestrator.listAgentSettings": {
29511
29656
  capName: "pipeline-orchestrator",
29512
29657
  capScope: "system",
@@ -29531,6 +29676,12 @@ Object.freeze({
29531
29676
  addonId: null,
29532
29677
  access: "create"
29533
29678
  },
29679
+ "pipelineOrchestrator.reconcileFromDisk": {
29680
+ capName: "pipeline-orchestrator",
29681
+ capScope: "system",
29682
+ addonId: null,
29683
+ access: "create"
29684
+ },
29534
29685
  "pipelineOrchestrator.removeAgentSettings": {
29535
29686
  capName: "pipeline-orchestrator",
29536
29687
  capScope: "system",
@@ -32522,6 +32673,11 @@ Object.freeze({
32522
32673
  form: "single",
32523
32674
  optional: true
32524
32675
  }],
32676
+ "pipelineAnalytics.reconcileFromDisk": [{
32677
+ name: "deviceId",
32678
+ form: "single",
32679
+ optional: false
32680
+ }],
32525
32681
  "pipelineAnalytics.restageRetrainTrack": [{
32526
32682
  name: "deviceId",
32527
32683
  form: "single",
@@ -33587,6 +33743,8 @@ DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb;
33587
33743
  DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
33588
33744
  DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
33589
33745
  DEFAULT_NATIVE_LEASE_SETTINGS.admission;
33746
+ var MB = 1024 * 1024;
33747
+ 1024 * MB, 3072 * MB;
33590
33748
  //#endregion
33591
33749
  //#region src/shared.ts
33592
33750
  /**
@@ -23,7 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  let node_crypto = require("node:crypto");
24
24
  let node_path = require("node:path");
25
25
  node_path = __toESM(node_path);
26
- //#region ../types/dist/event-category-Bxo5yJjt.mjs
26
+ //#region ../types/dist/event-category-XfKNtfCc.mjs
27
27
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
28
28
  EventCategory["SystemBoot"] = "system.boot";
29
29
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -40,9 +40,10 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
40
40
  EventCategory["SystemRestartCompleted"] = "system.restart-completed";
41
41
  /**
42
42
  * A newer addon or server-root package version was found by the
43
- * authoritative registry check. Emitted once per
44
- * `(target, packageName, currentVersion, latestVersion)` transition; repeated
45
- * polling of the same result is deduplicated by the checker.
43
+ * authoritative registry check. Emitted once when any observed
44
+ * `latestVersion` changes (or a package/node first appears behind);
45
+ * the payload carries the full currently-available list. Repeated
46
+ * polling of the same latests is silent.
46
47
  */
47
48
  EventCategory["UpdateAvailable"] = "update.available";
48
49
  /**
@@ -61,6 +62,22 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
61
62
  EventCategory["AddonInstalled"] = "addon.installed";
62
63
  EventCategory["AddonUninstalled"] = "addon.uninstalled";
63
64
  EventCategory["AddonCrashed"] = "addon.crashed";
65
+ /**
66
+ * A RUNNER the D6 crash circuit-breaker gave up on — terminal.
67
+ *
68
+ * `AddonCrashed` is the routine, self-healing fact ("it crashed; it is being
69
+ * respawned"); this is the one that never resolves itself. Emitted exactly
70
+ * once per trip, by `process-service.ts`, carrying the node, the runner, the
71
+ * addons it hosted and the crash count that tripped the breaker.
72
+ *
73
+ * It exists because a runner marked terminally `failed` used to be SILENT:
74
+ * on 2026-08-18 the `recorder` runner died of three unhandled ffmpeg spawn
75
+ * errors, the breaker stopped respawning it (correctly), `/health` kept
76
+ * returning 200, and fleet-wide recording was gone for 3h15 before the
77
+ * operator's phone told him. The Notification Center's system-event intake
78
+ * consumes this category and maps it to the `addon-crash-loop` kind.
79
+ */
80
+ EventCategory["AddonRunnerFailed"] = "addon.runner-failed";
64
81
  EventCategory["AddonError"] = "addon.error";
65
82
  EventCategory["AddonPageReady"] = "addon.page-ready";
66
83
  EventCategory["AddonWidgetReady"] = "addon.widget-ready";
@@ -7556,6 +7573,10 @@ var RecordingBandSchema = object({
7556
7573
  preBufferSec: number().min(0).optional(),
7557
7574
  postBufferSec: number().min(0).optional()
7558
7575
  });
7576
+ ({
7577
+ preBufferSec: 10,
7578
+ postBufferSec: 30
7579
+ }).postBufferSec * 1e3;
7559
7580
  /**
7560
7581
  * Per-device retention overrides. Every field is optional; an unset or `0`
7561
7582
  * value inherits the node-wide recorder default. Only footage-lifetime limits
@@ -7603,6 +7624,12 @@ var RecordingConfigSchema = object({
7603
7624
  /** DERIVED summary of `bands`, stamped by the recorder on every save.
7604
7625
  * Authoring it has no effect — see {@link RecordingStorageModeSchema}. */
7605
7626
  mode: RecordingStorageModeSchema.optional(),
7627
+ /**
7628
+ * Which assigned broker slots to record. Absent / empty = {@link
7629
+ * DEFAULT_RECORDING_PROFILES} (`high`+`low`) intersected with the
7630
+ * camera's currently assigned slots — never `mid` unless the operator
7631
+ * picks it, and never a slot the broker has not assigned.
7632
+ */
7606
7633
  profiles: array(CamProfileSchema).optional(),
7607
7634
  segmentSeconds: number().int().positive().optional(),
7608
7635
  /**
@@ -7683,7 +7710,11 @@ var RelocateFootageInputSchema = object({
7683
7710
  profiles: array(string()).optional(),
7684
7711
  /** Copy throttle in MB/s (default 40) — the drain is a background chore,
7685
7712
  * never allowed to starve live writers. */
7686
- throttleMbps: number().min(1).max(1e3).optional()
7713
+ throttleMbps: number().min(1).max(1e3).optional(),
7714
+ /** Move only segments whose startMs is >= this. Absent = the whole source
7715
+ * pile. Used when a full drain is too expensive and the operator only
7716
+ * wants the recent window on the new disk. */
7717
+ sinceMs: number().int().optional()
7687
7718
  });
7688
7719
  /** Internal, lease-scoped participant operation. It is intentionally separate
7689
7720
  * from persistent recording settings: a migration never changes
@@ -14246,6 +14277,7 @@ var NcSystemEventKindSchema = _enum([
14246
14277
  "node-offline",
14247
14278
  "node-inference-unavailable",
14248
14279
  "detection-blind",
14280
+ "addon-crash-loop",
14249
14281
  "addon-update-available",
14250
14282
  "server-update-available",
14251
14283
  "alarm-triggered",
@@ -14253,6 +14285,9 @@ var NcSystemEventKindSchema = _enum([
14253
14285
  "alarm-disarmed",
14254
14286
  "alarm-arming",
14255
14287
  "alarm-arm-refused",
14288
+ "addon-updated",
14289
+ "server-updated",
14290
+ "export-completed",
14256
14291
  "camera-online",
14257
14292
  "camera-offline",
14258
14293
  "camera-disabled",
@@ -16146,13 +16181,19 @@ var RetrainStatusSchema = _enum([
16146
16181
  * "never marked" from "already trained" must read `retrainStatus`.
16147
16182
  *
16148
16183
  * `debug` does NOT pin; it is attention, not durability.
16184
+ *
16185
+ * `favourited` IS a BOOLEAN pin (durability bit), not a retrain lifecycle.
16186
+ * A favourited track is skipped by retention the same way `staging` is, but
16187
+ * it does not enter `none|staging|trained` and has no staging budget.
16149
16188
  */
16150
16189
  var TrackFlagFields = {
16151
16190
  /** Operator marked this track as training material — i.e. `retrainStatus` is
16152
16191
  * `'staging'`. */
16153
16192
  markForTrain: boolean().optional(),
16154
16193
  /** Operator marked this track for diagnostic attention. */
16155
- debug: boolean().optional()
16194
+ debug: boolean().optional(),
16195
+ /** Operator favourited this track. Pins it against pruning. */
16196
+ favourited: boolean().optional()
16156
16197
  };
16157
16198
  /**
16158
16199
  * The lifecycle field itself, on the READ surfaces only (`Track`, `KeyEvent`).
@@ -16177,6 +16218,7 @@ var TrackFlagsSchema = object({
16177
16218
  trackId: string(),
16178
16219
  markForTrain: boolean(),
16179
16220
  debug: boolean(),
16221
+ favourited: boolean(),
16180
16222
  /** The lifecycle state the boolean was derived from. Required here (unlike on
16181
16223
  * a track row) because this shape is only ever produced by the write body,
16182
16224
  * which always knows it — and a surface that has just written needs to render
@@ -16809,6 +16851,12 @@ var TrackCascadeCountsSchema = object({
16809
16851
  /** Per-track CLIP search vectors removed (best-effort). */
16810
16852
  embeddings: number().int()
16811
16853
  });
16854
+ /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
16855
+ var DiskReconcileCountsSchema = object({
16856
+ mediaDropped: number().int(),
16857
+ tracks: number().int(),
16858
+ events: number().int()
16859
+ });
16812
16860
  /** Event-store footprint for one camera. */
16813
16861
  var EventStoreDeviceFootprintSchema = object({
16814
16862
  deviceId: number(),
@@ -16985,6 +17033,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16985
17033
  }), method(object({ deviceId: number() }), TrackCascadeCountsSchema, {
16986
17034
  kind: "mutation",
16987
17035
  auth: "admin"
17036
+ }), method(object({ deviceId: number() }), DiskReconcileCountsSchema, {
17037
+ kind: "mutation",
17038
+ auth: "admin"
16988
17039
  }), method(object({
16989
17040
  deviceId: number(),
16990
17041
  trackIds: array(string()).min(1)
@@ -18495,6 +18546,24 @@ var CameraStatusDegradationSchema = object({
18495
18546
  *
18496
18547
  * See spec: `docs/superpowers/specs/2026-06-24-camera-status-aggregator-cap.md`
18497
18548
  */
18549
+ var DiskReconcileJobSchema = object({
18550
+ state: _enum([
18551
+ "idle",
18552
+ "running",
18553
+ "done",
18554
+ "error"
18555
+ ]),
18556
+ total: number().int().nonnegative(),
18557
+ completed: number().int().nonnegative(),
18558
+ currentDeviceId: number().int().nullable(),
18559
+ failed: array(number().int()).readonly(),
18560
+ mediaDropped: number().int().nonnegative(),
18561
+ tracks: number().int().nonnegative(),
18562
+ events: number().int().nonnegative(),
18563
+ startedAtMs: number().int().nullable(),
18564
+ finishedAtMs: number().int().nullable(),
18565
+ error: string().nullable()
18566
+ });
18498
18567
  var CameraStatusSchema = object({
18499
18568
  deviceId: number(),
18500
18569
  assignment: CameraAssignmentStatusSchema,
@@ -18726,7 +18795,10 @@ method(StorageMigrationLeaseInputSchema, object({ paused: literal(true) }), {
18726
18795
  }), CameraSwitchGroupSchema, {
18727
18796
  kind: "mutation",
18728
18797
  auth: "admin"
18729
- }), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
18798
+ }), method(object({ deviceId: number() }), CameraStatusSchema), method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), DiskReconcileJobSchema, {
18799
+ kind: "mutation",
18800
+ auth: "admin"
18801
+ }), method(_void(), DiskReconcileJobSchema, { auth: "admin" }), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
18730
18802
  name: string(),
18731
18803
  description: string().optional(),
18732
18804
  config: CameraPipelineConfigSchema
@@ -19131,7 +19203,14 @@ targets: array(object({
19131
19203
  "sleeping",
19132
19204
  "unreachable",
19133
19205
  "waking"
19134
- ]).nullable()
19206
+ ]).nullable(),
19207
+ /** A battery camera (whatever its current state). An AWAKE battery
19208
+ * camera is deliberately NOT recaptured on the poll cadence — every
19209
+ * capture is a camera hit that would keep it out of sleep — so its
19210
+ * cached frame legitimately ages past the currency ceiling while
19211
+ * nothing is streaming. A surface must keep painting it (a fresh
19212
+ * frame is captured at each wake), not blank to "unavailable". */
19213
+ battery: boolean()
19135
19214
  })));
19136
19215
  /**
19137
19216
  * `sso-bridge` — internal hub-only cap that lets SSO-style auth
@@ -19837,20 +19916,6 @@ var VectorStatsResultSchema = object({
19837
19916
  exact: boolean()
19838
19917
  });
19839
19918
  method(VectorDeclareIndexInputSchema, _void(), { kind: "mutation" }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, { kind: "mutation" }), method(VectorQueryInputSchema, VectorQueryResultSchema), method(VectorGetInputSchema, VectorGetResultSchema), method(VectorDeleteInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, { kind: "mutation" }), method(VectorStatsInputSchema, VectorStatsResultSchema);
19840
- /**
19841
- * `videoclips` — the unified, navigable-clip surface for a camera.
19842
- *
19843
- * A device-scoped WRAPPER cap (like `pipeline-analytics`): exactly one active
19844
- * provider per device, substitutable. The DEFAULT provider (registered by
19845
- * `addon-post-analysis`, `defaultActive: true`) composes the analytics event
19846
- * log (markers + thumbnails) with the recorder's `getPlaybackManifest` — a clip
19847
- * is a time-WINDOW over existing footage, never a separate file. A camera that
19848
- * exposes NATIVE onboard clips (Reolink/Hikvision NVR) can later substitute the
19849
- * wrapper provider for its device and serve its own clip catalog + URLs.
19850
- *
19851
- * A `Clip` is purely time-based (subtree-blind): playback resolves segments by
19852
- * temporal overlap, so the API never decides `continuous` vs `events`.
19853
- */
19854
19919
  var ClipSchema = object({
19855
19920
  /** Opaque, provider-namespaced id. The default provider encodes the time
19856
19921
  * window so `getClipPlayback` is self-contained (no event re-query). */
@@ -19867,8 +19932,57 @@ var ClipSchema = object({
19867
19932
  startMs: number(),
19868
19933
  endMs: number()
19869
19934
  }),
19870
- /** Thumbnail URL (lazy; e.g. analytics `getEventMedia`). Never inlined. */
19871
- thumbnail: string().optional()
19935
+ /**
19936
+ * Lazy thumbnail URL, never inlined.
19937
+ *
19938
+ * Recording-derived clips (events-mode keep-window, and the prepared
19939
+ * continuous event+fragment visit) MUST use the snapshot of the **main
19940
+ * event of the interval** — `getEventMedia({ eventId, kind: 'snapshot' })`
19941
+ * of the event that owns `kind` (object > motion > audio). Do not extract
19942
+ * a keyframe from the recorded segments. Other providers (onboard, HKSV)
19943
+ * mint their own stills.
19944
+ *
19945
+ * **VOUCHED, never fabricated.** A provider emits this only for an event it
19946
+ * has CONFIRMED owns at least one media row (its own, or its owning track's).
19947
+ * Absent is meaningful — "this visit has no event still" — never "we did not
19948
+ * look". Stamping it from a URL template made 35% of one camera's clips point
19949
+ * at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
19950
+ * A read that FAILS drops the claim; it never invents it.
19951
+ */
19952
+ thumbnail: string().optional(),
19953
+ /**
19954
+ * An instant INSIDE this visit's footage, hole-safe, where a recorded still
19955
+ * can be decoded. Present whenever the visit came from recorded availability;
19956
+ * absent on a per-event padded window (there is no footage to promise).
19957
+ *
19958
+ * This is not a thumbnail and not a second byte path: it is the argument to
19959
+ * the recorder's existing still route. The surface — never the provider —
19960
+ * decides whether to use it. It is the midpoint of the visit's LONGEST
19961
+ * contiguous range, not of the visit: a visit spans its holes by
19962
+ * construction, so a naive midpoint lands in dead air.
19963
+ */
19964
+ stillAtMs: number().optional(),
19965
+ /**
19966
+ * Analytics event ids that overlap this visit — a BOUNDED sample, newest
19967
+ * first within kind (object → motion → audio), capped at
19968
+ * {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
19969
+ *
19970
+ * Bounded because it is not a payload the surface pages through: one visit on
19971
+ * device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
19972
+ * camera-day. Read {@link eventCount} for the true total.
19973
+ */
19974
+ eventIds: array(string()).optional(),
19975
+ /** How many analytics events actually overlap this visit. Differs from
19976
+ * `eventIds.length` exactly when the sample was capped — so a truncated
19977
+ * list is never mistaken for a quiet visit. */
19978
+ eventCount: number().int().nonnegative().optional(),
19979
+ /** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
19980
+ * than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
19981
+ * bar keeps showing them via `recording.getAvailability`. */
19982
+ holes: array(object({
19983
+ startMs: number(),
19984
+ endMs: number()
19985
+ })).optional()
19872
19986
  });
19873
19987
  var ClipPlaybackSchema = object({
19874
19988
  /** HLS master URL through the hub data-plane (Range + token in path). */
@@ -20332,7 +20446,14 @@ var SearchResultSchema = object({
20332
20446
  });
20333
20447
  var AutoUpdateSettingsSchema = object({
20334
20448
  channel: ChannelSchema,
20335
- intervalSeconds: number()
20449
+ intervalSeconds: number(),
20450
+ /**
20451
+ * Cadence of the "an update exists" POLLER, in seconds. Independent of
20452
+ * `channel`: the poller runs while auto-apply is `off`, because being told
20453
+ * about a publish and installing it are different decisions. Clamped
20454
+ * server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
20455
+ */
20456
+ updateCheckIntervalSeconds: number()
20336
20457
  });
20337
20458
  var AddonAutoUpdateSchema = ChannelWithInheritSchema;
20338
20459
  var RestartAddonResultSchema = unknown();
@@ -20473,7 +20594,9 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
20473
20594
  auth: "admin"
20474
20595
  }), method(_void(), AutoUpdateSettingsSchema, { auth: "admin" }), method(object({
20475
20596
  channel: ChannelSchema,
20476
- intervalSeconds: number().min(300).max(86400).optional()
20597
+ intervalSeconds: number().min(300).max(86400).optional(),
20598
+ /** Availability-poll cadence; see AutoUpdateSettingsSchema. */
20599
+ updateCheckIntervalSeconds: number().min(900).max(604800).optional()
20477
20600
  }), unknown(), {
20478
20601
  kind: "mutation",
20479
20602
  auth: "admin"
@@ -24466,7 +24589,9 @@ var ExportOptionsSchema = object({
24466
24589
  includeAudio: boolean(),
24467
24590
  maxLifeMs: number().int().positive(),
24468
24591
  deleteAfterDownload: boolean(),
24469
- title: string().max(200).optional()
24592
+ title: string().max(200).optional(),
24593
+ /** Notification-output target ids to ping when this export becomes ready. */
24594
+ notifyTargetIds: array(string().min(1)).max(20).optional()
24470
24595
  }).superRefine((v, ctx) => {
24471
24596
  if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
24472
24597
  code: ZodIssueCode.custom,
@@ -24525,10 +24650,18 @@ var ExportBytesSchema = object({
24525
24650
  });
24526
24651
  method(object({
24527
24652
  deviceId: number(),
24528
- profile: string(),
24653
+ /** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
24654
+ profile: string().optional(),
24655
+ profiles: array(string()).min(1).optional(),
24529
24656
  fromMs: number(),
24530
24657
  toMs: number(),
24531
24658
  options: ExportOptionsSchema
24659
+ }).superRefine((v, ctx) => {
24660
+ if ((v.profiles !== void 0 && v.profiles.length > 0 ? v.profiles : v.profile !== void 0 ? [v.profile] : []).length < 1) ctx.addIssue({
24661
+ code: ZodIssueCode.custom,
24662
+ message: "pass profiles[] (min 1) or legacy profile",
24663
+ path: ["profiles"]
24664
+ });
24532
24665
  }), ExportRecordSchema, {
24533
24666
  kind: "mutation",
24534
24667
  auth: "protected"
@@ -29128,6 +29261,12 @@ Object.freeze({
29128
29261
  addonId: null,
29129
29262
  access: "create"
29130
29263
  },
29264
+ "pipelineAnalytics.reconcileFromDisk": {
29265
+ capName: "pipeline-analytics",
29266
+ capScope: "device",
29267
+ addonId: null,
29268
+ access: "create"
29269
+ },
29131
29270
  "pipelineAnalytics.refreshStorageLocationsForMigration": {
29132
29271
  capName: "pipeline-analytics",
29133
29272
  capScope: "device",
@@ -29530,6 +29669,12 @@ Object.freeze({
29530
29669
  addonId: null,
29531
29670
  access: "view"
29532
29671
  },
29672
+ "pipelineOrchestrator.getReconcileFromDiskStatus": {
29673
+ capName: "pipeline-orchestrator",
29674
+ capScope: "system",
29675
+ addonId: null,
29676
+ access: "view"
29677
+ },
29533
29678
  "pipelineOrchestrator.listAgentSettings": {
29534
29679
  capName: "pipeline-orchestrator",
29535
29680
  capScope: "system",
@@ -29554,6 +29699,12 @@ Object.freeze({
29554
29699
  addonId: null,
29555
29700
  access: "create"
29556
29701
  },
29702
+ "pipelineOrchestrator.reconcileFromDisk": {
29703
+ capName: "pipeline-orchestrator",
29704
+ capScope: "system",
29705
+ addonId: null,
29706
+ access: "create"
29707
+ },
29557
29708
  "pipelineOrchestrator.removeAgentSettings": {
29558
29709
  capName: "pipeline-orchestrator",
29559
29710
  capScope: "system",
@@ -32545,6 +32696,11 @@ Object.freeze({
32545
32696
  form: "single",
32546
32697
  optional: true
32547
32698
  }],
32699
+ "pipelineAnalytics.reconcileFromDisk": [{
32700
+ name: "deviceId",
32701
+ form: "single",
32702
+ optional: false
32703
+ }],
32548
32704
  "pipelineAnalytics.restageRetrainTrack": [{
32549
32705
  name: "deviceId",
32550
32706
  form: "single",
@@ -33610,6 +33766,8 @@ DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb;
33610
33766
  DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
33611
33767
  DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
33612
33768
  DEFAULT_NATIVE_LEASE_SETTINGS.admission;
33769
+ var MB = 1024 * 1024;
33770
+ 1024 * MB, 3072 * MB;
33613
33771
  //#endregion
33614
33772
  //#region src/shared.ts
33615
33773
  /**
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_shared = require("./shared-C48hU09g.js");
5
+ const require_shared = require("./shared-CGc-WGwO.js");
6
6
  let node_stream = require("node:stream");
7
7
  let webdav = require("webdav");
8
8
  //#region src/providers/webdav/webdav-config-schema.ts
@@ -1,4 +1,4 @@
1
- import { a as safeJoinRemotePath, c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, r as getRequiredBasePath, s as storageProviderCapability, t as createSessionId } from "./shared-Cud38F0U.mjs";
1
+ import { a as safeJoinRemotePath, c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, r as getRequiredBasePath, s as storageProviderCapability, t as createSessionId } from "./shared-C-bqmSqc.mjs";
2
2
  import { PassThrough } from "node:stream";
3
3
  import { AuthType, createClient } from "webdav";
4
4
  //#region src/providers/webdav/webdav-config-schema.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-remote-storage",
3
- "version": "1.2.18",
3
+ "version": "1.2.20",
4
4
  "description": "Remote storage providers (SFTP, S3, WebDAV) — unifies remote backends behind the storage-provider cap",
5
5
  "keywords": [
6
6
  "camstack",