@camstack/addon-provider-onvif 1.2.50 → 1.2.52

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/addon.js CHANGED
@@ -7717,7 +7717,8 @@ var OpsLogReasonSchema = _enum([
7717
7717
  "quota",
7718
7718
  "manual",
7719
7719
  "operator",
7720
- "maintenance"
7720
+ "maintenance",
7721
+ "orphaned-device"
7721
7722
  ]);
7722
7723
  /** One audit row, shared verbatim by both domains. */
7723
7724
  var OpsLogEntrySchema = object({
@@ -12803,10 +12804,39 @@ var DevicePersistConfigPayloadSchema = object({
12803
12804
  deviceId: number(),
12804
12805
  data: record(string(), unknown())
12805
12806
  });
12807
+ /** What a migration actually did, per switch. `unreachable` is a first-class
12808
+ * answer: a camera that could not be asked is not a camera that was silenced. */
12809
+ var MigrateSwitchOutcomeSchema = _enum([
12810
+ "off",
12811
+ "on",
12812
+ "not-offered",
12813
+ "unreachable"
12814
+ ]);
12815
+ var MigrateSwitchReportSchema = object({
12816
+ deviceId: number(),
12817
+ switchId: CameraSwitchIdSchema,
12818
+ outcome: MigrateSwitchOutcomeSchema,
12819
+ detail: string().optional()
12820
+ });
12821
+ var MigrateDeviceResultSchema = object({
12822
+ sourceId: number(),
12823
+ targetId: number(),
12824
+ switches: array(MigrateSwitchReportSchema).readonly(),
12825
+ /** Switches that could NOT be turned off on the source. Empty is the only
12826
+ * value meaning the replaced hardware is quiet. */
12827
+ sourceStillLive: array(CameraSwitchIdSchema).readonly(),
12828
+ swapped: boolean()
12829
+ });
12806
12830
  method(object({
12807
12831
  addonId: string(),
12808
12832
  stableId: string()
12809
- }), object({ id: number() }), { kind: "mutation" }), method(DeviceRegisterPayloadSchema, _void(), { kind: "mutation" }), method(DeviceRemovePayloadSchema, _void(), { kind: "mutation" }), method(DevicePersistConfigPayloadSchema, _void(), { kind: "mutation" }), method(object({ deviceId: number() }), record(string(), unknown())), method(object({ deviceId: number() }), record(string(), unknown())), method(object({ deviceId: number() }), DeviceMetaSchema.nullable()), method(object({
12833
+ }), object({ id: number() }), { kind: "mutation" }), method(object({
12834
+ sourceId: number(),
12835
+ targetId: number()
12836
+ }), MigrateDeviceResultSchema, {
12837
+ kind: "mutation",
12838
+ auth: "admin"
12839
+ }), method(DeviceRegisterPayloadSchema, _void(), { kind: "mutation" }), method(DeviceRemovePayloadSchema, _void(), { kind: "mutation" }), method(DevicePersistConfigPayloadSchema, _void(), { kind: "mutation" }), method(object({ deviceId: number() }), record(string(), unknown())), method(object({ deviceId: number() }), record(string(), unknown())), method(object({ deviceId: number() }), DeviceMetaSchema.nullable()), method(object({
12810
12840
  deviceId: number(),
12811
12841
  name: string()
12812
12842
  }), _void(), {
@@ -18903,6 +18933,34 @@ var KeyEventsForDeviceSchema = object({
18903
18933
  deviceId: number(),
18904
18934
  events: array(KeyEventSchema).readonly()
18905
18935
  });
18936
+ /** One non-empty bucket of the timeline histogram. */
18937
+ var EventDensityBucketSchema = object({
18938
+ bucketStart: number(),
18939
+ motion: number().int(),
18940
+ object: number().int(),
18941
+ audio: number().int()
18942
+ });
18943
+ /**
18944
+ * One camera's row in a `getEventDensityBatch` answer.
18945
+ *
18946
+ * TWO facts, and the timeline draws them differently:
18947
+ *
18948
+ * - `read: 'read'` — the histogram for this camera. `buckets: []` is a real
18949
+ * claim: read, and nothing happened in the window.
18950
+ * - `read: 'unreadable'` — nobody could answer for this camera. `buckets` is
18951
+ * empty and that emptiness means NOTHING; the timeline must render unknown,
18952
+ * not quiet.
18953
+ *
18954
+ * Fanned out per camera the difference was free — one query rejected while the
18955
+ * others resolved — and collapsing to a batch is the exact moment it is lost
18956
+ * silently. Every requested id gets a row: a caller that asked for twelve and
18957
+ * got eleven cannot tell which one is missing, or that any is.
18958
+ */
18959
+ var EventDensityForDeviceSchema = object({
18960
+ deviceId: number(),
18961
+ read: _enum(["read", "unreadable"]),
18962
+ buckets: array(EventDensityBucketSchema).readonly()
18963
+ });
18906
18964
  object({
18907
18965
  trackId: string(),
18908
18966
  className: string(),
@@ -19213,12 +19271,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19213
19271
  since: number(),
19214
19272
  until: number(),
19215
19273
  bucketMs: number().int().positive()
19216
- }), array(object({
19217
- bucketStart: number(),
19218
- motion: number().int(),
19219
- object: number().int(),
19220
- audio: number().int()
19221
- })).readonly()), method(object({
19274
+ }), array(EventDensityBucketSchema).readonly()), method(object({
19275
+ deviceIds: array(number()).min(1).max(200),
19276
+ since: number(),
19277
+ until: number(),
19278
+ bucketMs: number().int().positive()
19279
+ }), array(EventDensityForDeviceSchema).readonly()), method(object({
19222
19280
  deviceId: number(),
19223
19281
  cutoffMs: number()
19224
19282
  }), object({
@@ -26674,6 +26732,39 @@ var RecordingDaysSchema = object({
26674
26732
  /** Local-midnight epochs (UTC ms) of days that have ≥1 recorded segment. */
26675
26733
  days: array(number())
26676
26734
  });
26735
+ /**
26736
+ * One camera's row in a `getAvailabilityBatch` answer.
26737
+ *
26738
+ * `ranges` is EXACTLY what `getAvailability` returns for that camera — the
26739
+ * batch collapses the transport, not the work — plus the one thing the singular
26740
+ * method never had to say:
26741
+ *
26742
+ * - `read: 'read'` — answered. `ranges: []` means "read, and this camera has
26743
+ * no footage in the window", which is a real claim.
26744
+ * - `read: 'unreadable'` — the recorder could not answer for this camera (its
26745
+ * calendar rung threw, its location is unmounted). `ranges` is empty and
26746
+ * that emptiness means NOTHING.
26747
+ *
26748
+ * A timeline that renders the second as the first tells an operator there is no
26749
+ * footage when the truth is that nobody looked.
26750
+ */
26751
+ var RecordingAvailabilityForDeviceSchema = object({
26752
+ deviceId: number(),
26753
+ read: _enum(["read", "unreadable"]),
26754
+ ranges: array(RecordingRangeSchema).readonly()
26755
+ });
26756
+ /**
26757
+ * One camera's row in a `getDaysWithRecordingsBatch` answer. Same rule as
26758
+ * {@link RecordingAvailabilityForDeviceSchema}: `days: []` on a `'read'` row is
26759
+ * "no footage in this month", `read: 'unreadable'` is "nobody could look", and
26760
+ * the date-picker's day dots must not spell them the same.
26761
+ */
26762
+ var RecordingDaysForDeviceSchema = object({
26763
+ deviceId: number(),
26764
+ read: _enum(["read", "unreadable"]),
26765
+ /** Local-midnight epochs (UTC ms) of days that have ≥1 recorded segment. */
26766
+ days: array(number()).readonly()
26767
+ });
26677
26768
  var RecordingManifestSchema = object({
26678
26769
  deviceId: number(),
26679
26770
  /** Local filesystem path to the master playlist; null when no recording exists for the requested range. */
@@ -26908,6 +26999,13 @@ method(object({
26908
26999
  }), RecordingAvailabilitySchema, {
26909
27000
  kind: "query",
26910
27001
  auth: "protected"
27002
+ }), method(object({
27003
+ deviceIds: array(number()).min(1).max(200),
27004
+ fromMs: number(),
27005
+ toMs: number()
27006
+ }), array(RecordingAvailabilityForDeviceSchema).readonly(), {
27007
+ kind: "query",
27008
+ auth: "protected"
26911
27009
  }), method(object({
26912
27010
  deviceId: number(),
26913
27011
  fromMs: number(),
@@ -26916,6 +27014,14 @@ method(object({
26916
27014
  }), RecordingDaysSchema, {
26917
27015
  kind: "query",
26918
27016
  auth: "protected"
27017
+ }), method(object({
27018
+ deviceIds: array(number()).min(1).max(200),
27019
+ fromMs: number(),
27020
+ toMs: number(),
27021
+ tzOffsetMinutes: number()
27022
+ }), array(RecordingDaysForDeviceSchema).readonly(), {
27023
+ kind: "query",
27024
+ auth: "protected"
26919
27025
  }), method(object({
26920
27026
  deviceId: number(),
26921
27027
  fromMs: number(),
@@ -30789,6 +30895,12 @@ Object.freeze({
30789
30895
  addonId: null,
30790
30896
  access: "view"
30791
30897
  },
30898
+ "deviceManager.migrateDevice": {
30899
+ capName: "device-manager",
30900
+ capScope: "system",
30901
+ addonId: null,
30902
+ access: "create"
30903
+ },
30792
30904
  "deviceManager.persistConfig": {
30793
30905
  capName: "device-manager",
30794
30906
  capScope: "system",
@@ -32523,6 +32635,12 @@ Object.freeze({
32523
32635
  addonId: null,
32524
32636
  access: "view"
32525
32637
  },
32638
+ "pipelineAnalytics.getEventDensityBatch": {
32639
+ capName: "pipeline-analytics",
32640
+ capScope: "device",
32641
+ addonId: null,
32642
+ access: "view"
32643
+ },
32526
32644
  "pipelineAnalytics.getEventMedia": {
32527
32645
  capName: "pipeline-analytics",
32528
32646
  capScope: "device",
@@ -33645,12 +33763,24 @@ Object.freeze({
33645
33763
  addonId: null,
33646
33764
  access: "view"
33647
33765
  },
33766
+ "recording.getAvailabilityBatch": {
33767
+ capName: "recording",
33768
+ capScope: "system",
33769
+ addonId: null,
33770
+ access: "view"
33771
+ },
33648
33772
  "recording.getDaysWithRecordings": {
33649
33773
  capName: "recording",
33650
33774
  capScope: "system",
33651
33775
  addonId: null,
33652
33776
  access: "view"
33653
33777
  },
33778
+ "recording.getDaysWithRecordingsBatch": {
33779
+ capName: "recording",
33780
+ capScope: "system",
33781
+ addonId: null,
33782
+ access: "view"
33783
+ },
33654
33784
  "recording.getDeviceConfig": {
33655
33785
  capName: "recording",
33656
33786
  capScope: "system",
@@ -36255,6 +36385,11 @@ Object.freeze({
36255
36385
  form: "single",
36256
36386
  optional: false
36257
36387
  }],
36388
+ "pipelineAnalytics.getEventDensityBatch": [{
36389
+ name: "deviceIds",
36390
+ form: "array",
36391
+ optional: false
36392
+ }],
36258
36393
  "pipelineAnalytics.getEventMedia": [{
36259
36394
  name: "deviceId",
36260
36395
  form: "single",
@@ -36670,11 +36805,21 @@ Object.freeze({
36670
36805
  form: "single",
36671
36806
  optional: false
36672
36807
  }],
36808
+ "recording.getAvailabilityBatch": [{
36809
+ name: "deviceIds",
36810
+ form: "array",
36811
+ optional: false
36812
+ }],
36673
36813
  "recording.getDaysWithRecordings": [{
36674
36814
  name: "deviceId",
36675
36815
  form: "single",
36676
36816
  optional: false
36677
36817
  }],
36818
+ "recording.getDaysWithRecordingsBatch": [{
36819
+ name: "deviceIds",
36820
+ form: "array",
36821
+ optional: false
36822
+ }],
36678
36823
  "recording.getDeviceConfig": [{
36679
36824
  name: "deviceId",
36680
36825
  form: "single",
package/dist/addon.mjs CHANGED
@@ -7718,7 +7718,8 @@ var OpsLogReasonSchema = _enum([
7718
7718
  "quota",
7719
7719
  "manual",
7720
7720
  "operator",
7721
- "maintenance"
7721
+ "maintenance",
7722
+ "orphaned-device"
7722
7723
  ]);
7723
7724
  /** One audit row, shared verbatim by both domains. */
7724
7725
  var OpsLogEntrySchema = object({
@@ -12804,10 +12805,39 @@ var DevicePersistConfigPayloadSchema = object({
12804
12805
  deviceId: number(),
12805
12806
  data: record(string(), unknown())
12806
12807
  });
12808
+ /** What a migration actually did, per switch. `unreachable` is a first-class
12809
+ * answer: a camera that could not be asked is not a camera that was silenced. */
12810
+ var MigrateSwitchOutcomeSchema = _enum([
12811
+ "off",
12812
+ "on",
12813
+ "not-offered",
12814
+ "unreachable"
12815
+ ]);
12816
+ var MigrateSwitchReportSchema = object({
12817
+ deviceId: number(),
12818
+ switchId: CameraSwitchIdSchema,
12819
+ outcome: MigrateSwitchOutcomeSchema,
12820
+ detail: string().optional()
12821
+ });
12822
+ var MigrateDeviceResultSchema = object({
12823
+ sourceId: number(),
12824
+ targetId: number(),
12825
+ switches: array(MigrateSwitchReportSchema).readonly(),
12826
+ /** Switches that could NOT be turned off on the source. Empty is the only
12827
+ * value meaning the replaced hardware is quiet. */
12828
+ sourceStillLive: array(CameraSwitchIdSchema).readonly(),
12829
+ swapped: boolean()
12830
+ });
12807
12831
  method(object({
12808
12832
  addonId: string(),
12809
12833
  stableId: string()
12810
- }), object({ id: number() }), { kind: "mutation" }), method(DeviceRegisterPayloadSchema, _void(), { kind: "mutation" }), method(DeviceRemovePayloadSchema, _void(), { kind: "mutation" }), method(DevicePersistConfigPayloadSchema, _void(), { kind: "mutation" }), method(object({ deviceId: number() }), record(string(), unknown())), method(object({ deviceId: number() }), record(string(), unknown())), method(object({ deviceId: number() }), DeviceMetaSchema.nullable()), method(object({
12834
+ }), object({ id: number() }), { kind: "mutation" }), method(object({
12835
+ sourceId: number(),
12836
+ targetId: number()
12837
+ }), MigrateDeviceResultSchema, {
12838
+ kind: "mutation",
12839
+ auth: "admin"
12840
+ }), method(DeviceRegisterPayloadSchema, _void(), { kind: "mutation" }), method(DeviceRemovePayloadSchema, _void(), { kind: "mutation" }), method(DevicePersistConfigPayloadSchema, _void(), { kind: "mutation" }), method(object({ deviceId: number() }), record(string(), unknown())), method(object({ deviceId: number() }), record(string(), unknown())), method(object({ deviceId: number() }), DeviceMetaSchema.nullable()), method(object({
12811
12841
  deviceId: number(),
12812
12842
  name: string()
12813
12843
  }), _void(), {
@@ -18904,6 +18934,34 @@ var KeyEventsForDeviceSchema = object({
18904
18934
  deviceId: number(),
18905
18935
  events: array(KeyEventSchema).readonly()
18906
18936
  });
18937
+ /** One non-empty bucket of the timeline histogram. */
18938
+ var EventDensityBucketSchema = object({
18939
+ bucketStart: number(),
18940
+ motion: number().int(),
18941
+ object: number().int(),
18942
+ audio: number().int()
18943
+ });
18944
+ /**
18945
+ * One camera's row in a `getEventDensityBatch` answer.
18946
+ *
18947
+ * TWO facts, and the timeline draws them differently:
18948
+ *
18949
+ * - `read: 'read'` — the histogram for this camera. `buckets: []` is a real
18950
+ * claim: read, and nothing happened in the window.
18951
+ * - `read: 'unreadable'` — nobody could answer for this camera. `buckets` is
18952
+ * empty and that emptiness means NOTHING; the timeline must render unknown,
18953
+ * not quiet.
18954
+ *
18955
+ * Fanned out per camera the difference was free — one query rejected while the
18956
+ * others resolved — and collapsing to a batch is the exact moment it is lost
18957
+ * silently. Every requested id gets a row: a caller that asked for twelve and
18958
+ * got eleven cannot tell which one is missing, or that any is.
18959
+ */
18960
+ var EventDensityForDeviceSchema = object({
18961
+ deviceId: number(),
18962
+ read: _enum(["read", "unreadable"]),
18963
+ buckets: array(EventDensityBucketSchema).readonly()
18964
+ });
18907
18965
  object({
18908
18966
  trackId: string(),
18909
18967
  className: string(),
@@ -19214,12 +19272,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19214
19272
  since: number(),
19215
19273
  until: number(),
19216
19274
  bucketMs: number().int().positive()
19217
- }), array(object({
19218
- bucketStart: number(),
19219
- motion: number().int(),
19220
- object: number().int(),
19221
- audio: number().int()
19222
- })).readonly()), method(object({
19275
+ }), array(EventDensityBucketSchema).readonly()), method(object({
19276
+ deviceIds: array(number()).min(1).max(200),
19277
+ since: number(),
19278
+ until: number(),
19279
+ bucketMs: number().int().positive()
19280
+ }), array(EventDensityForDeviceSchema).readonly()), method(object({
19223
19281
  deviceId: number(),
19224
19282
  cutoffMs: number()
19225
19283
  }), object({
@@ -26675,6 +26733,39 @@ var RecordingDaysSchema = object({
26675
26733
  /** Local-midnight epochs (UTC ms) of days that have ≥1 recorded segment. */
26676
26734
  days: array(number())
26677
26735
  });
26736
+ /**
26737
+ * One camera's row in a `getAvailabilityBatch` answer.
26738
+ *
26739
+ * `ranges` is EXACTLY what `getAvailability` returns for that camera — the
26740
+ * batch collapses the transport, not the work — plus the one thing the singular
26741
+ * method never had to say:
26742
+ *
26743
+ * - `read: 'read'` — answered. `ranges: []` means "read, and this camera has
26744
+ * no footage in the window", which is a real claim.
26745
+ * - `read: 'unreadable'` — the recorder could not answer for this camera (its
26746
+ * calendar rung threw, its location is unmounted). `ranges` is empty and
26747
+ * that emptiness means NOTHING.
26748
+ *
26749
+ * A timeline that renders the second as the first tells an operator there is no
26750
+ * footage when the truth is that nobody looked.
26751
+ */
26752
+ var RecordingAvailabilityForDeviceSchema = object({
26753
+ deviceId: number(),
26754
+ read: _enum(["read", "unreadable"]),
26755
+ ranges: array(RecordingRangeSchema).readonly()
26756
+ });
26757
+ /**
26758
+ * One camera's row in a `getDaysWithRecordingsBatch` answer. Same rule as
26759
+ * {@link RecordingAvailabilityForDeviceSchema}: `days: []` on a `'read'` row is
26760
+ * "no footage in this month", `read: 'unreadable'` is "nobody could look", and
26761
+ * the date-picker's day dots must not spell them the same.
26762
+ */
26763
+ var RecordingDaysForDeviceSchema = object({
26764
+ deviceId: number(),
26765
+ read: _enum(["read", "unreadable"]),
26766
+ /** Local-midnight epochs (UTC ms) of days that have ≥1 recorded segment. */
26767
+ days: array(number()).readonly()
26768
+ });
26678
26769
  var RecordingManifestSchema = object({
26679
26770
  deviceId: number(),
26680
26771
  /** Local filesystem path to the master playlist; null when no recording exists for the requested range. */
@@ -26909,6 +27000,13 @@ method(object({
26909
27000
  }), RecordingAvailabilitySchema, {
26910
27001
  kind: "query",
26911
27002
  auth: "protected"
27003
+ }), method(object({
27004
+ deviceIds: array(number()).min(1).max(200),
27005
+ fromMs: number(),
27006
+ toMs: number()
27007
+ }), array(RecordingAvailabilityForDeviceSchema).readonly(), {
27008
+ kind: "query",
27009
+ auth: "protected"
26912
27010
  }), method(object({
26913
27011
  deviceId: number(),
26914
27012
  fromMs: number(),
@@ -26917,6 +27015,14 @@ method(object({
26917
27015
  }), RecordingDaysSchema, {
26918
27016
  kind: "query",
26919
27017
  auth: "protected"
27018
+ }), method(object({
27019
+ deviceIds: array(number()).min(1).max(200),
27020
+ fromMs: number(),
27021
+ toMs: number(),
27022
+ tzOffsetMinutes: number()
27023
+ }), array(RecordingDaysForDeviceSchema).readonly(), {
27024
+ kind: "query",
27025
+ auth: "protected"
26920
27026
  }), method(object({
26921
27027
  deviceId: number(),
26922
27028
  fromMs: number(),
@@ -30790,6 +30896,12 @@ Object.freeze({
30790
30896
  addonId: null,
30791
30897
  access: "view"
30792
30898
  },
30899
+ "deviceManager.migrateDevice": {
30900
+ capName: "device-manager",
30901
+ capScope: "system",
30902
+ addonId: null,
30903
+ access: "create"
30904
+ },
30793
30905
  "deviceManager.persistConfig": {
30794
30906
  capName: "device-manager",
30795
30907
  capScope: "system",
@@ -32524,6 +32636,12 @@ Object.freeze({
32524
32636
  addonId: null,
32525
32637
  access: "view"
32526
32638
  },
32639
+ "pipelineAnalytics.getEventDensityBatch": {
32640
+ capName: "pipeline-analytics",
32641
+ capScope: "device",
32642
+ addonId: null,
32643
+ access: "view"
32644
+ },
32527
32645
  "pipelineAnalytics.getEventMedia": {
32528
32646
  capName: "pipeline-analytics",
32529
32647
  capScope: "device",
@@ -33646,12 +33764,24 @@ Object.freeze({
33646
33764
  addonId: null,
33647
33765
  access: "view"
33648
33766
  },
33767
+ "recording.getAvailabilityBatch": {
33768
+ capName: "recording",
33769
+ capScope: "system",
33770
+ addonId: null,
33771
+ access: "view"
33772
+ },
33649
33773
  "recording.getDaysWithRecordings": {
33650
33774
  capName: "recording",
33651
33775
  capScope: "system",
33652
33776
  addonId: null,
33653
33777
  access: "view"
33654
33778
  },
33779
+ "recording.getDaysWithRecordingsBatch": {
33780
+ capName: "recording",
33781
+ capScope: "system",
33782
+ addonId: null,
33783
+ access: "view"
33784
+ },
33655
33785
  "recording.getDeviceConfig": {
33656
33786
  capName: "recording",
33657
33787
  capScope: "system",
@@ -36256,6 +36386,11 @@ Object.freeze({
36256
36386
  form: "single",
36257
36387
  optional: false
36258
36388
  }],
36389
+ "pipelineAnalytics.getEventDensityBatch": [{
36390
+ name: "deviceIds",
36391
+ form: "array",
36392
+ optional: false
36393
+ }],
36259
36394
  "pipelineAnalytics.getEventMedia": [{
36260
36395
  name: "deviceId",
36261
36396
  form: "single",
@@ -36671,11 +36806,21 @@ Object.freeze({
36671
36806
  form: "single",
36672
36807
  optional: false
36673
36808
  }],
36809
+ "recording.getAvailabilityBatch": [{
36810
+ name: "deviceIds",
36811
+ form: "array",
36812
+ optional: false
36813
+ }],
36674
36814
  "recording.getDaysWithRecordings": [{
36675
36815
  name: "deviceId",
36676
36816
  form: "single",
36677
36817
  optional: false
36678
36818
  }],
36819
+ "recording.getDaysWithRecordingsBatch": [{
36820
+ name: "deviceIds",
36821
+ form: "array",
36822
+ optional: false
36823
+ }],
36679
36824
  "recording.getDeviceConfig": [{
36680
36825
  name: "deviceId",
36681
36826
  form: "single",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-onvif",
3
- "version": "1.2.50",
3
+ "version": "1.2.52",
4
4
  "description": "ONVIF camera device provider addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",