camstack 1.2.59 → 1.2.60

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.
@@ -33129,6 +33129,33 @@ var intercomCapability = {
33129
33129
  deviceNative: true,
33130
33130
  mode: "singleton",
33131
33131
  deviceTypes: [DeviceType.Camera],
33132
+ /**
33133
+ * **Auth tier: `protected` on every method — deliberate, and load-bearing.**
33134
+ *
33135
+ * Talking through a camera is an OPERATE action, not a CONFIGURE one. This
33136
+ * cap has no configuration surface at all: all six methods open, feed and
33137
+ * close one live audio session against one `deviceId`. That is the same
33138
+ * authority as `ptz.move` or `snapshot.getSnapshot`, both `protected` — and
33139
+ * the opposite of `ptz.savePreset` / `snapshot.invalidateCache`, which are
33140
+ * `admin` because they change what the device IS.
33141
+ *
33142
+ * `protected` does not mean ungated: `protectedProcedure` runs the
33143
+ * `METHOD_ACCESS_MAP` scope check, and every method here is `scope: 'device'`
33144
+ * with `access: 'create'` and a `deviceId` in its input. So a caller needs a
33145
+ * grant that covers THAT camera at `create` — a `camera-viewer` (`view`
33146
+ * only) still cannot talk, and a grant on camera 5 cannot talk through
33147
+ * camera 7.
33148
+ *
33149
+ * Every method was `auth: 'admin'` from the initial commit, which made the
33150
+ * cap unreachable by every non-admin principal — `adminProcedure` throws
33151
+ * `FORBIDDEN: Admin required` BEFORE the scope check runs, so the scope
33152
+ * machinery generated for this cap (`METHOD_ACCESS_MAP`,
33153
+ * `DEVICE_SCOPED_CAPS`, `METHOD_DEVICE_SELECTORS`) was complete and dead. The
33154
+ * `camera-operator` scope preset has promised "PTZ control, intercom,
33155
+ * snapshots" since that same commit; the promise could not be kept. Recorded
33156
+ * as D289; `scripts/check-scope-preset-promises.ts` now fails the build if a
33157
+ * preset promises a cap no row of that preset can reach.
33158
+ */
33132
33159
  methods: {
33133
33160
  /**
33134
33161
  * Open a server-side WebRTC audio-only session. Returns an SDP
@@ -33141,7 +33168,7 @@ var intercomCapability = {
33141
33168
  sdpOffer: external_exports.string()
33142
33169
  }), {
33143
33170
  kind: "mutation",
33144
- auth: "admin"
33171
+ auth: "protected"
33145
33172
  }),
33146
33173
  handleAnswer: method(external_exports.object({
33147
33174
  deviceId: external_exports.number(),
@@ -33149,7 +33176,7 @@ var intercomCapability = {
33149
33176
  sdpAnswer: external_exports.string()
33150
33177
  }), external_exports.void(), {
33151
33178
  kind: "mutation",
33152
- auth: "admin"
33179
+ auth: "protected"
33153
33180
  }),
33154
33181
  /** Close explicitly. Server also auto-closes on 30s idle. */
33155
33182
  stopSession: method(external_exports.object({
@@ -33157,7 +33184,7 @@ var intercomCapability = {
33157
33184
  sessionId: external_exports.string()
33158
33185
  }), external_exports.void(), {
33159
33186
  kind: "mutation",
33160
- auth: "admin"
33187
+ auth: "protected"
33161
33188
  }),
33162
33189
  /**
33163
33190
  * Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
@@ -33170,7 +33197,7 @@ var intercomCapability = {
33170
33197
  */
33171
33198
  startTalkSession: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.object({ sessionId: external_exports.string() }), {
33172
33199
  kind: "mutation",
33173
- auth: "admin"
33200
+ auth: "protected"
33174
33201
  }),
33175
33202
  /**
33176
33203
  * Push one chunk of talk-back audio onto the active talk session.
@@ -33205,12 +33232,12 @@ var intercomCapability = {
33205
33232
  sequenceNumber: external_exports.number().int()
33206
33233
  }), external_exports.object({ accepted: external_exports.boolean() }), {
33207
33234
  kind: "mutation",
33208
- auth: "admin"
33235
+ auth: "protected"
33209
33236
  }),
33210
33237
  /** Close the raw-PCM talk session. Idempotent. */
33211
33238
  endTalkSession: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.void(), {
33212
33239
  kind: "mutation",
33213
- auth: "admin"
33240
+ auth: "protected"
33214
33241
  })
33215
33242
  },
33216
33243
  events: { onStatusChanged: { data: external_exports.object({
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runDiscover
4
- } from "./chunk-UMNISWW6.js";
4
+ } from "./chunk-ARTM5NDU.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-WK4Q3DVU.js");
41
+ await import("./launcher-CL4LHR6D.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-WK4Q3DVU.js");
86
+ await import("./launcher-CL4LHR6D.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-NCRIXDCA.js");
1133
+ const { discoverNodes, resolveHubFromDiscovered, filterHubNodes, DEFAULT_HUB_HTTPS_PORT } = await import("./discover-UFN2BYNO.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-UMNISWW6.js";
8
+ } from "./chunk-ARTM5NDU.js";
9
9
  import "./chunk-LMMQX4CK.js";
10
10
  export {
11
11
  DEFAULT_HUB_HTTPS_PORT,
@@ -23633,9 +23633,9 @@ var require_zod = __commonJS({
23633
23633
  }
23634
23634
  });
23635
23635
 
23636
- // ../system/dist/dist-C7uZ_fOb.js
23637
- var require_dist_C7uZ_fOb = __commonJS({
23638
- "../system/dist/dist-C7uZ_fOb.js"(exports) {
23636
+ // ../system/dist/dist-BPlfW-CG.js
23637
+ var require_dist_BPlfW_CG = __commonJS({
23638
+ "../system/dist/dist-BPlfW-CG.js"(exports) {
23639
23639
  "use strict";
23640
23640
  var zod = require_zod();
23641
23641
  var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
@@ -43645,6 +43645,33 @@ var require_dist_C7uZ_fOb = __commonJS({
43645
43645
  deviceNative: true,
43646
43646
  mode: "singleton",
43647
43647
  deviceTypes: [DeviceType.Camera],
43648
+ /**
43649
+ * **Auth tier: `protected` on every method — deliberate, and load-bearing.**
43650
+ *
43651
+ * Talking through a camera is an OPERATE action, not a CONFIGURE one. This
43652
+ * cap has no configuration surface at all: all six methods open, feed and
43653
+ * close one live audio session against one `deviceId`. That is the same
43654
+ * authority as `ptz.move` or `snapshot.getSnapshot`, both `protected` — and
43655
+ * the opposite of `ptz.savePreset` / `snapshot.invalidateCache`, which are
43656
+ * `admin` because they change what the device IS.
43657
+ *
43658
+ * `protected` does not mean ungated: `protectedProcedure` runs the
43659
+ * `METHOD_ACCESS_MAP` scope check, and every method here is `scope: 'device'`
43660
+ * with `access: 'create'` and a `deviceId` in its input. So a caller needs a
43661
+ * grant that covers THAT camera at `create` — a `camera-viewer` (`view`
43662
+ * only) still cannot talk, and a grant on camera 5 cannot talk through
43663
+ * camera 7.
43664
+ *
43665
+ * Every method was `auth: 'admin'` from the initial commit, which made the
43666
+ * cap unreachable by every non-admin principal — `adminProcedure` throws
43667
+ * `FORBIDDEN: Admin required` BEFORE the scope check runs, so the scope
43668
+ * machinery generated for this cap (`METHOD_ACCESS_MAP`,
43669
+ * `DEVICE_SCOPED_CAPS`, `METHOD_DEVICE_SELECTORS`) was complete and dead. The
43670
+ * `camera-operator` scope preset has promised "PTZ control, intercom,
43671
+ * snapshots" since that same commit; the promise could not be kept. Recorded
43672
+ * as D289; `scripts/check-scope-preset-promises.ts` now fails the build if a
43673
+ * preset promises a cap no row of that preset can reach.
43674
+ */
43648
43675
  methods: {
43649
43676
  /**
43650
43677
  * Open a server-side WebRTC audio-only session. Returns an SDP
@@ -43657,7 +43684,7 @@ var require_dist_C7uZ_fOb = __commonJS({
43657
43684
  sdpOffer: zod.z.string()
43658
43685
  }), {
43659
43686
  kind: "mutation",
43660
- auth: "admin"
43687
+ auth: "protected"
43661
43688
  }),
43662
43689
  handleAnswer: method(zod.z.object({
43663
43690
  deviceId: zod.z.number(),
@@ -43665,7 +43692,7 @@ var require_dist_C7uZ_fOb = __commonJS({
43665
43692
  sdpAnswer: zod.z.string()
43666
43693
  }), zod.z.void(), {
43667
43694
  kind: "mutation",
43668
- auth: "admin"
43695
+ auth: "protected"
43669
43696
  }),
43670
43697
  /** Close explicitly. Server also auto-closes on 30s idle. */
43671
43698
  stopSession: method(zod.z.object({
@@ -43673,7 +43700,7 @@ var require_dist_C7uZ_fOb = __commonJS({
43673
43700
  sessionId: zod.z.string()
43674
43701
  }), zod.z.void(), {
43675
43702
  kind: "mutation",
43676
- auth: "admin"
43703
+ auth: "protected"
43677
43704
  }),
43678
43705
  /**
43679
43706
  * Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
@@ -43686,7 +43713,7 @@ var require_dist_C7uZ_fOb = __commonJS({
43686
43713
  */
43687
43714
  startTalkSession: method(zod.z.object({ deviceId: zod.z.number() }), zod.z.object({ sessionId: zod.z.string() }), {
43688
43715
  kind: "mutation",
43689
- auth: "admin"
43716
+ auth: "protected"
43690
43717
  }),
43691
43718
  /**
43692
43719
  * Push one chunk of talk-back audio onto the active talk session.
@@ -43721,12 +43748,12 @@ var require_dist_C7uZ_fOb = __commonJS({
43721
43748
  sequenceNumber: zod.z.number().int()
43722
43749
  }), zod.z.object({ accepted: zod.z.boolean() }), {
43723
43750
  kind: "mutation",
43724
- auth: "admin"
43751
+ auth: "protected"
43725
43752
  }),
43726
43753
  /** Close the raw-PCM talk session. Idempotent. */
43727
43754
  endTalkSession: method(zod.z.object({ deviceId: zod.z.number() }), zod.z.void(), {
43728
43755
  kind: "mutation",
43729
- auth: "admin"
43756
+ auth: "protected"
43730
43757
  })
43731
43758
  },
43732
43759
  events: { onStatusChanged: { data: zod.z.object({
@@ -58686,7 +58713,7 @@ var require_alerts_addon = __commonJS({
58686
58713
  [Symbol.toStringTag]: { value: "Module" }
58687
58714
  });
58688
58715
  require_chunk_Cek0wNdY();
58689
- var require_dist10 = require_dist_C7uZ_fOb();
58716
+ var require_dist10 = require_dist_BPlfW_CG();
58690
58717
  function selectExpired(alerts, cutoffMs) {
58691
58718
  return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
58692
58719
  }
@@ -59505,7 +59532,7 @@ var require_console_logging = __commonJS({
59505
59532
  [Symbol.toStringTag]: { value: "Module" }
59506
59533
  });
59507
59534
  require_chunk_Cek0wNdY();
59508
- var require_dist10 = require_dist_C7uZ_fOb();
59535
+ var require_dist10 = require_dist_BPlfW_CG();
59509
59536
  var require_formatter = require_formatter_DqAKDlvN();
59510
59537
  var LEVEL_RANK = {
59511
59538
  debug: 0,
@@ -59599,7 +59626,7 @@ var require_core_blocks_addon = __commonJS({
59599
59626
  "use strict";
59600
59627
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
59601
59628
  var require_chunk = require_chunk_Cek0wNdY();
59602
- var require_dist10 = require_dist_C7uZ_fOb();
59629
+ var require_dist10 = require_dist_BPlfW_CG();
59603
59630
  var node_crypto = __require("crypto");
59604
59631
  var node_fs_promises = __require("fs/promises");
59605
59632
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -60496,11 +60523,11 @@ var require_core_blocks = __commonJS({
60496
60523
  }
60497
60524
  });
60498
60525
 
60499
- // ../system/dist/retired-settings-keys-CBU4-TJO.js
60500
- var require_retired_settings_keys_CBU4_TJO = __commonJS({
60501
- "../system/dist/retired-settings-keys-CBU4-TJO.js"(exports) {
60526
+ // ../system/dist/retired-settings-keys-DBY6ebwV.js
60527
+ var require_retired_settings_keys_DBY6ebwV = __commonJS({
60528
+ "../system/dist/retired-settings-keys-DBY6ebwV.js"(exports) {
60502
60529
  "use strict";
60503
- var require_dist10 = require_dist_C7uZ_fOb();
60530
+ var require_dist10 = require_dist_BPlfW_CG();
60504
60531
  function settingsStoreIsAuthoritativeHere(env) {
60505
60532
  const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
60506
60533
  return raw === "" || raw === "hub";
@@ -62714,8 +62741,8 @@ var require_device_manager_addon = __commonJS({
62714
62741
  [Symbol.toStringTag]: { value: "Module" }
62715
62742
  });
62716
62743
  require_chunk_Cek0wNdY();
62717
- var require_dist10 = require_dist_C7uZ_fOb();
62718
- var require_retired_settings_keys = require_retired_settings_keys_CBU4_TJO();
62744
+ var require_dist10 = require_dist_BPlfW_CG();
62745
+ var require_retired_settings_keys = require_retired_settings_keys_DBY6ebwV();
62719
62746
  var node_crypto = __require("crypto");
62720
62747
  var _camstack_types_node = require_node();
62721
62748
  var JOB_HISTORY = 20;
@@ -67464,7 +67491,7 @@ var require_hub_forwarder = __commonJS({
67464
67491
  [Symbol.toStringTag]: { value: "Module" }
67465
67492
  });
67466
67493
  require_chunk_Cek0wNdY();
67467
- var require_dist10 = require_dist_C7uZ_fOb();
67494
+ var require_dist10 = require_dist_BPlfW_CG();
67468
67495
  var require_formatter = require_formatter_DqAKDlvN();
67469
67496
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
67470
67497
  var HubForwarderDestination = class {
@@ -67601,7 +67628,7 @@ var require_liveness_monitor_addon = __commonJS({
67601
67628
  "use strict";
67602
67629
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
67603
67630
  require_chunk_Cek0wNdY();
67604
- var require_dist10 = require_dist_C7uZ_fOb();
67631
+ var require_dist10 = require_dist_BPlfW_CG();
67605
67632
  var NO_DEVICES = "liveness:no-devices";
67606
67633
  var ALL_OFFLINE = "liveness:all-devices-offline";
67607
67634
  var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
@@ -67791,7 +67818,7 @@ var require_local_auth_addon = __commonJS({
67791
67818
  [Symbol.toStringTag]: { value: "Module" }
67792
67819
  });
67793
67820
  var require_chunk = require_chunk_Cek0wNdY();
67794
- var require_dist10 = require_dist_C7uZ_fOb();
67821
+ var require_dist10 = require_dist_BPlfW_CG();
67795
67822
  var node_crypto = __require("crypto");
67796
67823
  node_crypto = require_chunk.__toESM(node_crypto);
67797
67824
  var crypto$1 = __require("crypto");
@@ -75604,7 +75631,7 @@ var require_loki_logging = __commonJS({
75604
75631
  [Symbol.toStringTag]: { value: "Module" }
75605
75632
  });
75606
75633
  require_chunk_Cek0wNdY();
75607
- var require_dist10 = require_dist_C7uZ_fOb();
75634
+ var require_dist10 = require_dist_BPlfW_CG();
75608
75635
  function sanitizeLabelName(raw) {
75609
75636
  const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
75610
75637
  return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
@@ -76169,7 +76196,7 @@ var require_native_metrics_addon = __commonJS({
76169
76196
  [Symbol.toStringTag]: { value: "Module" }
76170
76197
  });
76171
76198
  var require_chunk = require_chunk_Cek0wNdY();
76172
- var require_dist10 = require_dist_C7uZ_fOb();
76199
+ var require_dist10 = require_dist_BPlfW_CG();
76173
76200
  var node_fs_promises = __require("fs/promises");
76174
76201
  var node_child_process = __require("child_process");
76175
76202
  var node_util = __require("util");
@@ -78791,7 +78818,7 @@ var require_filesystem_storage_addon = __commonJS({
78791
78818
  [Symbol.toStringTag]: { value: "Module" }
78792
78819
  });
78793
78820
  var require_chunk = require_chunk_Cek0wNdY();
78794
- var require_dist10 = require_dist_C7uZ_fOb();
78821
+ var require_dist10 = require_dist_BPlfW_CG();
78795
78822
  var node_crypto = __require("crypto");
78796
78823
  var node_fs_promises = __require("fs/promises");
78797
78824
  var node_path = __require("path");
@@ -79907,8 +79934,8 @@ var require_sqlite_settings_addon = __commonJS({
79907
79934
  [Symbol.toStringTag]: { value: "Module" }
79908
79935
  });
79909
79936
  var require_chunk = require_chunk_Cek0wNdY();
79910
- var require_dist10 = require_dist_C7uZ_fOb();
79911
- var require_retired_settings_keys = require_retired_settings_keys_CBU4_TJO();
79937
+ var require_dist10 = require_dist_BPlfW_CG();
79938
+ var require_retired_settings_keys = require_retired_settings_keys_DBY6ebwV();
79912
79939
  var node_crypto = __require("crypto");
79913
79940
  var node_fs = __require("fs");
79914
79941
  var node_module = __require("module");
@@ -82187,7 +82214,7 @@ var require_storage_orchestrator_addon = __commonJS({
82187
82214
  [Symbol.toStringTag]: { value: "Module" }
82188
82215
  });
82189
82216
  var require_chunk = require_chunk_Cek0wNdY();
82190
- var require_dist10 = require_dist_C7uZ_fOb();
82217
+ var require_dist10 = require_dist_BPlfW_CG();
82191
82218
  var node_crypto = __require("crypto");
82192
82219
  var node_fs_promises = __require("fs/promises");
82193
82220
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -84068,7 +84095,7 @@ var require_system_config_addon = __commonJS({
84068
84095
  [Symbol.toStringTag]: { value: "Module" }
84069
84096
  });
84070
84097
  require_chunk_Cek0wNdY();
84071
- var require_dist10 = require_dist_C7uZ_fOb();
84098
+ var require_dist10 = require_dist_BPlfW_CG();
84072
84099
  var SECTION_TITLES = {
84073
84100
  server: "Server",
84074
84101
  auth: "Authentication"
@@ -102129,7 +102156,7 @@ var require_winston_logging = __commonJS({
102129
102156
  [Symbol.toStringTag]: { value: "Module" }
102130
102157
  });
102131
102158
  var require_chunk = require_chunk_Cek0wNdY();
102132
- var require_dist10 = require_dist_C7uZ_fOb();
102159
+ var require_dist10 = require_dist_BPlfW_CG();
102133
102160
  var require_formatter = require_formatter_DqAKDlvN();
102134
102161
  var node_path = __require("path");
102135
102162
  node_path = require_chunk.__toESM(node_path);
@@ -114617,12 +114644,12 @@ var require_dist2 = __commonJS({
114617
114644
  }
114618
114645
  });
114619
114646
 
114620
- // ../system/dist/manifest-python-deps-CRTx_j4k.js
114621
- var require_manifest_python_deps_CRTx_j4k = __commonJS({
114622
- "../system/dist/manifest-python-deps-CRTx_j4k.js"(exports) {
114647
+ // ../system/dist/manifest-python-deps-COeSr7el.js
114648
+ var require_manifest_python_deps_COeSr7el = __commonJS({
114649
+ "../system/dist/manifest-python-deps-COeSr7el.js"(exports) {
114623
114650
  "use strict";
114624
114651
  var require_chunk = require_chunk_Cek0wNdY();
114625
- require_dist_C7uZ_fOb();
114652
+ require_dist_BPlfW_CG();
114626
114653
  var node_crypto = __require("crypto");
114627
114654
  node_crypto = require_chunk.__toESM(node_crypto);
114628
114655
  var _camstack_types_node = require_node();
@@ -126068,7 +126095,7 @@ var require_dist3 = __commonJS({
126068
126095
  "use strict";
126069
126096
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
126070
126097
  var require_chunk = require_chunk_Cek0wNdY();
126071
- var require_dist10 = require_dist_C7uZ_fOb();
126098
+ var require_dist10 = require_dist_BPlfW_CG();
126072
126099
  var require_builtins_alerts_alerts_addon = require_alerts_addon();
126073
126100
  require_alerts();
126074
126101
  var require_formatter = require_formatter_DqAKDlvN();
@@ -126094,7 +126121,7 @@ var require_dist3 = __commonJS({
126094
126121
  var require_builtins_winston_logging_index = require_winston_logging();
126095
126122
  var require_file_data_plane = require_file_data_plane_DO8KbxCe();
126096
126123
  var require_tls$1 = require_tls_BxQlomxd();
126097
- var require_manifest_python_deps = require_manifest_python_deps_CRTx_j4k();
126124
+ var require_manifest_python_deps = require_manifest_python_deps_COeSr7el();
126098
126125
  var require_resource_monitor = require_resource_monitor_CdnzxBLP();
126099
126126
  var require_custom_action_registry = require_custom_action_registry_jY0NOZK8();
126100
126127
  var zod = require_zod();
@@ -227689,6 +227716,33 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
227689
227716
  deviceNative: true,
227690
227717
  mode: "singleton",
227691
227718
  deviceTypes: [require_sleep.DeviceType.Camera],
227719
+ /**
227720
+ * **Auth tier: `protected` on every method — deliberate, and load-bearing.**
227721
+ *
227722
+ * Talking through a camera is an OPERATE action, not a CONFIGURE one. This
227723
+ * cap has no configuration surface at all: all six methods open, feed and
227724
+ * close one live audio session against one `deviceId`. That is the same
227725
+ * authority as `ptz.move` or `snapshot.getSnapshot`, both `protected` — and
227726
+ * the opposite of `ptz.savePreset` / `snapshot.invalidateCache`, which are
227727
+ * `admin` because they change what the device IS.
227728
+ *
227729
+ * `protected` does not mean ungated: `protectedProcedure` runs the
227730
+ * `METHOD_ACCESS_MAP` scope check, and every method here is `scope: 'device'`
227731
+ * with `access: 'create'` and a `deviceId` in its input. So a caller needs a
227732
+ * grant that covers THAT camera at `create` — a `camera-viewer` (`view`
227733
+ * only) still cannot talk, and a grant on camera 5 cannot talk through
227734
+ * camera 7.
227735
+ *
227736
+ * Every method was `auth: 'admin'` from the initial commit, which made the
227737
+ * cap unreachable by every non-admin principal — `adminProcedure` throws
227738
+ * `FORBIDDEN: Admin required` BEFORE the scope check runs, so the scope
227739
+ * machinery generated for this cap (`METHOD_ACCESS_MAP`,
227740
+ * `DEVICE_SCOPED_CAPS`, `METHOD_DEVICE_SELECTORS`) was complete and dead. The
227741
+ * `camera-operator` scope preset has promised "PTZ control, intercom,
227742
+ * snapshots" since that same commit; the promise could not be kept. Recorded
227743
+ * as D289; `scripts/check-scope-preset-promises.ts` now fails the build if a
227744
+ * preset promises a cap no row of that preset can reach.
227745
+ */
227692
227746
  methods: {
227693
227747
  /**
227694
227748
  * Open a server-side WebRTC audio-only session. Returns an SDP
@@ -227701,7 +227755,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
227701
227755
  sdpOffer: zod.z.string()
227702
227756
  }), {
227703
227757
  kind: "mutation",
227704
- auth: "admin"
227758
+ auth: "protected"
227705
227759
  }),
227706
227760
  handleAnswer: require_sleep.method(zod.z.object({
227707
227761
  deviceId: zod.z.number(),
@@ -227709,7 +227763,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
227709
227763
  sdpAnswer: zod.z.string()
227710
227764
  }), zod.z.void(), {
227711
227765
  kind: "mutation",
227712
- auth: "admin"
227766
+ auth: "protected"
227713
227767
  }),
227714
227768
  /** Close explicitly. Server also auto-closes on 30s idle. */
227715
227769
  stopSession: require_sleep.method(zod.z.object({
@@ -227717,7 +227771,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
227717
227771
  sessionId: zod.z.string()
227718
227772
  }), zod.z.void(), {
227719
227773
  kind: "mutation",
227720
- auth: "admin"
227774
+ auth: "protected"
227721
227775
  }),
227722
227776
  /**
227723
227777
  * Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
@@ -227730,7 +227784,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
227730
227784
  */
227731
227785
  startTalkSession: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), zod.z.object({ sessionId: zod.z.string() }), {
227732
227786
  kind: "mutation",
227733
- auth: "admin"
227787
+ auth: "protected"
227734
227788
  }),
227735
227789
  /**
227736
227790
  * Push one chunk of talk-back audio onto the active talk session.
@@ -227765,12 +227819,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
227765
227819
  sequenceNumber: zod.z.number().int()
227766
227820
  }), zod.z.object({ accepted: zod.z.boolean() }), {
227767
227821
  kind: "mutation",
227768
- auth: "admin"
227822
+ auth: "protected"
227769
227823
  }),
227770
227824
  /** Close the raw-PCM talk session. Idempotent. */
227771
227825
  endTalkSession: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), zod.z.void(), {
227772
227826
  kind: "mutation",
227773
- auth: "admin"
227827
+ auth: "protected"
227774
227828
  })
227775
227829
  },
227776
227830
  events: { onStatusChanged: { data: zod.z.object({
@@ -245028,7 +245082,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
245028
245082
  {
245029
245083
  id: "camera-operator",
245030
245084
  label: "Camera operator",
245031
- description: "Everything `camera-viewer` does plus PTZ control, intercom, snapshots and recording start/stop. Cannot delete devices or recordings.",
245085
+ description: "Everything `camera-viewer` does plus live control of every granted camera \u2014 PTZ movement, intercom talk-back and on-demand snapshots. Cannot delete devices, and grants nothing on system-scope caps (recording control, users, addons).",
245032
245086
  rows: [{
245033
245087
  type: "category",
245034
245088
  target: "device",
@@ -401628,6 +401682,7 @@ var require_scope_access = __commonJS({
401628
401682
  "use strict";
401629
401683
  Object.defineProperty(exports, "__esModule", { value: true });
401630
401684
  exports.DEVICE_ENUMERATION_METHODS = void 0;
401685
+ exports.referencedDeviceIds = referencedDeviceIds;
401631
401686
  exports.checkScopeAccess = checkScopeAccess;
401632
401687
  var system_1 = require_dist3();
401633
401688
  var types_1 = require_dist4();
@@ -413553,6 +413608,25 @@ var require_trpc_router = __commonJS({
413553
413608
  }
413554
413609
  });
413555
413610
 
413611
+ // ../../server/backend/dist/api/trpc/trpc-error-device-tags.js
413612
+ var require_trpc_error_device_tags = __commonJS({
413613
+ "../../server/backend/dist/api/trpc/trpc-error-device-tags.js"(exports) {
413614
+ "use strict";
413615
+ Object.defineProperty(exports, "__esModule", { value: true });
413616
+ exports.trpcErrorDeviceTags = trpcErrorDeviceTags;
413617
+ var scope_access_js_1 = require_scope_access();
413618
+ function trpcErrorDeviceTags(path, input) {
413619
+ if (path === void 0)
413620
+ return {};
413621
+ const ids = (0, scope_access_js_1.referencedDeviceIds)(path, input);
413622
+ const first = ids[0];
413623
+ if (first === void 0)
413624
+ return {};
413625
+ return ids.length > 1 ? { deviceId: first, deviceCount: ids.length } : { deviceId: first };
413626
+ }
413627
+ }
413628
+ });
413629
+
413556
413630
  // ../../server/backend/dist/api/trpc/ws-request-census.js
413557
413631
  var require_ws_request_census = __commonJS({
413558
413632
  "../../server/backend/dist/api/trpc/ws-request-census.js"(exports) {
@@ -421635,6 +421709,7 @@ var require_main4 = __commonJS({
421635
421709
  var trpc_context_1 = require_trpc_context();
421636
421710
  var trpc_router_1 = require_trpc_router();
421637
421711
  var trpc_error_principal_1 = require_trpc_error_principal();
421712
+ var trpc_error_device_tags_1 = require_trpc_error_device_tags();
421638
421713
  var ws_request_census_1 = require_ws_request_census();
421639
421714
  var addon_route_jwt_gate_js_1 = require_addon_route_jwt_gate();
421640
421715
  var session_cookie_js_1 = require_session_cookie();
@@ -421933,9 +422008,13 @@ var require_main4 = __commonJS({
421933
422008
  (0, http_request_census_hook_1.recordTrpcHttpRequest)(httpRequestCensus, req, trpcCtx.user);
421934
422009
  return trpcCtx;
421935
422010
  },
421936
- onError: ({ path: trpcPath, error, ctx }) => {
422011
+ onError: ({ path: trpcPath, error, ctx, input }) => {
421937
422012
  const trpcLogger = app.get(logging_service_1.LoggingService).createLogger("tRPC");
421938
422013
  trpcLogger.warn("tRPC error", {
422014
+ // WHICH camera. A refusal on a device-scoped path that names no
422015
+ // deviceId cannot answer "one camera or all of them?" — the
422016
+ // question always asked first (2026-08-29, `intercom.startSession`).
422017
+ tags: (0, trpc_error_device_tags_1.trpcErrorDeviceTags)(trpcPath, input),
421939
422018
  meta: {
421940
422019
  code: error.code,
421941
422020
  path: trpcPath ?? "?",
@@ -422334,9 +422413,12 @@ var require_main4 = __commonJS({
422334
422413
  (0, ws_request_census_1.identifyWsCensusSession)(opts.res, (0, trpc_error_principal_1.describeTrpcPrincipal)(wsCtx.user));
422335
422414
  return wsCtx;
422336
422415
  },
422337
- onError: ({ path: trpcPath, error, ctx }) => {
422416
+ onError: ({ path: trpcPath, error, ctx, input }) => {
422338
422417
  const trpcLogger = app.get(logging_service_1.LoggingService).createLogger("tRPC:ws");
422339
422418
  trpcLogger.warn("tRPC error", {
422419
+ // The socket the viewer uses — this is the transport that logged the
422420
+ // four `intercom.startSession` refusals with no camera on them.
422421
+ tags: (0, trpc_error_device_tags_1.trpcErrorDeviceTags)(trpcPath, input),
422340
422422
  meta: {
422341
422423
  code: error.code,
422342
422424
  path: trpcPath ?? "?",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "camstack",
3
- "version": "1.2.59",
3
+ "version": "1.2.60",
4
4
  "description": "CLI tool for managing and running CamStack server",
5
5
  "keywords": [
6
6
  "camstack",