@camstack/addon-cloudflare 1.2.26 → 1.2.28

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.
@@ -5800,6 +5800,13 @@ var BaseAddon = class {
5800
5800
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5801
5801
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5802
5802
  _registeredCapNames = [];
5803
+ /**
5804
+ * True only after `readAddonStore` actually answered. Constructor
5805
+ * defaults look like stored config when the store is down — a forked
5806
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5807
+ * mode, 2026-08-25) is not "the operator chose this".
5808
+ */
5809
+ settingsStoreReady = false;
5803
5810
  /** Default config values. Provided via constructor. */
5804
5811
  defaults;
5805
5812
  constructor(defaults) {
@@ -6200,7 +6207,9 @@ var BaseAddon = class {
6200
6207
  ];
6201
6208
  let lastErr;
6202
6209
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6203
- return await settings.readAddonStore() ?? {};
6210
+ const stored = await settings.readAddonStore() ?? {};
6211
+ this.settingsStoreReady = true;
6212
+ return stored;
6204
6213
  } catch (err) {
6205
6214
  lastErr = err;
6206
6215
  const msg = err instanceof Error ? err.message : String(err);
@@ -6208,6 +6217,7 @@ var BaseAddon = class {
6208
6217
  if (attempt === delaysMs.length) break;
6209
6218
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6210
6219
  }
6220
+ this.settingsStoreReady = false;
6211
6221
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6212
6222
  return {};
6213
6223
  }
@@ -8095,6 +8105,12 @@ var ModelVariantGroupSchema = object({
8095
8105
  */
8096
8106
  resolution: number().int().positive().optional()
8097
8107
  });
8108
+ var ModelProviderIdSchema = _enum([
8109
+ "camstack",
8110
+ "frigate",
8111
+ "scrypted",
8112
+ "custom"
8113
+ ]);
8098
8114
  var ModelCatalogEntrySchema = object({
8099
8115
  id: string(),
8100
8116
  name: string(),
@@ -8192,6 +8208,12 @@ var ModelCatalogEntrySchema = object({
8192
8208
  */
8193
8209
  group: ModelVariantGroupSchema.optional(),
8194
8210
  /**
8211
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8212
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8213
+ * persisted before this field existed (`inferModelProvider` fills those).
8214
+ */
8215
+ provider: ModelProviderIdSchema.optional(),
8216
+ /**
8195
8217
  * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8196
8218
  * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8197
8219
  * labels already ARE the CamStack macros (Scrypted identity map).
@@ -11741,6 +11763,27 @@ var LinkedDeviceSchema = object({
11741
11763
  features: array(string()),
11742
11764
  producesTrackedEvents: boolean().optional()
11743
11765
  });
11766
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
11767
+ * The batch answer needs the tag; the single-device answer already has it
11768
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
11769
+ var LinkedDevicesForDeviceSchema = object({
11770
+ deviceId: number(),
11771
+ mode: LinkedDevicesModeSchema,
11772
+ devices: array(LinkedDeviceSchema)
11773
+ });
11774
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
11775
+ * `getAllBindings` all answer in. Declared once: three copies of the same
11776
+ * object literal is exactly how the three drift apart. */
11777
+ var DeviceBindingsForDeviceSchema = object({
11778
+ deviceId: number(),
11779
+ entries: array(object({
11780
+ capName: string(),
11781
+ kind: _enum(["native", "wrapped"]),
11782
+ providerAddonId: string(),
11783
+ providerNodeId: string(),
11784
+ nativeAddonId: string()
11785
+ }))
11786
+ });
11744
11787
  var SavedDeviceRowSchema = object({
11745
11788
  /** Numeric id reserved at allocateDeviceId time. */
11746
11789
  id: number(),
@@ -11966,11 +12009,25 @@ method(object({
11966
12009
  projection: _enum(["full", "slim"]).optional(),
11967
12010
  /** Return only camera devices. Filtering server-side instead of
11968
12011
  * shipping 293 rows to find 12. */
11969
- isCamera: boolean().optional()
12012
+ isCamera: boolean().optional(),
12013
+ /**
12014
+ * Return only these device ids. For the caller that already KNOWS the
12015
+ * handful it wants and needs a field the id-bearing answer does not
12016
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12017
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12018
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12019
+ * refetches on the reconcile interval, on a phone.
12020
+ *
12021
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12022
+ * keys rather than rejecting them (verified against the live hub
12023
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12024
+ * it answers today and the caller filters as it already does.
12025
+ */
12026
+ deviceIds: array(number()).optional()
11970
12027
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
11971
12028
  mode: LinkedDevicesModeSchema,
11972
12029
  devices: array(LinkedDeviceSchema)
11973
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12030
+ })), method(object({ deviceIds: array(number()) }), array(LinkedDevicesForDeviceSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
11974
12031
  deviceId: number(),
11975
12032
  values: record(string(), unknown())
11976
12033
  }), object({ success: literal(true) }), {
@@ -11997,25 +12054,7 @@ method(object({
11997
12054
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
11998
12055
  kind: "mutation",
11999
12056
  auth: "admin"
12000
- }), method(object({ deviceId: number() }), object({
12001
- deviceId: number(),
12002
- entries: array(object({
12003
- capName: string(),
12004
- kind: _enum(["native", "wrapped"]),
12005
- providerAddonId: string(),
12006
- providerNodeId: string(),
12007
- nativeAddonId: string()
12008
- }))
12009
- })), method(object({}), array(object({
12010
- deviceId: number(),
12011
- entries: array(object({
12012
- capName: string(),
12013
- kind: _enum(["native", "wrapped"]),
12014
- providerAddonId: string(),
12015
- providerNodeId: string(),
12016
- nativeAddonId: string()
12017
- }))
12018
- }))), method(object({
12057
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
12019
12058
  deviceId: number(),
12020
12059
  capName: string(),
12021
12060
  wrapperAddonId: string(),
@@ -14404,12 +14443,15 @@ var NcOccupancyConditionSchema = object({
14404
14443
  * there is no second switch that can disagree with the first and every rule
14405
14444
  * authored before the decision migrates for free (`audioModeOf`):
14406
14445
  *
14407
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14408
- * classifier labels with one of them. No window, no percentage:
14409
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14410
- * `throttle` cooldown is the only brake. The per-label confidence floor is
14411
- * the analyzer's (`classificationMinScore`, per device) a label only
14412
- * reaches this condition if the classifier was already confident enough.
14446
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
14447
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
14448
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
14449
+ * frames is the wrong question for a classifier that labels 1–3 frames
14450
+ * per episode. The count window is the brake that drops a single-frame
14451
+ * false positive; the rule's own `throttle` cooldown is the other. The
14452
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
14453
+ * per device) — a label only reaches this condition if the classifier was
14454
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
14413
14455
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14414
14456
  * the condition: at least `hitPercent`% of the samples over
14415
14457
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -14436,14 +14478,22 @@ var NcOccupancyConditionSchema = object({
14436
14478
  * an operator who typed `dog` mean the same thing.
14437
14479
  */
14438
14480
  var NcAudioConditionSchema = object({
14439
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
14481
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
14440
14482
  labels: array(string().min(1)).min(1).optional(),
14441
14483
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14442
14484
  dbThreshold: number().min(-96).max(0).optional(),
14443
14485
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14444
14486
  hitPercent: number().int().min(1).max(100).default(60),
14445
14487
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14446
- samplingSeconds: number().int().min(1).max(300).default(10)
14488
+ samplingSeconds: number().int().min(1).max(300).default(10),
14489
+ /**
14490
+ * LABEL MODE: how many labelled frames must land inside
14491
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
14492
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
14493
+ */
14494
+ confirmHits: number().int().min(1).max(20).optional(),
14495
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
14496
+ confirmWindowSec: number().int().min(1).max(60).optional()
14447
14497
  });
14448
14498
  /**
14449
14499
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -16817,6 +16867,46 @@ var RecentTracksPageSchema = object({
16817
16867
  /** Cursor for the next page, or null when this page is the last. */
16818
16868
  nextCursor: string().nullable()
16819
16869
  });
16870
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
16871
+ var LIST_GROUPS_MAX_LIMIT = 100;
16872
+ var AnalyticsGroupRecordSchema = object({
16873
+ id: string(),
16874
+ deviceId: number().int(),
16875
+ openedAt: number().int(),
16876
+ closedAt: number().int(),
16877
+ timestamp: number().int(),
16878
+ memberCount: number().int(),
16879
+ memberTrackIds: array(string()).readonly(),
16880
+ className: string(),
16881
+ classes: array(string()).readonly(),
16882
+ /** Relative event-media path, or null when the group has no picture yet. */
16883
+ mediaUrl: string().nullable(),
16884
+ singleton: boolean()
16885
+ });
16886
+ var AnalyticsGroupMemberSchema = object({
16887
+ trackId: string(),
16888
+ deviceId: number().int(),
16889
+ className: string(),
16890
+ firstSeen: number().int(),
16891
+ lastSeen: number().int(),
16892
+ mediaUrl: string().nullable()
16893
+ });
16894
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
16895
+ var ListGroupsQueryInput = object({
16896
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
16897
+ deviceIds: array(number()),
16898
+ /** Window lower bound on `closedAt` (inclusive). */
16899
+ since: number().optional(),
16900
+ /** Window upper bound on `openedAt` (inclusive). */
16901
+ until: number().optional(),
16902
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
16903
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
16904
+ cursor: string().optional()
16905
+ });
16906
+ var ListGroupsPageSchema = object({
16907
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
16908
+ nextCursor: string().nullable()
16909
+ });
16820
16910
  var KeyEventQueryInput = object({
16821
16911
  deviceId: number(),
16822
16912
  /** Window lower bound (track firstSeen ≥ since). */
@@ -16892,7 +16982,9 @@ var TrackCascadeCountsSchema = object({
16892
16982
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
16893
16983
  plates: number().int(),
16894
16984
  /** Per-track CLIP search vectors removed (best-effort). */
16895
- embeddings: number().int()
16985
+ embeddings: number().int(),
16986
+ /** Group membership + group rows removed with their last member (best-effort). */
16987
+ groups: number().int()
16896
16988
  });
16897
16989
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
16898
16990
  var DiskReconcileCountsSchema = object({
@@ -17038,7 +17130,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17038
17130
  * stationary registry). Default false: the timeline lists passages,
17039
17131
  * not parking records (operator decision, 2026-08-15). */
17040
17132
  includeStationary: boolean().optional()
17041
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17133
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17134
+ deviceId: number(),
17135
+ groupId: string().min(1)
17136
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17042
17137
  kind: "mutation",
17043
17138
  auth: "admin"
17044
17139
  }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
@@ -17348,7 +17443,8 @@ var PipelineModelOptionSchema = object({
17348
17443
  sizeMB: number()
17349
17444
  })),
17350
17445
  group: ModelVariantGroupSchema.optional(),
17351
- legacy: boolean().optional()
17446
+ legacy: boolean().optional(),
17447
+ provider: ModelProviderIdSchema.optional()
17352
17448
  });
17353
17449
  var ConfigFieldBridge = custom();
17354
17450
  var PipelineAddonSchemaSchema = object({
@@ -23955,7 +24051,12 @@ var PlateInfoSchema = object({
23955
24051
  plateBbox: BoundingBoxSchema.optional(),
23956
24052
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
23957
24053
  keyFrameMediaKey: string().optional(),
23958
- base64: string().optional()
24054
+ base64: string().optional(),
24055
+ /**
24056
+ * Same crop as a data-plane URL. `getPlateByTrack` returns this and
24057
+ * never inlines JPEG; `listPlates` still inlines for the admin-ui.
24058
+ */
24059
+ cropUrl: string().optional()
23959
24060
  });
23960
24061
  var MediaFileLiteSchema = object({
23961
24062
  key: string(),
@@ -27595,6 +27696,12 @@ Object.freeze({
27595
27696
  addonId: null,
27596
27697
  access: "view"
27597
27698
  },
27699
+ "deviceManager.getBindingsBatch": {
27700
+ capName: "device-manager",
27701
+ capScope: "system",
27702
+ addonId: null,
27703
+ access: "view"
27704
+ },
27598
27705
  "deviceManager.getChildren": {
27599
27706
  capName: "device-manager",
27600
27707
  capScope: "system",
@@ -27655,6 +27762,12 @@ Object.freeze({
27655
27762
  addonId: null,
27656
27763
  access: "view"
27657
27764
  },
27765
+ "deviceManager.getLinkedDevicesBatch": {
27766
+ capName: "device-manager",
27767
+ capScope: "system",
27768
+ addonId: null,
27769
+ access: "view"
27770
+ },
27658
27771
  "deviceManager.getRoleDisplayDefaults": {
27659
27772
  capName: "device-manager",
27660
27773
  capScope: "system",
@@ -29425,6 +29538,12 @@ Object.freeze({
29425
29538
  addonId: null,
29426
29539
  access: "view"
29427
29540
  },
29541
+ "pipelineAnalytics.getGroup": {
29542
+ capName: "pipeline-analytics",
29543
+ capScope: "device",
29544
+ addonId: null,
29545
+ access: "view"
29546
+ },
29428
29547
  "pipelineAnalytics.getKeyEvents": {
29429
29548
  capName: "pipeline-analytics",
29430
29549
  capScope: "device",
@@ -29509,6 +29628,12 @@ Object.freeze({
29509
29628
  addonId: null,
29510
29629
  access: "view"
29511
29630
  },
29631
+ "pipelineAnalytics.listGroups": {
29632
+ capName: "pipeline-analytics",
29633
+ capScope: "device",
29634
+ addonId: null,
29635
+ access: "view"
29636
+ },
29512
29637
  "pipelineAnalytics.listOpsLog": {
29513
29638
  capName: "pipeline-analytics",
29514
29639
  capScope: "device",
@@ -32290,6 +32415,11 @@ Object.freeze({
32290
32415
  form: "single",
32291
32416
  optional: false
32292
32417
  }],
32418
+ "deviceManager.getBindingsBatch": [{
32419
+ name: "deviceIds",
32420
+ form: "array",
32421
+ optional: false
32422
+ }],
32293
32423
  "deviceManager.getChildren": [{
32294
32424
  name: "parentDeviceId",
32295
32425
  form: "single",
@@ -32335,6 +32465,11 @@ Object.freeze({
32335
32465
  form: "single",
32336
32466
  optional: false
32337
32467
  }],
32468
+ "deviceManager.getLinkedDevicesBatch": [{
32469
+ name: "deviceIds",
32470
+ form: "array",
32471
+ optional: false
32472
+ }],
32338
32473
  "deviceManager.getSettingsSchema": [{
32339
32474
  name: "deviceId",
32340
32475
  form: "single",
@@ -32355,6 +32490,11 @@ Object.freeze({
32355
32490
  form: "single",
32356
32491
  optional: false
32357
32492
  }],
32493
+ "deviceManager.listAll": [{
32494
+ name: "deviceIds",
32495
+ form: "array",
32496
+ optional: true
32497
+ }],
32358
32498
  "deviceManager.loadConfig": [{
32359
32499
  name: "deviceId",
32360
32500
  form: "single",
@@ -32928,6 +33068,11 @@ Object.freeze({
32928
33068
  form: "single",
32929
33069
  optional: false
32930
33070
  }],
33071
+ "pipelineAnalytics.getGroup": [{
33072
+ name: "deviceId",
33073
+ form: "single",
33074
+ optional: false
33075
+ }],
32931
33076
  "pipelineAnalytics.getKeyEvents": [{
32932
33077
  name: "deviceId",
32933
33078
  form: "single",
@@ -32983,6 +33128,11 @@ Object.freeze({
32983
33128
  form: "array",
32984
33129
  optional: false
32985
33130
  }],
33131
+ "pipelineAnalytics.listGroups": [{
33132
+ name: "deviceIds",
33133
+ form: "array",
33134
+ optional: false
33135
+ }],
32986
33136
  "pipelineAnalytics.listOpsLog": [{
32987
33137
  name: "deviceId",
32988
33138
  form: "single",
@@ -5800,6 +5800,13 @@ var BaseAddon = class {
5800
5800
  _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
5801
5801
  /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
5802
5802
  _registeredCapNames = [];
5803
+ /**
5804
+ * True only after `readAddonStore` actually answered. Constructor
5805
+ * defaults look like stored config when the store is down — a forked
5806
+ * addon that auto-starts from those defaults (cloudflare-tunnel quick
5807
+ * mode, 2026-08-25) is not "the operator chose this".
5808
+ */
5809
+ settingsStoreReady = false;
5803
5810
  /** Default config values. Provided via constructor. */
5804
5811
  defaults;
5805
5812
  constructor(defaults) {
@@ -6200,7 +6207,9 @@ var BaseAddon = class {
6200
6207
  ];
6201
6208
  let lastErr;
6202
6209
  for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
6203
- return await settings.readAddonStore() ?? {};
6210
+ const stored = await settings.readAddonStore() ?? {};
6211
+ this.settingsStoreReady = true;
6212
+ return stored;
6204
6213
  } catch (err) {
6205
6214
  lastErr = err;
6206
6215
  const msg = err instanceof Error ? err.message : String(err);
@@ -6208,6 +6217,7 @@ var BaseAddon = class {
6208
6217
  if (attempt === delaysMs.length) break;
6209
6218
  await new Promise((r) => setTimeout(r, delaysMs[attempt]));
6210
6219
  }
6220
+ this.settingsStoreReady = false;
6211
6221
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
6212
6222
  return {};
6213
6223
  }
@@ -8095,6 +8105,12 @@ var ModelVariantGroupSchema = object({
8095
8105
  */
8096
8106
  resolution: number().int().positive().optional()
8097
8107
  });
8108
+ var ModelProviderIdSchema = _enum([
8109
+ "camstack",
8110
+ "frigate",
8111
+ "scrypted",
8112
+ "custom"
8113
+ ]);
8098
8114
  var ModelCatalogEntrySchema = object({
8099
8115
  id: string(),
8100
8116
  name: string(),
@@ -8192,6 +8208,12 @@ var ModelCatalogEntrySchema = object({
8192
8208
  */
8193
8209
  group: ModelVariantGroupSchema.optional(),
8194
8210
  /**
8211
+ * Catalog source for the pipeline stepper's provider-first picker. Absent on
8212
+ * built-in CamStack entries (treated as `camstack`) and on registry rows
8213
+ * persisted before this field existed (`inferModelProvider` fills those).
8214
+ */
8215
+ provider: ModelProviderIdSchema.optional(),
8216
+ /**
8195
8217
  * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8196
8218
  * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8197
8219
  * labels already ARE the CamStack macros (Scrypted identity map).
@@ -11741,6 +11763,27 @@ var LinkedDeviceSchema = object({
11741
11763
  features: array(string()),
11742
11764
  producesTrackedEvents: boolean().optional()
11743
11765
  });
11766
+ /** One camera's resolved linked set, tagged with the camera it belongs to.
11767
+ * The batch answer needs the tag; the single-device answer already has it
11768
+ * from the input, which is why `getLinkedDevices` keeps the untagged shape. */
11769
+ var LinkedDevicesForDeviceSchema = object({
11770
+ deviceId: number(),
11771
+ mode: LinkedDevicesModeSchema,
11772
+ devices: array(LinkedDeviceSchema)
11773
+ });
11774
+ /** One device's binding map — the shape `getBindings`, `getBindingsBatch` and
11775
+ * `getAllBindings` all answer in. Declared once: three copies of the same
11776
+ * object literal is exactly how the three drift apart. */
11777
+ var DeviceBindingsForDeviceSchema = object({
11778
+ deviceId: number(),
11779
+ entries: array(object({
11780
+ capName: string(),
11781
+ kind: _enum(["native", "wrapped"]),
11782
+ providerAddonId: string(),
11783
+ providerNodeId: string(),
11784
+ nativeAddonId: string()
11785
+ }))
11786
+ });
11744
11787
  var SavedDeviceRowSchema = object({
11745
11788
  /** Numeric id reserved at allocateDeviceId time. */
11746
11789
  id: number(),
@@ -11966,11 +12009,25 @@ method(object({
11966
12009
  projection: _enum(["full", "slim"]).optional(),
11967
12010
  /** Return only camera devices. Filtering server-side instead of
11968
12011
  * shipping 293 rows to find 12. */
11969
- isCamera: boolean().optional()
12012
+ isCamera: boolean().optional(),
12013
+ /**
12014
+ * Return only these device ids. For the caller that already KNOWS the
12015
+ * handful it wants and needs a field the id-bearing answer does not
12016
+ * carry — the viewer's linked-devices panel joins `type` and `online`
12017
+ * onto ~8 linked ids and, unfiltered, dragged the fleet across to do
12018
+ * it: 433 KB slim / 958 KB full for 967 devices, on a query that
12019
+ * refetches on the reconcile interval, on a phone.
12020
+ *
12021
+ * Safe to send at a hub that predates it: Zod STRIPS unknown input
12022
+ * keys rather than rejecting them (verified against the live hub
12023
+ * 2026-08-25 — 967 rows came back), so an old hub answers exactly what
12024
+ * it answers today and the caller filters as it already does.
12025
+ */
12026
+ deviceIds: array(number()).optional()
11970
12027
  }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
11971
12028
  mode: LinkedDevicesModeSchema,
11972
12029
  devices: array(LinkedDeviceSchema)
11973
- })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
12030
+ })), method(object({ deviceIds: array(number()) }), array(LinkedDevicesForDeviceSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
11974
12031
  deviceId: number(),
11975
12032
  values: record(string(), unknown())
11976
12033
  }), object({ success: literal(true) }), {
@@ -11997,25 +12054,7 @@ method(object({
11997
12054
  }), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
11998
12055
  kind: "mutation",
11999
12056
  auth: "admin"
12000
- }), method(object({ deviceId: number() }), object({
12001
- deviceId: number(),
12002
- entries: array(object({
12003
- capName: string(),
12004
- kind: _enum(["native", "wrapped"]),
12005
- providerAddonId: string(),
12006
- providerNodeId: string(),
12007
- nativeAddonId: string()
12008
- }))
12009
- })), method(object({}), array(object({
12010
- deviceId: number(),
12011
- entries: array(object({
12012
- capName: string(),
12013
- kind: _enum(["native", "wrapped"]),
12014
- providerAddonId: string(),
12015
- providerNodeId: string(),
12016
- nativeAddonId: string()
12017
- }))
12018
- }))), method(object({
12057
+ }), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
12019
12058
  deviceId: number(),
12020
12059
  capName: string(),
12021
12060
  wrapperAddonId: string(),
@@ -14404,12 +14443,15 @@ var NcOccupancyConditionSchema = object({
14404
14443
  * there is no second switch that can disagree with the first and every rule
14405
14444
  * authored before the decision migrates for free (`audioModeOf`):
14406
14445
  *
14407
- * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14408
- * classifier labels with one of them. No window, no percentage:
14409
- * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14410
- * `throttle` cooldown is the only brake. The per-label confidence floor is
14411
- * the analyzer's (`classificationMinScore`, per device) a label only
14412
- * reaches this condition if the classifier was already confident enough.
14446
+ * - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
14447
+ * labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
14448
+ * `hitPercent` and `samplingSeconds` are still ignored a percentage of
14449
+ * frames is the wrong question for a classifier that labels 1–3 frames
14450
+ * per episode. The count window is the brake that drops a single-frame
14451
+ * false positive; the rule's own `throttle` cooldown is the other. The
14452
+ * per-label confidence floor is the analyzer's (`classificationMinScore`,
14453
+ * per device) — a label only reaches this condition if the classifier was
14454
+ * already confident enough. `confirmHits: 1` restores first-frame fire.
14413
14455
  * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14414
14456
  * the condition: at least `hitPercent`% of the samples over
14415
14457
  * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
@@ -14436,14 +14478,22 @@ var NcOccupancyConditionSchema = object({
14436
14478
  * an operator who typed `dog` mean the same thing.
14437
14479
  */
14438
14480
  var NcAudioConditionSchema = object({
14439
- /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
14481
+ /** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
14440
14482
  labels: array(string().min(1)).min(1).optional(),
14441
14483
  /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14442
14484
  dbThreshold: number().min(-96).max(0).optional(),
14443
14485
  /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14444
14486
  hitPercent: number().int().min(1).max(100).default(60),
14445
14487
  /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14446
- samplingSeconds: number().int().min(1).max(300).default(10)
14488
+ samplingSeconds: number().int().min(1).max(300).default(10),
14489
+ /**
14490
+ * LABEL MODE: how many labelled frames must land inside
14491
+ * {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
14492
+ * Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
14493
+ */
14494
+ confirmHits: number().int().min(1).max(20).optional(),
14495
+ /** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
14496
+ confirmWindowSec: number().int().min(1).max(60).optional()
14447
14497
  });
14448
14498
  /**
14449
14499
  * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
@@ -16817,6 +16867,46 @@ var RecentTracksPageSchema = object({
16817
16867
  /** Cursor for the next page, or null when this page is the last. */
16818
16868
  nextCursor: string().nullable()
16819
16869
  });
16870
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
16871
+ var LIST_GROUPS_MAX_LIMIT = 100;
16872
+ var AnalyticsGroupRecordSchema = object({
16873
+ id: string(),
16874
+ deviceId: number().int(),
16875
+ openedAt: number().int(),
16876
+ closedAt: number().int(),
16877
+ timestamp: number().int(),
16878
+ memberCount: number().int(),
16879
+ memberTrackIds: array(string()).readonly(),
16880
+ className: string(),
16881
+ classes: array(string()).readonly(),
16882
+ /** Relative event-media path, or null when the group has no picture yet. */
16883
+ mediaUrl: string().nullable(),
16884
+ singleton: boolean()
16885
+ });
16886
+ var AnalyticsGroupMemberSchema = object({
16887
+ trackId: string(),
16888
+ deviceId: number().int(),
16889
+ className: string(),
16890
+ firstSeen: number().int(),
16891
+ lastSeen: number().int(),
16892
+ mediaUrl: string().nullable()
16893
+ });
16894
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
16895
+ var ListGroupsQueryInput = object({
16896
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
16897
+ deviceIds: array(number()),
16898
+ /** Window lower bound on `closedAt` (inclusive). */
16899
+ since: number().optional(),
16900
+ /** Window upper bound on `openedAt` (inclusive). */
16901
+ until: number().optional(),
16902
+ limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
16903
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
16904
+ cursor: string().optional()
16905
+ });
16906
+ var ListGroupsPageSchema = object({
16907
+ groups: array(AnalyticsGroupRecordSchema).readonly(),
16908
+ nextCursor: string().nullable()
16909
+ });
16820
16910
  var KeyEventQueryInput = object({
16821
16911
  deviceId: number(),
16822
16912
  /** Window lower bound (track firstSeen ≥ since). */
@@ -16892,7 +16982,9 @@ var TrackCascadeCountsSchema = object({
16892
16982
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
16893
16983
  plates: number().int(),
16894
16984
  /** Per-track CLIP search vectors removed (best-effort). */
16895
- embeddings: number().int()
16985
+ embeddings: number().int(),
16986
+ /** Group membership + group rows removed with their last member (best-effort). */
16987
+ groups: number().int()
16896
16988
  });
16897
16989
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
16898
16990
  var DiskReconcileCountsSchema = object({
@@ -17038,7 +17130,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17038
17130
  * stationary registry). Default false: the timeline lists passages,
17039
17131
  * not parking records (operator decision, 2026-08-15). */
17040
17132
  includeStationary: boolean().optional()
17041
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
17133
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
17134
+ deviceId: number(),
17135
+ groupId: string().min(1)
17136
+ }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
17042
17137
  kind: "mutation",
17043
17138
  auth: "admin"
17044
17139
  }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
@@ -17348,7 +17443,8 @@ var PipelineModelOptionSchema = object({
17348
17443
  sizeMB: number()
17349
17444
  })),
17350
17445
  group: ModelVariantGroupSchema.optional(),
17351
- legacy: boolean().optional()
17446
+ legacy: boolean().optional(),
17447
+ provider: ModelProviderIdSchema.optional()
17352
17448
  });
17353
17449
  var ConfigFieldBridge = custom();
17354
17450
  var PipelineAddonSchemaSchema = object({
@@ -23955,7 +24051,12 @@ var PlateInfoSchema = object({
23955
24051
  plateBbox: BoundingBoxSchema.optional(),
23956
24052
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
23957
24053
  keyFrameMediaKey: string().optional(),
23958
- base64: string().optional()
24054
+ base64: string().optional(),
24055
+ /**
24056
+ * Same crop as a data-plane URL. `getPlateByTrack` returns this and
24057
+ * never inlines JPEG; `listPlates` still inlines for the admin-ui.
24058
+ */
24059
+ cropUrl: string().optional()
23959
24060
  });
23960
24061
  var MediaFileLiteSchema = object({
23961
24062
  key: string(),
@@ -27595,6 +27696,12 @@ Object.freeze({
27595
27696
  addonId: null,
27596
27697
  access: "view"
27597
27698
  },
27699
+ "deviceManager.getBindingsBatch": {
27700
+ capName: "device-manager",
27701
+ capScope: "system",
27702
+ addonId: null,
27703
+ access: "view"
27704
+ },
27598
27705
  "deviceManager.getChildren": {
27599
27706
  capName: "device-manager",
27600
27707
  capScope: "system",
@@ -27655,6 +27762,12 @@ Object.freeze({
27655
27762
  addonId: null,
27656
27763
  access: "view"
27657
27764
  },
27765
+ "deviceManager.getLinkedDevicesBatch": {
27766
+ capName: "device-manager",
27767
+ capScope: "system",
27768
+ addonId: null,
27769
+ access: "view"
27770
+ },
27658
27771
  "deviceManager.getRoleDisplayDefaults": {
27659
27772
  capName: "device-manager",
27660
27773
  capScope: "system",
@@ -29425,6 +29538,12 @@ Object.freeze({
29425
29538
  addonId: null,
29426
29539
  access: "view"
29427
29540
  },
29541
+ "pipelineAnalytics.getGroup": {
29542
+ capName: "pipeline-analytics",
29543
+ capScope: "device",
29544
+ addonId: null,
29545
+ access: "view"
29546
+ },
29428
29547
  "pipelineAnalytics.getKeyEvents": {
29429
29548
  capName: "pipeline-analytics",
29430
29549
  capScope: "device",
@@ -29509,6 +29628,12 @@ Object.freeze({
29509
29628
  addonId: null,
29510
29629
  access: "view"
29511
29630
  },
29631
+ "pipelineAnalytics.listGroups": {
29632
+ capName: "pipeline-analytics",
29633
+ capScope: "device",
29634
+ addonId: null,
29635
+ access: "view"
29636
+ },
29512
29637
  "pipelineAnalytics.listOpsLog": {
29513
29638
  capName: "pipeline-analytics",
29514
29639
  capScope: "device",
@@ -32290,6 +32415,11 @@ Object.freeze({
32290
32415
  form: "single",
32291
32416
  optional: false
32292
32417
  }],
32418
+ "deviceManager.getBindingsBatch": [{
32419
+ name: "deviceIds",
32420
+ form: "array",
32421
+ optional: false
32422
+ }],
32293
32423
  "deviceManager.getChildren": [{
32294
32424
  name: "parentDeviceId",
32295
32425
  form: "single",
@@ -32335,6 +32465,11 @@ Object.freeze({
32335
32465
  form: "single",
32336
32466
  optional: false
32337
32467
  }],
32468
+ "deviceManager.getLinkedDevicesBatch": [{
32469
+ name: "deviceIds",
32470
+ form: "array",
32471
+ optional: false
32472
+ }],
32338
32473
  "deviceManager.getSettingsSchema": [{
32339
32474
  name: "deviceId",
32340
32475
  form: "single",
@@ -32355,6 +32490,11 @@ Object.freeze({
32355
32490
  form: "single",
32356
32491
  optional: false
32357
32492
  }],
32493
+ "deviceManager.listAll": [{
32494
+ name: "deviceIds",
32495
+ form: "array",
32496
+ optional: true
32497
+ }],
32358
32498
  "deviceManager.loadConfig": [{
32359
32499
  name: "deviceId",
32360
32500
  form: "single",
@@ -32928,6 +33068,11 @@ Object.freeze({
32928
33068
  form: "single",
32929
33069
  optional: false
32930
33070
  }],
33071
+ "pipelineAnalytics.getGroup": [{
33072
+ name: "deviceId",
33073
+ form: "single",
33074
+ optional: false
33075
+ }],
32931
33076
  "pipelineAnalytics.getKeyEvents": [{
32932
33077
  name: "deviceId",
32933
33078
  form: "single",
@@ -32983,6 +33128,11 @@ Object.freeze({
32983
33128
  form: "array",
32984
33129
  optional: false
32985
33130
  }],
33131
+ "pipelineAnalytics.listGroups": [{
33132
+ name: "deviceIds",
33133
+ form: "array",
33134
+ optional: false
33135
+ }],
32986
33136
  "pipelineAnalytics.listOpsLog": [{
32987
33137
  name: "deviceId",
32988
33138
  form: "single",
@@ -21,7 +21,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  enumerable: true
22
22
  }) : target, mod));
23
23
  //#endregion
24
- const require_dist = require("../dist-CbCaiEpw.js");
24
+ const require_dist = require("../dist-BvZr1B8F.js");
25
25
  let node_crypto = require("node:crypto");
26
26
  let node_path = require("node:path");
27
27
  node_path = __toESM(node_path);
@@ -758,6 +758,24 @@ var cloudflareTunnelActions = require_dist.defineCustomActions({
758
758
  })).readonly() }))
759
759
  });
760
760
  //#endregion
761
+ //#region src/tunnel/auto-start.ts
762
+ /**
763
+ * Whether cloudflare-tunnel may spawn `cloudflared` at boot / config-reload.
764
+ *
765
+ * Constructor defaults are `mode: 'quick'`. A settings store that has not
766
+ * answered therefore looks exactly like "the operator wants a trycloudflare
767
+ * URL". Auto-starting that is how a named hostname (the one the notification
768
+ * centre still mints) went Cloudflare 530: the named tunnel never connected.
769
+ *
770
+ * An empty store that DID answer is a real choice — a fresh install still
771
+ * auto-starts quick. Only "never answered" is refused.
772
+ */
773
+ function shouldAutoStartTunnel(input) {
774
+ if (!input.settingsStoreReady) return false;
775
+ if (input.mode === "quick") return true;
776
+ return input.hasCustomToken && input.hasCustomHostname;
777
+ }
778
+ //#endregion
761
779
  //#region src/tunnel/cloudflare-tunnel.addon.ts
762
780
  /**
763
781
  * Cloudflare Tunnel — exposes CamStack via Cloudflare's network.
@@ -882,6 +900,10 @@ var CloudflareTunnelAddon = class extends require_dist.BaseAddon {
882
900
  }
883
901
  });
884
902
  });
903
+ else if (!this.settingsStoreReady) this.ctx.logger.warn("boot auto-start skipped — settings store did not answer; defaults are not an operator choice", { tags: {
904
+ topic: "tunnel",
905
+ phase: "auto-start-skipped"
906
+ } });
885
907
  return {
886
908
  providers: [{
887
909
  capability: require_dist.networkAccessCapability,
@@ -903,8 +925,12 @@ var CloudflareTunnelAddon = class extends require_dist.BaseAddon {
903
925
  * auto-start and by `onConfigChanged` to decide whether a change
904
926
  * warrants a respawn even when no instance was previously running. */
905
927
  isRunCapable() {
906
- if (this.config.mode === "quick") return true;
907
- return !!this.config.customTunnelToken && !!this.config.customHostname;
928
+ return shouldAutoStartTunnel({
929
+ settingsStoreReady: this.settingsStoreReady,
930
+ mode: this.config.mode,
931
+ hasCustomToken: !!this.config.customTunnelToken,
932
+ hasCustomHostname: !!this.config.customHostname
933
+ });
908
934
  }
909
935
  async onShutdown() {
910
936
  if (this.service) {
@@ -1,4 +1,4 @@
1
- import { a as BaseAddon, c as boolean, d as number, f as object, m as EventCategory, n as defineCustomActions, o as _enum, p as string, r as networkAccessCapability, s as array, t as customAction, u as literal } from "../dist-DnohT9Id.mjs";
1
+ import { a as BaseAddon, c as boolean, d as number, f as object, m as EventCategory, n as defineCustomActions, o as _enum, p as string, r as networkAccessCapability, s as array, t as customAction, u as literal } from "../dist-BmCe-liz.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import * as path from "node:path";
4
4
  import { spawn } from "node:child_process";
@@ -731,6 +731,24 @@ var cloudflareTunnelActions = defineCustomActions({
731
731
  })).readonly() }))
732
732
  });
733
733
  //#endregion
734
+ //#region src/tunnel/auto-start.ts
735
+ /**
736
+ * Whether cloudflare-tunnel may spawn `cloudflared` at boot / config-reload.
737
+ *
738
+ * Constructor defaults are `mode: 'quick'`. A settings store that has not
739
+ * answered therefore looks exactly like "the operator wants a trycloudflare
740
+ * URL". Auto-starting that is how a named hostname (the one the notification
741
+ * centre still mints) went Cloudflare 530: the named tunnel never connected.
742
+ *
743
+ * An empty store that DID answer is a real choice — a fresh install still
744
+ * auto-starts quick. Only "never answered" is refused.
745
+ */
746
+ function shouldAutoStartTunnel(input) {
747
+ if (!input.settingsStoreReady) return false;
748
+ if (input.mode === "quick") return true;
749
+ return input.hasCustomToken && input.hasCustomHostname;
750
+ }
751
+ //#endregion
734
752
  //#region src/tunnel/cloudflare-tunnel.addon.ts
735
753
  /**
736
754
  * Cloudflare Tunnel — exposes CamStack via Cloudflare's network.
@@ -855,6 +873,10 @@ var CloudflareTunnelAddon = class extends BaseAddon {
855
873
  }
856
874
  });
857
875
  });
876
+ else if (!this.settingsStoreReady) this.ctx.logger.warn("boot auto-start skipped — settings store did not answer; defaults are not an operator choice", { tags: {
877
+ topic: "tunnel",
878
+ phase: "auto-start-skipped"
879
+ } });
858
880
  return {
859
881
  providers: [{
860
882
  capability: networkAccessCapability,
@@ -876,8 +898,12 @@ var CloudflareTunnelAddon = class extends BaseAddon {
876
898
  * auto-start and by `onConfigChanged` to decide whether a change
877
899
  * warrants a respawn even when no instance was previously running. */
878
900
  isRunCapable() {
879
- if (this.config.mode === "quick") return true;
880
- return !!this.config.customTunnelToken && !!this.config.customHostname;
901
+ return shouldAutoStartTunnel({
902
+ settingsStoreReady: this.settingsStoreReady,
903
+ mode: this.config.mode,
904
+ hasCustomToken: !!this.config.customTunnelToken,
905
+ hasCustomHostname: !!this.config.customHostname
906
+ });
881
907
  }
882
908
  async onShutdown() {
883
909
  if (this.service) {
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_dist = require("../dist-CbCaiEpw.js");
2
+ const require_dist = require("../dist-BvZr1B8F.js");
3
3
  //#region src/turn/cloudflare-turn.ts
4
4
  /**
5
5
  * Cloudflare returns ICE servers in several flavours depending on which
@@ -1,4 +1,4 @@
1
- import { a as BaseAddon, c as boolean, d as number, f as object, i as turnProviderCapability, l as discriminatedUnion, n as defineCustomActions, p as string, t as customAction, u as literal } from "../dist-DnohT9Id.mjs";
1
+ import { a as BaseAddon, c as boolean, d as number, f as object, i as turnProviderCapability, l as discriminatedUnion, n as defineCustomActions, p as string, t as customAction, u as literal } from "../dist-BmCe-liz.mjs";
2
2
  //#region src/turn/cloudflare-turn.ts
3
3
  /**
4
4
  * Cloudflare returns ICE servers in several flavours depending on which
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-cloudflare",
3
- "version": "1.2.26",
3
+ "version": "1.2.28",
4
4
  "description": "Cloudflare bundle — Tunnel (network-access) + TURN relay (turn-provider). Multi-entry npm package shipping 2 addons under a single bundle.",
5
5
  "keywords": [
6
6
  "camstack",