camstack 1.2.30 → 1.2.32

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.
@@ -34423,9 +34423,21 @@ var RecordingLocationUsageSchema = external_exports.object({
34423
34423
  * previously rendered as two identical "disks" with a nonsensical used
34424
34424
  * split — hydrate attribution across aliases is arbitrary by nature. */
34425
34425
  locationIds: external_exports.array(external_exports.string()).optional(),
34426
- /** Bytes of recordings stored on this PHYSICAL volume (all aliases). */
34426
+ /**
34427
+ * Filesystem root of the physical volume this row accounts — the only
34428
+ * operator-legible identity when a location has no `displayName` yet, and
34429
+ * what a "which disk is full" alert has to name. OPTIONAL for the same
34430
+ * train-skew reason as `oldestMs`: a recorder that predates the field omits
34431
+ * it, and this schema must keep validating that older provider's payload.
34432
+ */
34433
+ root: external_exports.string().optional(),
34434
+ /** Bytes of recordings stored on this PHYSICAL volume (all aliases). This is
34435
+ * the footage's SHARE of the volume, not how full the volume is — the disk
34436
+ * also holds everything that is not recordings. Volume fill is
34437
+ * `(totalBytes - availableBytes) / totalBytes`. */
34427
34438
  usedBytes: external_exports.number(),
34428
- /** Free bytes on the location's volume; null when capacity is unknown (remote). */
34439
+ /** Free bytes on the location's volume; null when capacity is unknown
34440
+ * (remote/unstattable — the recorder logs the statfs failure with the root). */
34429
34441
  availableBytes: external_exports.number().nullable(),
34430
34442
  /** Total bytes of the location's volume; null when unknown. */
34431
34443
  totalBytes: external_exports.number().nullable()
@@ -34434,7 +34446,15 @@ var RecordingStorageUsageSchema = external_exports.object({
34434
34446
  nodeId: external_exports.string(),
34435
34447
  totalUsedBytes: external_exports.number(),
34436
34448
  devices: external_exports.array(RecordingDeviceUsageSchema),
34437
- locations: external_exports.array(RecordingLocationUsageSchema)
34449
+ /**
34450
+ * Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
34451
+ * of the payload without it: the hub serves this method through a BAKED
34452
+ * router, so a hub whose framework train predates a change to this array
34453
+ * strips it out of an answer a newer recorder happily produced. A UI that
34454
+ * dereferences it unconditionally crashes the whole Storage page over a
34455
+ * missing disk bar.
34456
+ */
34457
+ locations: external_exports.array(RecordingLocationUsageSchema).optional()
34438
34458
  });
34439
34459
  var RecordingRebalanceMoveSchema = external_exports.object({
34440
34460
  deviceId: external_exports.number(),
@@ -43911,9 +43931,18 @@ var CAP_PROVIDER_KIND_MAP = Object.freeze({
43911
43931
  "network-access": "ingress",
43912
43932
  "smtp-provider": "email"
43913
43933
  });
43934
+ var NC_AUDIO_DEFAULTS = {
43935
+ hitPercent: 60,
43936
+ samplingSeconds: 10
43937
+ };
43914
43938
  var ICON_BY_ID = new Map(AUDIO_MACRO_LABELS.flatMap((macro2) => macro2.icon === void 0 ? [] : [[macro2.id, macro2.icon]]));
43915
43939
  var NC_BASE_CONDITION_KEYS = ["devices", "classes"];
43916
43940
  var BASE_CONDITION_SET = new Set(NC_BASE_CONDITION_KEYS);
43941
+ var NC_AUDIO_SEED = {
43942
+ hitPercent: NC_AUDIO_DEFAULTS.hitPercent,
43943
+ samplingSeconds: NC_AUDIO_DEFAULTS.samplingSeconds,
43944
+ dbThreshold: -55
43945
+ };
43917
43946
  var TimelapseTemplateSchema = external_exports.object({
43918
43947
  title: external_exports.string().max(500).optional(),
43919
43948
  body: external_exports.string().max(2e3).optional()
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runDiscover
4
- } from "./chunk-UCPOTU23.js";
4
+ } from "./chunk-CJBTSCQF.js";
5
5
  import "./chunk-LMMQX4CK.js";
6
6
 
7
7
  // src/cli.ts
@@ -38,7 +38,7 @@ async function runServe(args) {
38
38
  ...typeof values.data === "string" ? { data: values.data } : {}
39
39
  };
40
40
  Object.assign(process.env, buildServeEnv(opts));
41
- await import("./launcher-3IBPG6ZS.js");
41
+ await import("./launcher-OBXOIIQJ.js");
42
42
  }
43
43
 
44
44
  // src/commands/agent.ts
@@ -83,7 +83,7 @@ async function runAgent(args) {
83
83
  ...typeof values.port === "string" ? { port: values.port } : {}
84
84
  };
85
85
  Object.assign(process.env, buildAgentEnv(opts));
86
- await import("./launcher-3IBPG6ZS.js");
86
+ await import("./launcher-OBXOIIQJ.js");
87
87
  }
88
88
 
89
89
  // src/commands/setup.ts
@@ -1130,7 +1130,7 @@ function isUnknown(_value) {
1130
1130
  return true;
1131
1131
  }
1132
1132
  async function resolveServerInteractive(presetNamespace) {
1133
- const { discoverNodes, resolveHubFromDiscovered, filterHubNodes, DEFAULT_HUB_HTTPS_PORT } = await import("./discover-3RXB46IL.js");
1133
+ const { discoverNodes, resolveHubFromDiscovered, filterHubNodes, DEFAULT_HUB_HTTPS_PORT } = await import("./discover-HYDSUPIC.js");
1134
1134
  if (presetNamespace) {
1135
1135
  const spinner4 = clack.spinner();
1136
1136
  spinner4.start(`Discovering hub on LAN (namespace "${presetNamespace}")`);
@@ -5,7 +5,7 @@ import {
5
5
  filterHubNodes,
6
6
  resolveHubFromDiscovered,
7
7
  runDiscover
8
- } from "./chunk-UCPOTU23.js";
8
+ } from "./chunk-CJBTSCQF.js";
9
9
  import "./chunk-LMMQX4CK.js";
10
10
  export {
11
11
  DEFAULT_HUB_HTTPS_PORT,
@@ -23631,9 +23631,9 @@ var require_zod = __commonJS({
23631
23631
  }
23632
23632
  });
23633
23633
 
23634
- // ../system/dist/dist-CB2eCKJb.js
23635
- var require_dist_CB2eCKJb = __commonJS({
23636
- "../system/dist/dist-CB2eCKJb.js"(exports) {
23634
+ // ../system/dist/dist-Ctfhlrtl.js
23635
+ var require_dist_Ctfhlrtl = __commonJS({
23636
+ "../system/dist/dist-Ctfhlrtl.js"(exports) {
23637
23637
  "use strict";
23638
23638
  var zod = require_zod();
23639
23639
  var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
@@ -44705,9 +44705,21 @@ var require_dist_CB2eCKJb = __commonJS({
44705
44705
  * previously rendered as two identical "disks" with a nonsensical used
44706
44706
  * split — hydrate attribution across aliases is arbitrary by nature. */
44707
44707
  locationIds: zod.z.array(zod.z.string()).optional(),
44708
- /** Bytes of recordings stored on this PHYSICAL volume (all aliases). */
44708
+ /**
44709
+ * Filesystem root of the physical volume this row accounts — the only
44710
+ * operator-legible identity when a location has no `displayName` yet, and
44711
+ * what a "which disk is full" alert has to name. OPTIONAL for the same
44712
+ * train-skew reason as `oldestMs`: a recorder that predates the field omits
44713
+ * it, and this schema must keep validating that older provider's payload.
44714
+ */
44715
+ root: zod.z.string().optional(),
44716
+ /** Bytes of recordings stored on this PHYSICAL volume (all aliases). This is
44717
+ * the footage's SHARE of the volume, not how full the volume is — the disk
44718
+ * also holds everything that is not recordings. Volume fill is
44719
+ * `(totalBytes - availableBytes) / totalBytes`. */
44709
44720
  usedBytes: zod.z.number(),
44710
- /** Free bytes on the location's volume; null when capacity is unknown (remote). */
44721
+ /** Free bytes on the location's volume; null when capacity is unknown
44722
+ * (remote/unstattable — the recorder logs the statfs failure with the root). */
44711
44723
  availableBytes: zod.z.number().nullable(),
44712
44724
  /** Total bytes of the location's volume; null when unknown. */
44713
44725
  totalBytes: zod.z.number().nullable()
@@ -44716,7 +44728,15 @@ var require_dist_CB2eCKJb = __commonJS({
44716
44728
  nodeId: zod.z.string(),
44717
44729
  totalUsedBytes: zod.z.number(),
44718
44730
  devices: zod.z.array(RecordingDeviceUsageSchema),
44719
- locations: zod.z.array(RecordingLocationUsageSchema)
44731
+ /**
44732
+ * Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
44733
+ * of the payload without it: the hub serves this method through a BAKED
44734
+ * router, so a hub whose framework train predates a change to this array
44735
+ * strips it out of an answer a newer recorder happily produced. A UI that
44736
+ * dereferences it unconditionally crashes the whole Storage page over a
44737
+ * missing disk bar.
44738
+ */
44739
+ locations: zod.z.array(RecordingLocationUsageSchema).optional()
44720
44740
  });
44721
44741
  var RecordingRebalanceMoveSchema = zod.z.object({
44722
44742
  deviceId: zod.z.number(),
@@ -55149,7 +55169,12 @@ var require_dist_CB2eCKJb = __commonJS({
55149
55169
  durability: "session",
55150
55170
  volatileFields: []
55151
55171
  };
55172
+ var NC_AUDIO_DEFAULTS = {
55173
+ hitPercent: 60,
55174
+ samplingSeconds: 10
55175
+ };
55152
55176
  new Map(AUDIO_MACRO_LABELS.flatMap((macro2) => macro2.icon === void 0 ? [] : [[macro2.id, macro2.icon]]));
55177
+ NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
55153
55178
  var TimelapseTemplateSchema = zod.z.object({
55154
55179
  title: zod.z.string().max(500).optional(),
55155
55180
  body: zod.z.string().max(2e3).optional()
@@ -56083,7 +56108,7 @@ var require_alerts_addon = __commonJS({
56083
56108
  [Symbol.toStringTag]: { value: "Module" }
56084
56109
  });
56085
56110
  require_chunk_Cek0wNdY();
56086
- var require_dist10 = require_dist_CB2eCKJb();
56111
+ var require_dist10 = require_dist_Ctfhlrtl();
56087
56112
  function selectExpired(alerts, cutoffMs) {
56088
56113
  return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
56089
56114
  }
@@ -56896,7 +56921,7 @@ var require_console_logging = __commonJS({
56896
56921
  [Symbol.toStringTag]: { value: "Module" }
56897
56922
  });
56898
56923
  require_chunk_Cek0wNdY();
56899
- var require_dist10 = require_dist_CB2eCKJb();
56924
+ var require_dist10 = require_dist_Ctfhlrtl();
56900
56925
  var require_formatter = require_formatter_DqAKDlvN();
56901
56926
  var LEVEL_RANK = {
56902
56927
  debug: 0,
@@ -56990,7 +57015,7 @@ var require_core_blocks_addon = __commonJS({
56990
57015
  "use strict";
56991
57016
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
56992
57017
  var require_chunk = require_chunk_Cek0wNdY();
56993
- var require_dist10 = require_dist_CB2eCKJb();
57018
+ var require_dist10 = require_dist_Ctfhlrtl();
56994
57019
  var node_crypto = __require("crypto");
56995
57020
  var node_fs_promises = __require("fs/promises");
56996
57021
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -59710,7 +59735,7 @@ var require_device_manager_addon = __commonJS({
59710
59735
  [Symbol.toStringTag]: { value: "Module" }
59711
59736
  });
59712
59737
  require_chunk_Cek0wNdY();
59713
- var require_dist10 = require_dist_CB2eCKJb();
59738
+ var require_dist10 = require_dist_Ctfhlrtl();
59714
59739
  var node_crypto = __require("crypto");
59715
59740
  var _camstack_types_node = require_node();
59716
59741
  var JOB_HISTORY = 20;
@@ -63698,7 +63723,7 @@ var require_hub_forwarder = __commonJS({
63698
63723
  [Symbol.toStringTag]: { value: "Module" }
63699
63724
  });
63700
63725
  require_chunk_Cek0wNdY();
63701
- var require_dist10 = require_dist_CB2eCKJb();
63726
+ var require_dist10 = require_dist_Ctfhlrtl();
63702
63727
  var require_formatter = require_formatter_DqAKDlvN();
63703
63728
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
63704
63729
  var HubForwarderDestination = class {
@@ -63835,7 +63860,7 @@ var require_liveness_monitor_addon = __commonJS({
63835
63860
  "use strict";
63836
63861
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
63837
63862
  require_chunk_Cek0wNdY();
63838
- var require_dist10 = require_dist_CB2eCKJb();
63863
+ var require_dist10 = require_dist_Ctfhlrtl();
63839
63864
  var NO_DEVICES = "liveness:no-devices";
63840
63865
  var ALL_OFFLINE = "liveness:all-devices-offline";
63841
63866
  var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
@@ -64025,7 +64050,7 @@ var require_local_auth_addon = __commonJS({
64025
64050
  [Symbol.toStringTag]: { value: "Module" }
64026
64051
  });
64027
64052
  var require_chunk = require_chunk_Cek0wNdY();
64028
- var require_dist10 = require_dist_CB2eCKJb();
64053
+ var require_dist10 = require_dist_Ctfhlrtl();
64029
64054
  var node_crypto = __require("crypto");
64030
64055
  node_crypto = require_chunk.__toESM(node_crypto);
64031
64056
  var crypto$1 = __require("crypto");
@@ -71709,7 +71734,7 @@ var require_loki_logging = __commonJS({
71709
71734
  [Symbol.toStringTag]: { value: "Module" }
71710
71735
  });
71711
71736
  require_chunk_Cek0wNdY();
71712
- var require_dist10 = require_dist_CB2eCKJb();
71737
+ var require_dist10 = require_dist_Ctfhlrtl();
71713
71738
  function sanitizeLabelName(raw) {
71714
71739
  const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
71715
71740
  return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
@@ -72274,7 +72299,7 @@ var require_native_metrics_addon = __commonJS({
72274
72299
  [Symbol.toStringTag]: { value: "Module" }
72275
72300
  });
72276
72301
  var require_chunk = require_chunk_Cek0wNdY();
72277
- var require_dist10 = require_dist_CB2eCKJb();
72302
+ var require_dist10 = require_dist_Ctfhlrtl();
72278
72303
  var node_child_process = __require("child_process");
72279
72304
  var node_util = __require("util");
72280
72305
  var node_os = __require("os");
@@ -73216,7 +73241,7 @@ var require_filesystem_storage_addon = __commonJS({
73216
73241
  [Symbol.toStringTag]: { value: "Module" }
73217
73242
  });
73218
73243
  var require_chunk = require_chunk_Cek0wNdY();
73219
- var require_dist10 = require_dist_CB2eCKJb();
73244
+ var require_dist10 = require_dist_Ctfhlrtl();
73220
73245
  var node_crypto = __require("crypto");
73221
73246
  var node_fs_promises = __require("fs/promises");
73222
73247
  var node_path = __require("path");
@@ -74332,7 +74357,7 @@ var require_sqlite_settings_addon = __commonJS({
74332
74357
  [Symbol.toStringTag]: { value: "Module" }
74333
74358
  });
74334
74359
  var require_chunk = require_chunk_Cek0wNdY();
74335
- var require_dist10 = require_dist_CB2eCKJb();
74360
+ var require_dist10 = require_dist_Ctfhlrtl();
74336
74361
  var node_crypto = __require("crypto");
74337
74362
  var node_fs = __require("fs");
74338
74363
  var node_module = __require("module");
@@ -75900,7 +75925,7 @@ var require_storage_orchestrator_addon = __commonJS({
75900
75925
  [Symbol.toStringTag]: { value: "Module" }
75901
75926
  });
75902
75927
  var require_chunk = require_chunk_Cek0wNdY();
75903
- var require_dist10 = require_dist_CB2eCKJb();
75928
+ var require_dist10 = require_dist_Ctfhlrtl();
75904
75929
  var node_crypto = __require("crypto");
75905
75930
  var node_fs_promises = __require("fs/promises");
75906
75931
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -77779,7 +77804,7 @@ var require_system_config_addon = __commonJS({
77779
77804
  [Symbol.toStringTag]: { value: "Module" }
77780
77805
  });
77781
77806
  require_chunk_Cek0wNdY();
77782
- var require_dist10 = require_dist_CB2eCKJb();
77807
+ var require_dist10 = require_dist_Ctfhlrtl();
77783
77808
  var SECTION_TITLES = {
77784
77809
  server: "Server",
77785
77810
  auth: "Authentication"
@@ -95840,7 +95865,7 @@ var require_winston_logging = __commonJS({
95840
95865
  [Symbol.toStringTag]: { value: "Module" }
95841
95866
  });
95842
95867
  var require_chunk = require_chunk_Cek0wNdY();
95843
- var require_dist10 = require_dist_CB2eCKJb();
95868
+ var require_dist10 = require_dist_Ctfhlrtl();
95844
95869
  var require_formatter = require_formatter_DqAKDlvN();
95845
95870
  var node_path = __require("path");
95846
95871
  node_path = require_chunk.__toESM(node_path);
@@ -118089,7 +118114,7 @@ var require_dist3 = __commonJS({
118089
118114
  "use strict";
118090
118115
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
118091
118116
  var require_chunk = require_chunk_Cek0wNdY();
118092
- var require_dist10 = require_dist_CB2eCKJb();
118117
+ var require_dist10 = require_dist_Ctfhlrtl();
118093
118118
  var require_builtins_alerts_alerts_addon = require_alerts_addon();
118094
118119
  require_alerts();
118095
118120
  var require_formatter = require_formatter_DqAKDlvN();
@@ -259215,9 +259240,21 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
259215
259240
  * previously rendered as two identical "disks" with a nonsensical used
259216
259241
  * split — hydrate attribution across aliases is arbitrary by nature. */
259217
259242
  locationIds: zod.z.array(zod.z.string()).optional(),
259218
- /** Bytes of recordings stored on this PHYSICAL volume (all aliases). */
259243
+ /**
259244
+ * Filesystem root of the physical volume this row accounts — the only
259245
+ * operator-legible identity when a location has no `displayName` yet, and
259246
+ * what a "which disk is full" alert has to name. OPTIONAL for the same
259247
+ * train-skew reason as `oldestMs`: a recorder that predates the field omits
259248
+ * it, and this schema must keep validating that older provider's payload.
259249
+ */
259250
+ root: zod.z.string().optional(),
259251
+ /** Bytes of recordings stored on this PHYSICAL volume (all aliases). This is
259252
+ * the footage's SHARE of the volume, not how full the volume is — the disk
259253
+ * also holds everything that is not recordings. Volume fill is
259254
+ * `(totalBytes - availableBytes) / totalBytes`. */
259219
259255
  usedBytes: zod.z.number(),
259220
- /** Free bytes on the location's volume; null when capacity is unknown (remote). */
259256
+ /** Free bytes on the location's volume; null when capacity is unknown
259257
+ * (remote/unstattable — the recorder logs the statfs failure with the root). */
259221
259258
  availableBytes: zod.z.number().nullable(),
259222
259259
  /** Total bytes of the location's volume; null when unknown. */
259223
259260
  totalBytes: zod.z.number().nullable()
@@ -259226,7 +259263,15 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
259226
259263
  nodeId: zod.z.string(),
259227
259264
  totalUsedBytes: zod.z.number(),
259228
259265
  devices: zod.z.array(RecordingDeviceUsageSchema),
259229
- locations: zod.z.array(RecordingLocationUsageSchema)
259266
+ /**
259267
+ * Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
259268
+ * of the payload without it: the hub serves this method through a BAKED
259269
+ * router, so a hub whose framework train predates a change to this array
259270
+ * strips it out of an answer a newer recorder happily produced. A UI that
259271
+ * dereferences it unconditionally crashes the whole Storage page over a
259272
+ * missing disk bar.
259273
+ */
259274
+ locations: zod.z.array(RecordingLocationUsageSchema).optional()
259230
259275
  });
259231
259276
  var RecordingRebalanceMoveSchema = zod.z.object({
259232
259277
  deviceId: zod.z.number(),
@@ -274028,6 +274073,242 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
274028
274073
  renderedAs
274029
274074
  };
274030
274075
  }
274076
+ var NO_SENSOR_KIND = "An occupancy edge is not a sensor event: it carries no sensor kind, so this condition can never match one.";
274077
+ var NO_EVENT_TOKEN = "An occupancy edge carries no device event-type token \u2014 that comes from an event-emitter slice, which a count crossing has none of.";
274078
+ var OCCUPANCY_SOURCE = "An occupancy edge is always produced by the pipeline, so any other source can never match and \u201Cpipeline\u201D narrows nothing.";
274079
+ var AUDIO_SOURCE = "A confirmed sound window is always stamped as an audio subject, so any other source can never match.";
274080
+ var SOUND_NOT_A_PICTURE = "A sound rule fires on what the camera HEARD. Its subject carries no detection, so this condition can never match one \u2014 name the sounds in the Sound condition instead.";
274081
+ var SOUND_NO_CONFIDENCE = "A sound window\u2019s evidence is a percentage of samples, not a detector score, so a confidence threshold can never be satisfied.";
274082
+ var SOUND_NO_GEOMETRY = "A sound has no position in the frame, so a zone or a drawn polygon can never match it.";
274083
+ var NC_RULE_KIND_SPECS = [
274084
+ {
274085
+ kind: "detection",
274086
+ label: "Conditions",
274087
+ excluded: {},
274088
+ carriesMedia: true,
274089
+ carriesSubjectCrop: true
274090
+ },
274091
+ {
274092
+ kind: "sensor",
274093
+ label: "Conditions",
274094
+ /** Same argument as `detection`: `occupancy` converts, it does not confuse. */
274095
+ excluded: {},
274096
+ carriesMedia: true,
274097
+ carriesSubjectCrop: true,
274098
+ blurb: "A doorbell press or a linked sensor changing state. The picture comes from the camera the sensor is attributed to."
274099
+ },
274100
+ {
274101
+ kind: "occupancy",
274102
+ label: "Occupancy",
274103
+ discriminator: "occupancy",
274104
+ excluded: {
274105
+ sensorKinds: NO_SENSOR_KIND,
274106
+ eventTypeTokens: NO_EVENT_TOKEN,
274107
+ source: OCCUPANCY_SOURCE
274108
+ },
274109
+ carriesMedia: true,
274110
+ carriesSubjectCrop: true,
274111
+ blurb: "Fires when the count in a camera frame or a zone crosses your threshold and holds. Pick the camera first \u2014 the zone list is that camera\u2019s."
274112
+ },
274113
+ {
274114
+ kind: "sound",
274115
+ label: "Sound",
274116
+ discriminator: "audio",
274117
+ excluded: {
274118
+ classes: SOUND_NOT_A_PICTURE,
274119
+ classesExclude: SOUND_NOT_A_PICTURE,
274120
+ minConfidence: SOUND_NO_CONFIDENCE,
274121
+ zones: SOUND_NO_GEOMETRY,
274122
+ zonesExclude: SOUND_NO_GEOMETRY,
274123
+ customZones: SOUND_NO_GEOMETRY,
274124
+ crossing: SOUND_NO_GEOMETRY,
274125
+ labelEquals: SOUND_NOT_A_PICTURE,
274126
+ identities: SOUND_NOT_A_PICTURE,
274127
+ identitiesExclude: SOUND_NOT_A_PICTURE,
274128
+ plates: SOUND_NOT_A_PICTURE,
274129
+ source: AUDIO_SOURCE
274130
+ },
274131
+ carriesMedia: true,
274132
+ carriesSubjectCrop: false,
274133
+ blurb: "Fires on a confirmed sampling window of audio, never on a picture. Name the sounds and/or keep a level threshold \u2014 with neither it can never match.",
274134
+ mediaBlurb: "A sound owns no frame, so the picture is a photograph of the camera taken at the moment of the match \u2014 the whole scene, never a crop. The GIF and the clip are cut around the same instant, from the camera."
274135
+ },
274136
+ {
274137
+ kind: "system",
274138
+ label: "Events",
274139
+ discriminator: "systemEvent",
274140
+ excluded: {},
274141
+ carriesMedia: false,
274142
+ carriesSubjectCrop: false,
274143
+ blurb: "The installation itself: a camera or stream dropping, a node leaving, an update landing. There is no detection behind it and no frame to attach."
274144
+ }
274145
+ ];
274146
+ function ruleKindSpec(kind) {
274147
+ return NC_RULE_KIND_SPECS.find((s) => s.kind === kind) ?? NC_RULE_KIND_SPECS[0];
274148
+ }
274149
+ var NC_SYSTEM_DELIVERY = "system-event";
274150
+ function isSystemDelivery(delivery) {
274151
+ return delivery === NC_SYSTEM_DELIVERY;
274152
+ }
274153
+ function ruleKindOf(rule) {
274154
+ if (isSystemDelivery(rule.delivery)) return "system";
274155
+ if (rule.conditions.occupancy !== void 0 && rule.delivery === "device-event") return "occupancy";
274156
+ if (rule.conditions.audio !== void 0 && rule.delivery === "immediate") return "sound";
274157
+ if (rule.delivery === "device-event") return "sensor";
274158
+ return "detection";
274159
+ }
274160
+ function conditionVisibleForKind(descriptor, delivery, kind) {
274161
+ const spec = ruleKindSpec(kind);
274162
+ if (descriptor.id === spec.discriminator) return true;
274163
+ if (!descriptor.appliesTo.includes(delivery)) return false;
274164
+ return spec.excluded[descriptor.id] === void 0;
274165
+ }
274166
+ function conditionExclusionReason(descriptorId, kind) {
274167
+ const spec = ruleKindSpec(kind);
274168
+ if (descriptorId === spec.discriminator) return null;
274169
+ return spec.excluded[descriptorId] ?? null;
274170
+ }
274171
+ function droppedConditionsForKind(conditions, kind) {
274172
+ const spec = ruleKindSpec(kind);
274173
+ const out = [];
274174
+ for (const [conditionId, value] of Object.entries(conditions)) {
274175
+ if (value === void 0) continue;
274176
+ if (Array.isArray(value) && value.length === 0) continue;
274177
+ const reason = spec.excluded[conditionId];
274178
+ if (reason !== void 0) out.push({
274179
+ conditionId,
274180
+ reason
274181
+ });
274182
+ }
274183
+ return out;
274184
+ }
274185
+ var NC_RULE_SECTIONS = [
274186
+ {
274187
+ key: "all",
274188
+ label: "All rules",
274189
+ blurb: "Every notification rule, whatever its trigger. Each rule picks a trigger, matches conditions and fans out to one or more delivery targets.",
274190
+ newRuleLabel: "New rule",
274191
+ emptyHint: "No notification rules yet. Create one to be told what your cameras see."
274192
+ },
274193
+ {
274194
+ key: "detection",
274195
+ label: "Detections & devices",
274196
+ blurb: "Rules driven by what the cameras and sensors SEE \u2014 detections, tracks, doorbell presses and package events.",
274197
+ newRuleLabel: "New detection rule",
274198
+ emptyHint: "No detection rules yet. Create one to be told what your cameras see."
274199
+ },
274200
+ {
274201
+ key: "audio",
274202
+ label: "Sound",
274203
+ blurb: "Rules on what the cameras HEAR: a sustained window of sound loud enough, or classified as one of the sounds you name. A sound rule fires only on a confirmed sampling window, never on a picture.",
274204
+ newRuleLabel: "New sound rule",
274205
+ emptyHint: "No sound rules yet. A new sound rule opens on the sound condition already filled in with a level threshold, so you only have to name the sounds and the targets."
274206
+ },
274207
+ {
274208
+ key: "occupancy",
274209
+ label: "Occupancy",
274210
+ blurb: 'Rules on how many objects are in a camera frame or a zone, and for how long: "the driveway became occupied", "the garden emptied".',
274211
+ newRuleLabel: "New occupancy rule",
274212
+ emptyHint: "No occupancy rules yet. A new occupancy rule opens on the occupancy condition; pick the camera first, then the zone and the count."
274213
+ },
274214
+ {
274215
+ key: "system",
274216
+ label: "System events",
274217
+ blurb: "Rules about the installation itself: a camera or stream dropping, a node leaving the cluster, an addon or server update becoming available. These carry no image and are not tied to a detection.",
274218
+ newRuleLabel: "New system rule",
274219
+ emptyHint: "No system-event rules yet. These tell you when a camera drops, a node leaves or an update lands."
274220
+ }
274221
+ ];
274222
+ function ruleSection(key) {
274223
+ return NC_RULE_SECTIONS.find((s) => s.key === key) ?? NC_RULE_SECTIONS[0];
274224
+ }
274225
+ function parseRuleSection(value) {
274226
+ return NC_RULE_SECTIONS.find((s) => s.key === value)?.key;
274227
+ }
274228
+ function isAudioRule(subject) {
274229
+ return subject.conditions.audio !== void 0;
274230
+ }
274231
+ function isOccupancyRule(subject) {
274232
+ return subject.conditions.occupancy !== void 0;
274233
+ }
274234
+ function ruleMatchesSection(subject, section) {
274235
+ if (section === "all") return true;
274236
+ if (section === "system") return isSystemDelivery(subject.delivery);
274237
+ if (section === "audio") return isAudioRule(subject);
274238
+ if (section === "occupancy") return isOccupancyRule(subject);
274239
+ return !isSystemDelivery(subject.delivery) && !isAudioRule(subject) && !isOccupancyRule(subject);
274240
+ }
274241
+ function ruleSectionOf(subject) {
274242
+ if (ruleMatchesSection(subject, "system")) return "system";
274243
+ if (ruleMatchesSection(subject, "audio")) return "audio";
274244
+ if (ruleMatchesSection(subject, "occupancy")) return "occupancy";
274245
+ return "detection";
274246
+ }
274247
+ var NC_OCCUPANCY_DEFAULTS = {
274248
+ op: "became-occupied",
274249
+ count: 1,
274250
+ sustainSeconds: 15
274251
+ };
274252
+ var NC_AUDIO_SEED = {
274253
+ hitPercent: NC_AUDIO_DEFAULTS.hitPercent,
274254
+ samplingSeconds: NC_AUDIO_DEFAULTS.samplingSeconds,
274255
+ dbThreshold: -55
274256
+ };
274257
+ function ruleSeedForSection(section) {
274258
+ switch (section) {
274259
+ case "system":
274260
+ return {
274261
+ delivery: NC_SYSTEM_DELIVERY,
274262
+ conditions: {}
274263
+ };
274264
+ case "audio":
274265
+ return {
274266
+ delivery: "immediate",
274267
+ conditions: { audio: NC_AUDIO_SEED }
274268
+ };
274269
+ case "occupancy":
274270
+ return {
274271
+ delivery: "device-event",
274272
+ conditions: { occupancy: NC_OCCUPANCY_DEFAULTS }
274273
+ };
274274
+ default:
274275
+ return {
274276
+ delivery: "immediate",
274277
+ conditions: {}
274278
+ };
274279
+ }
274280
+ }
274281
+ function defaultDeliveryForSection(section) {
274282
+ return ruleSeedForSection(section).delivery;
274283
+ }
274284
+ var NC_RULE_EDITOR_SECTION_ORDER = [
274285
+ "rule",
274286
+ "devices",
274287
+ "conditions",
274288
+ "notification",
274289
+ "actions",
274290
+ "advanced"
274291
+ ];
274292
+ var EDITOR_SECTION_LABELS = {
274293
+ rule: "Rule",
274294
+ devices: "Cameras",
274295
+ conditions: "Conditions",
274296
+ notification: "Notification",
274297
+ actions: "Actions",
274298
+ advanced: "Advanced"
274299
+ };
274300
+ function ruleEditorSectionsForKind(kind) {
274301
+ const spec = ruleKindSpec(kind);
274302
+ const sections = [];
274303
+ for (const key of NC_RULE_EDITOR_SECTION_ORDER) {
274304
+ if (key === "devices" && kind === "system") continue;
274305
+ sections.push({
274306
+ key,
274307
+ label: key === "conditions" ? spec.label : EDITOR_SECTION_LABELS[key]
274308
+ });
274309
+ }
274310
+ return sections;
274311
+ }
274031
274312
  var WEEKDAY_TO_DAY = {
274032
274313
  Sun: 0,
274033
274314
  Mon: 1,
@@ -276371,6 +276652,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
276371
276652
  exports.NC_AUDIO_HIT_PERCENT_MIN = NC_AUDIO_HIT_PERCENT_MIN;
276372
276653
  exports.NC_AUDIO_SAMPLING_MAX_SEC = NC_AUDIO_SAMPLING_MAX_SEC;
276373
276654
  exports.NC_AUDIO_SAMPLING_MIN_SEC = NC_AUDIO_SAMPLING_MIN_SEC;
276655
+ exports.NC_AUDIO_SEED = NC_AUDIO_SEED;
276374
276656
  exports.NC_AUTHORABLE_SYSTEM_EVENT_KINDS = NC_AUTHORABLE_SYSTEM_EVENT_KINDS;
276375
276657
  exports.NC_BASE_CONDITION_KEYS = NC_BASE_CONDITION_KEYS;
276376
276658
  exports.NC_CONDITION_CATALOG = NC_CONDITION_CATALOG;
@@ -276382,7 +276664,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
276382
276664
  exports.NC_HISTORY_LIMIT_DEFAULT = NC_HISTORY_LIMIT_DEFAULT;
276383
276665
  exports.NC_HISTORY_LIMIT_MAX = NC_HISTORY_LIMIT_MAX;
276384
276666
  exports.NC_MAX_PER_TRACK_IMMEDIATE = NC_MAX_PER_TRACK_IMMEDIATE;
276667
+ exports.NC_OCCUPANCY_DEFAULTS = NC_OCCUPANCY_DEFAULTS;
276668
+ exports.NC_RULE_EDITOR_SECTION_ORDER = NC_RULE_EDITOR_SECTION_ORDER;
276669
+ exports.NC_RULE_KIND_SPECS = NC_RULE_KIND_SPECS;
276670
+ exports.NC_RULE_SECTIONS = NC_RULE_SECTIONS;
276385
276671
  exports.NC_SNOOZE_MAX_MINUTES = NC_SNOOZE_MAX_MINUTES;
276672
+ exports.NC_SYSTEM_DELIVERY = NC_SYSTEM_DELIVERY;
276386
276673
  exports.NC_SYSTEM_EVENT_FILTER_KEYS = NC_SYSTEM_EVENT_FILTER_KEYS;
276387
276674
  exports.NC_TAXONOMY = NC_TAXONOMY;
276388
276675
  exports.NativeCropBboxSchema = NativeCropBboxSchema;
@@ -276870,6 +277157,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
276870
277157
  exports.compileExpressionSafe = compileExpressionSafe;
276871
277158
  exports.composeSwitchedOff = composeSwitchedOff;
276872
277159
  exports.conditionDepth = conditionDepth;
277160
+ exports.conditionExclusionReason = conditionExclusionReason;
277161
+ exports.conditionVisibleForKind = conditionVisibleForKind;
276873
277162
  exports.connectionTestCapability = connectionTestCapability;
276874
277163
  exports.connectivityCapability = connectivityCapability;
276875
277164
  exports.consumablesCapability = consumablesCapability;
@@ -276899,6 +277188,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
276899
277188
  exports.declarationOwnerNodeId = declarationOwnerNodeId;
276900
277189
  exports.decodeVectorBase64 = decodeVectorBase64;
276901
277190
  exports.decoderCapability = decoderCapability;
277191
+ exports.defaultDeliveryForSection = defaultDeliveryForSection;
276902
277192
  exports.defaultDeviceFor = defaultDeviceFor;
276903
277193
  exports.defineCustomActions = defineCustomActions;
276904
277194
  exports.deriveBatteryPresence = deriveBatteryPresence;
@@ -276921,6 +277211,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
276921
277211
  exports.deviceStateCapability = deviceStateCapability;
276922
277212
  exports.deviceStatusCapability = deviceStatusCapability;
276923
277213
  exports.doorbellCapability = doorbellCapability;
277214
+ exports.droppedConditionsForKind = droppedConditionsForKind;
276924
277215
  exports.egressTranscodeSharingKey = egressTranscodeSharingKey;
276925
277216
  exports.egressTransportFromRequest = egressTransportFromRequest;
276926
277217
  exports.embeddingEncoderCapability = embeddingEncoderCapability;
@@ -276972,6 +277263,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
276972
277263
  exports.isAgentOnlyPlacement = isAgentOnlyPlacement;
276973
277264
  exports.isArrayOutputSchema = isArrayOutputSchema;
276974
277265
  exports.isAudioLabelSelected = isAudioLabelSelected;
277266
+ exports.isAudioRule = isAudioRule;
276975
277267
  exports.isBaseConditionKey = isBaseConditionKey;
276976
277268
  exports.isBatteryPresenceFault = isBatteryPresenceFault;
276977
277269
  exports.isCollectionArrayMethod = isCollectionArrayMethod;
@@ -276983,10 +277275,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
276983
277275
  exports.isIsolatedBuiltin = isIsolatedBuiltin;
276984
277276
  exports.isNode = isNode;
276985
277277
  exports.isObjectInput = isObjectInput;
277278
+ exports.isOccupancyRule = isOccupancyRule;
276986
277279
  exports.isRestoredCap = isRestoredCap;
276987
277280
  exports.isSameAddonId = isSameAddonId;
276988
277281
  exports.isScheduleActive = isScheduleActive;
276989
277282
  exports.isSoftwareDecode = require_canonical_hash.isSoftwareDecode;
277283
+ exports.isSystemDelivery = isSystemDelivery;
276990
277284
  exports.isVoidInput = isVoidInput;
276991
277285
  exports.jobKindSchema = jobKindSchema;
276992
277286
  exports.kebabToCamel = kebabToCamel;
@@ -277050,6 +277344,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
277050
277344
  exports.parseJsonUnknown = require_sleep.parseJsonUnknown;
277051
277345
  exports.parseProcStatus = parseProcStatus;
277052
277346
  exports.parseProfileBrokerId = require_sleep.parseProfileBrokerId;
277347
+ exports.parseRuleSection = parseRuleSection;
277053
277348
  exports.parseStreamParamsFormPatch = parseStreamParamsFormPatch;
277054
277349
  exports.patchAudio = patchAudio;
277055
277350
  exports.petFeederCapability = petFeederCapability;
@@ -277109,6 +277404,13 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
277109
277404
  exports.resolveVariantModelId = resolveVariantModelId;
277110
277405
  exports.resolveViewableDeviceIds = resolveViewableDeviceIds;
277111
277406
  exports.roleSpec = roleSpec;
277407
+ exports.ruleEditorSectionsForKind = ruleEditorSectionsForKind;
277408
+ exports.ruleKindOf = ruleKindOf;
277409
+ exports.ruleKindSpec = ruleKindSpec;
277410
+ exports.ruleMatchesSection = ruleMatchesSection;
277411
+ exports.ruleSection = ruleSection;
277412
+ exports.ruleSectionOf = ruleSectionOf;
277413
+ exports.ruleSeedForSection = ruleSeedForSection;
277112
277414
  exports.runInferenceStep = runInferenceStep;
277113
277415
  exports.runtimeDevices = runtimeDevices;
277114
277416
  exports.runtimeStatePolicyFor = runtimeStatePolicyFor;
@@ -401038,6 +401340,52 @@ var require_boot_config = __commonJS({
401038
401340
  }
401039
401341
  });
401040
401342
 
401343
+ // ../../server/backend/dist/core/app-links/well-known-app-links.js
401344
+ var require_well_known_app_links = __commonJS({
401345
+ "../../server/backend/dist/core/app-links/well-known-app-links.js"(exports) {
401346
+ "use strict";
401347
+ Object.defineProperty(exports, "__esModule", { value: true });
401348
+ exports.buildAppLinkFiles = buildAppLinkFiles;
401349
+ exports.parseCertFingerprints = parseCertFingerprints;
401350
+ var APPLE_TEAM_ID = "C3F24V5NS5";
401351
+ var BUNDLE_ID = "com.apocaliss92.camstack";
401352
+ var BUNDLE_ID_DEV = "com.apocaliss92.camstack.dev";
401353
+ var VIEWER_COMPONENT = "/viewer/camstack/*";
401354
+ function buildAppLinkFiles(androidCertSha256) {
401355
+ const appIds = [`${APPLE_TEAM_ID}.${BUNDLE_ID}`, `${APPLE_TEAM_ID}.${BUNDLE_ID_DEV}`];
401356
+ return {
401357
+ appleAppSiteAssociation: {
401358
+ applinks: {
401359
+ apps: [],
401360
+ details: [
401361
+ {
401362
+ appIDs: appIds,
401363
+ components: [{ "/": VIEWER_COMPONENT, comment: "Viewer deep links" }]
401364
+ },
401365
+ // Legacy `paths` block for pre-iOS-13 parsers, as Zentik ships.
401366
+ { appID: appIds[0], paths: [VIEWER_COMPONENT] }
401367
+ ]
401368
+ },
401369
+ webcredentials: { apps: appIds }
401370
+ },
401371
+ assetLinks: [BUNDLE_ID, BUNDLE_ID_DEV].map((pkg) => ({
401372
+ relation: ["delegate_permission/common.handle_all_urls"],
401373
+ target: {
401374
+ namespace: "android_app",
401375
+ package_name: pkg,
401376
+ sha256_cert_fingerprints: [...androidCertSha256]
401377
+ }
401378
+ }))
401379
+ };
401380
+ }
401381
+ function parseCertFingerprints(raw) {
401382
+ if (raw === void 0)
401383
+ return [];
401384
+ return raw.split(",").map((s) => s.trim().toUpperCase()).filter((s) => /^[0-9A-F]{2}(:[0-9A-F]{2}){31}$/.test(s));
401385
+ }
401386
+ }
401387
+ });
401388
+
401041
401389
  // ../../server/backend/dist/core/addon/addon-call-gateway.js
401042
401390
  var require_addon_call_gateway = __commonJS({
401043
401391
  "../../server/backend/dist/core/addon/addon-call-gateway.js"(exports) {
@@ -408306,6 +408654,7 @@ var require_main4 = __commonJS({
408306
408654
  var integration_id_backfill_1 = require_integration_id_backfill();
408307
408655
  var post_boot_service_1 = require_post_boot_service();
408308
408656
  var addon_package_service_1 = require_addon_package_service();
408657
+ var well_known_app_links_1 = require_well_known_app_links();
408309
408658
  var addon_registry_service_1 = require_addon_registry_service();
408310
408659
  var addon_bridge_service_1 = require_addon_bridge_service();
408311
408660
  var addon_pages_service_1 = require_addon_pages_service();
@@ -409002,6 +409351,14 @@ var require_main4 = __commonJS({
409002
409351
  const adminUiState = { staticDir: null, indexPath: null };
409003
409352
  const viewerUiState = { staticDir: null, indexPath: null };
409004
409353
  const VIEWER_MOUNT = "/viewer/camstack";
409354
+ {
409355
+ const { appleAppSiteAssociation, assetLinks } = (0, well_known_app_links_1.buildAppLinkFiles)((0, well_known_app_links_1.parseCertFingerprints)(process.env["CAMSTACK_ANDROID_CERT_SHA256"]));
409356
+ const aasa = JSON.stringify(appleAppSiteAssociation);
409357
+ const assetlinks = JSON.stringify(assetLinks);
409358
+ fastify.get("/.well-known/apple-app-site-association", async (_request, reply) => reply.type("application/json").send(aasa));
409359
+ fastify.get("/apple-app-site-association", async (_request, reply) => reply.type("application/json").send(aasa));
409360
+ fastify.get("/.well-known/assetlinks.json", async (_request, reply) => reply.type("application/json").send(assetlinks));
409361
+ }
409005
409362
  fastify.get("/viewer", async (_request, reply) => reply.redirect(`${VIEWER_MOUNT}/`));
409006
409363
  fastify.get("/viewer/", async (_request, reply) => reply.redirect(`${VIEWER_MOUNT}/`));
409007
409364
  fastify.get("/webrtc-test.html", async (_request, reply) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "camstack",
3
- "version": "1.2.30",
3
+ "version": "1.2.32",
4
4
  "description": "CLI tool for managing and running CamStack server",
5
5
  "keywords": [
6
6
  "camstack",