camstack 1.2.52 → 1.2.53

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.
@@ -14522,7 +14522,7 @@ function date4(params) {
14522
14522
  // ../../node_modules/zod/v4/classic/external.js
14523
14523
  config(en_default());
14524
14524
 
14525
- // ../types/dist/sleep-Dolp38qx.mjs
14525
+ // ../types/dist/sleep-C9C8EoK8.mjs
14526
14526
  var WELL_KNOWN_TABS = [
14527
14527
  {
14528
14528
  id: "overview",
@@ -17070,6 +17070,10 @@ var addonSettingsCapability = {
17070
17070
  kind: "mutation",
17071
17071
  auth: "admin"
17072
17072
  }),
17073
+ getIntegrationSettings: method(external_exports.object({
17074
+ addonId: external_exports.string(),
17075
+ nodeId: external_exports.string().optional()
17076
+ }), SettingsSchemaWithValuesSchema.nullable()),
17073
17077
  getDeviceSettings: method(external_exports.object({
17074
17078
  addonId: external_exports.string(),
17075
17079
  deviceId: external_exports.number(),
@@ -38487,6 +38491,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
38487
38491
  addonId: null,
38488
38492
  access: "view"
38489
38493
  },
38494
+ "addonSettings.getIntegrationSettings": {
38495
+ capName: "addon-settings",
38496
+ capScope: "system",
38497
+ addonId: null,
38498
+ access: "view"
38499
+ },
38490
38500
  "addonSettings.updateDeviceSettings": {
38491
38501
  capName: "addon-settings",
38492
38502
  capScope: "system",
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runDiscover
4
- } from "./chunk-YVH2KSTL.js";
4
+ } from "./chunk-P6IF6IX5.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-7T4UUMOZ.js");
41
+ await import("./launcher-3YAJ644E.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-7T4UUMOZ.js");
86
+ await import("./launcher-3YAJ644E.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-7QG523MP.js");
1133
+ const { discoverNodes, resolveHubFromDiscovered, filterHubNodes, DEFAULT_HUB_HTTPS_PORT } = await import("./discover-255INDLH.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-YVH2KSTL.js";
8
+ } from "./chunk-P6IF6IX5.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-B-mBrEz9.js
23637
- var require_dist_B_mBrEz9 = __commonJS({
23638
- "../system/dist/dist-B-mBrEz9.js"(exports) {
23636
+ // ../system/dist/dist-DdEXjTDo.js
23637
+ var require_dist_DdEXjTDo = __commonJS({
23638
+ "../system/dist/dist-DdEXjTDo.js"(exports) {
23639
23639
  "use strict";
23640
23640
  var zod = require_zod();
23641
23641
  var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
@@ -24231,6 +24231,40 @@ var require_dist_B_mBrEz9 = __commonJS({
24231
24231
  deviceSettingsSchema() {
24232
24232
  return null;
24233
24233
  }
24234
+ /**
24235
+ * INTEGRATION-LEVEL SETTINGS — declare which of this addon's global sections
24236
+ * ARE the configuration of its integration.
24237
+ *
24238
+ * Return the `ConfigSection.id`s, from {@link globalSettingsSchema}, that an
24239
+ * operator should find on the addon's integration page (System →
24240
+ * Integrations → <name>) rather than only in the cluster-wide list of every
24241
+ * addon. Empty (the default) means the addon has no integration-level
24242
+ * settings and no such surface is offered — this is opt-in, because whether
24243
+ * an addon's configuration IS its integration's configuration depends on the
24244
+ * nature of the integration.
24245
+ *
24246
+ * WHAT THIS IS NOT. It is not a scope. The selected sections keep living in
24247
+ * the ONE global schema, in the ONE addon store, written by the ONE
24248
+ * `updateGlobalSettings` path. There is deliberately no
24249
+ * `updateIntegrationSettings`: a second write path is how a surface acquires
24250
+ * a second store key, and this repo has shipped that twice (`btmPath@hub`,
24251
+ * D266). Selecting sections cannot introduce a key that selecting cannot.
24252
+ *
24253
+ * WHY IT IS A LIST OF SECTION IDS AND NOT A MARKER ON THE SECTION.
24254
+ * `ConfigFieldBase` used to carry `scope?: 'device' | 'global'` and it was
24255
+ * removed with the reason recorded at
24256
+ * `packages/types/src/interfaces/config-ui.ts:249` — *"a field's scope is
24257
+ * determined by WHICH schema it lives in, not by a field-level marker."* A
24258
+ * marker sprinkled across sections also has to borrow a field that already
24259
+ * means something else; borrowing `section.tab` put the literal word
24260
+ * "integration" into an operator-facing tab bar, because `tab` means "how to
24261
+ * GROUP this visually" and cannot also mean "where this lives" (D269
24262
+ * supersedes D268). One declaration, in one place, next to the schema whose
24263
+ * ids it names.
24264
+ */
24265
+ integrationSettingSections() {
24266
+ return [];
24267
+ }
24234
24268
  async getGlobalSettings(overlay, cap, nodeId) {
24235
24269
  const schema = this.globalSettingsSchema(cap);
24236
24270
  if (!schema) return { sections: [] };
@@ -24241,6 +24275,55 @@ var require_dist_B_mBrEz9 = __commonJS({
24241
24275
  } : projected);
24242
24276
  }
24243
24277
  /**
24278
+ * The integration-level view of this addon's settings: exactly the sections
24279
+ * named by {@link integrationSettingSections}, hydrated from the SAME store
24280
+ * `getGlobalSettings` reads, and narrowed to cluster-scoped fields.
24281
+ *
24282
+ * Returns `null` when the addon declared nothing — an addon that opts out has
24283
+ * no integration settings surface at all, rather than an empty one that reads
24284
+ * as a failed load.
24285
+ *
24286
+ * Three properties hold BY CONSTRUCTION, which is why they are here in core
24287
+ * and not in whichever UI happens to render this:
24288
+ *
24289
+ * 1. **One key.** The payload is a SUBSET of the global schema, so a field
24290
+ * shown here is the same field, with the same bare key, that the addon's
24291
+ * own page shows. There is no integration-specific writer — callers save
24292
+ * through `updateGlobalSettings` — so a second store key is unreachable,
24293
+ * not merely discouraged.
24294
+ * 2. **No node scope.** `perNode: true` fields are DROPPED. Their store key
24295
+ * is `<key>@<nodeId>` and an integration is not a node; whichever node
24296
+ * such a field silently picked would be a wrong answer for the operator
24297
+ * who opened the page (D266).
24298
+ * 3. **No silent typo.** A declared id that names no section throws. The
24299
+ * alternative — skip it — turns a rename into a surface that quietly
24300
+ * empties, which looks exactly like an addon with nothing to configure.
24301
+ */
24302
+ async getIntegrationSettings(nodeId) {
24303
+ const declared = this.integrationSettingSections();
24304
+ if (declared.length === 0) return null;
24305
+ const schema = this.globalSettingsSchema();
24306
+ if (!schema) throw new Error(`${this.constructor.name}: integrationSettingSections() names [${declared.join(", ")}] but globalSettingsSchema() returns null.`);
24307
+ const byId = new Map(schema.sections.map((section) => [section.id, section]));
24308
+ const sections = [];
24309
+ for (const id of declared) {
24310
+ const section = byId.get(id);
24311
+ if (!section) throw new Error(`${this.constructor.name}: integrationSettingSections() names unknown section "${id}". Known sections: [${[...byId.keys()].join(", ")}].`);
24312
+ const fields = dropPerNodeFields(section.fields);
24313
+ if (fields.length === 0) continue;
24314
+ sections.push({
24315
+ ...section,
24316
+ fields
24317
+ });
24318
+ }
24319
+ if (sections.length === 0) return null;
24320
+ const projected = await this.resolveGlobalStore(nodeId);
24321
+ return hydrateSchema({
24322
+ ...schema,
24323
+ sections
24324
+ }, projected);
24325
+ }
24326
+ /**
24244
24327
  * The raw addon store PROJECTED onto the target node's bare per-node keys:
24245
24328
  * every `perNode: true` field carries THAT node's scoped value on its bare
24246
24329
  * key (absent scoped key ⇒ key absent, so the schema `default` wins — no
@@ -24540,6 +24623,33 @@ var require_dist_B_mBrEz9 = __commonJS({
24540
24623
  return {};
24541
24624
  }
24542
24625
  };
24626
+ function dropPerNodeFields(fields) {
24627
+ const kept = [];
24628
+ for (const field of fields) {
24629
+ if (field.type === "group") {
24630
+ const inner = dropPerNodeFields(field.fields);
24631
+ if (inner.length > 0) kept.push({
24632
+ ...field,
24633
+ fields: inner
24634
+ });
24635
+ continue;
24636
+ }
24637
+ if (field.type === "sub-tabs") {
24638
+ const tabs = field.tabs.map((tab) => ({
24639
+ ...tab,
24640
+ fields: dropPerNodeFields(tab.fields)
24641
+ })).filter((tab) => tab.fields.length > 0);
24642
+ if (tabs.length > 0) kept.push({
24643
+ ...field,
24644
+ tabs
24645
+ });
24646
+ continue;
24647
+ }
24648
+ if ("perNode" in field && field.perNode === true) continue;
24649
+ kept.push(field);
24650
+ }
24651
+ return kept;
24652
+ }
24543
24653
  function collectPerNodeFieldKeys(fields) {
24544
24654
  const collected = [];
24545
24655
  for (const field of fields) {
@@ -27528,6 +27638,10 @@ var require_dist_B_mBrEz9 = __commonJS({
27528
27638
  kind: "mutation",
27529
27639
  auth: "admin"
27530
27640
  }),
27641
+ getIntegrationSettings: method(zod.z.object({
27642
+ addonId: zod.z.string(),
27643
+ nodeId: zod.z.string().optional()
27644
+ }), SettingsSchemaWithValuesSchema.nullable()),
27531
27645
  getDeviceSettings: method(zod.z.object({
27532
27646
  addonId: zod.z.string(),
27533
27647
  deviceId: zod.z.number(),
@@ -49658,6 +49772,12 @@ var require_dist_B_mBrEz9 = __commonJS({
49658
49772
  addonId: null,
49659
49773
  access: "view"
49660
49774
  },
49775
+ "addonSettings.getIntegrationSettings": {
49776
+ capName: "addon-settings",
49777
+ capScope: "system",
49778
+ addonId: null,
49779
+ access: "view"
49780
+ },
49661
49781
  "addonSettings.updateDeviceSettings": {
49662
49782
  capName: "addon-settings",
49663
49783
  capScope: "system",
@@ -58395,7 +58515,7 @@ var require_alerts_addon = __commonJS({
58395
58515
  [Symbol.toStringTag]: { value: "Module" }
58396
58516
  });
58397
58517
  require_chunk_Cek0wNdY();
58398
- var require_dist10 = require_dist_B_mBrEz9();
58518
+ var require_dist10 = require_dist_DdEXjTDo();
58399
58519
  function selectExpired(alerts, cutoffMs) {
58400
58520
  return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
58401
58521
  }
@@ -59214,7 +59334,7 @@ var require_console_logging = __commonJS({
59214
59334
  [Symbol.toStringTag]: { value: "Module" }
59215
59335
  });
59216
59336
  require_chunk_Cek0wNdY();
59217
- var require_dist10 = require_dist_B_mBrEz9();
59337
+ var require_dist10 = require_dist_DdEXjTDo();
59218
59338
  var require_formatter = require_formatter_DqAKDlvN();
59219
59339
  var LEVEL_RANK = {
59220
59340
  debug: 0,
@@ -59308,7 +59428,7 @@ var require_core_blocks_addon = __commonJS({
59308
59428
  "use strict";
59309
59429
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
59310
59430
  var require_chunk = require_chunk_Cek0wNdY();
59311
- var require_dist10 = require_dist_B_mBrEz9();
59431
+ var require_dist10 = require_dist_DdEXjTDo();
59312
59432
  var node_crypto = __require("crypto");
59313
59433
  var node_fs_promises = __require("fs/promises");
59314
59434
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -60205,28 +60325,88 @@ var require_core_blocks = __commonJS({
60205
60325
  }
60206
60326
  });
60207
60327
 
60208
- // ../system/dist/retired-settings-keys-Davtjo5p.js
60209
- var require_retired_settings_keys_Davtjo5p = __commonJS({
60210
- "../system/dist/retired-settings-keys-Davtjo5p.js"(exports) {
60328
+ // ../system/dist/retired-settings-keys-BXaTgLyZ.js
60329
+ var require_retired_settings_keys_BXaTgLyZ = __commonJS({
60330
+ "../system/dist/retired-settings-keys-BXaTgLyZ.js"(exports) {
60211
60331
  "use strict";
60212
- var require_dist10 = require_dist_B_mBrEz9();
60332
+ var require_dist10 = require_dist_DdEXjTDo();
60213
60333
  function settingsStoreIsAuthoritativeHere(env) {
60214
60334
  const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
60215
60335
  return raw === "" || raw === "hub";
60216
60336
  }
60217
- var RETIRED_SETTINGS_KEYS = [{
60218
- namespace: "detection-pipeline",
60219
- collection: "addon-settings",
60220
- row: "root",
60221
- keys: ["pipelineSteps", "pipelineEngine"],
60222
- perNodeKeys: [
60223
- "engineRuntime",
60224
- "engineBackend",
60225
- "engineDevice",
60226
- "probedBestEngine"
60227
- ],
60228
- reason: "global step/engine seed and the per-node engine cascade \u2014 both removed; the live per-camera dispatch path never read either"
60229
- }];
60337
+ var RETIRED_SETTINGS_KEYS = [
60338
+ {
60339
+ namespace: "detection-pipeline",
60340
+ collection: "addon-settings",
60341
+ row: "root",
60342
+ keys: ["pipelineSteps", "pipelineEngine"],
60343
+ perNodeKeys: [
60344
+ "engineRuntime",
60345
+ "engineBackend",
60346
+ "engineDevice",
60347
+ "probedBestEngine"
60348
+ ],
60349
+ reason: "global step/engine seed and the per-node engine cascade \u2014 both removed; the live per-camera dispatch path never read either"
60350
+ },
60351
+ {
60352
+ namespace: "pipeline-orchestrator",
60353
+ collection: "addon-settings",
60354
+ row: "root",
60355
+ keys: [
60356
+ "enabledNodes",
60357
+ "enabledDecoderNodes",
60358
+ "enabledAudioNodes",
60359
+ "remoteSourcingNodes",
60360
+ "nativeLeaseTtlMs"
60361
+ ],
60362
+ perNodeKeys: ["backend"],
60363
+ reason: 'the pre-singleton "enabled nodes" role model (node capability now lives per node in agent-settings and is derived by NodeTopology.refresh, never read from this row), `nativeLeaseTtlMs` superseded by `nativeLeaseActivityMs`, and the decoder `backend` selector whose resolver returns DEFAULT_DECODER_BACKEND unconditionally (decoder-backend.ts)'
60364
+ },
60365
+ {
60366
+ namespace: "stream-broker",
60367
+ collection: "addon-settings",
60368
+ row: "root",
60369
+ keys: [
60370
+ "maxDecodeFps",
60371
+ "initialReconnectDelayMs",
60372
+ "maxReconnectDelayMs"
60373
+ ],
60374
+ perNodeKeys: [],
60375
+ reason: "three form knobs deleted 2026-08-28 (D272): nothing capped decode fps, and the reconnect ladder has always run on the INITIAL/MAX_RECONNECT_DELAY_MS constants"
60376
+ },
60377
+ {
60378
+ namespace: "embedding-encoder",
60379
+ collection: "addon-settings",
60380
+ row: "root",
60381
+ keys: ["runtime", "backend"],
60382
+ perNodeKeys: [],
60383
+ reason: "two selects deleted 2026-08-28 (D272); their only reference was a withTags log field, and the CUDA/CoreML options named acceleration that has never existed \u2014 embedding inference is always ONNX in the embedded Python"
60384
+ },
60385
+ {
60386
+ namespace: "audio-analyzer",
60387
+ collection: "addon-settings",
60388
+ row: "root",
60389
+ keys: ["selectedAudioModel", "probedBestAudioBackend"],
60390
+ perNodeKeys: [],
60391
+ reason: "`selectedAudioModel` deleted 2026-08-28 (D272) \u2014 createAudioPipeline takes a backend and no model id, so it never reached inference; the BARE `probedBestAudioBackend` is pre-perNode residue that projectStore already drops (no bare fallback), so no node can ever read it"
60392
+ },
60393
+ {
60394
+ namespace: "decoder-nodeav",
60395
+ collection: "addon-settings",
60396
+ row: "root",
60397
+ keys: ["probedBestHwaccel"],
60398
+ perNodeKeys: [],
60399
+ reason: "bare pre-perNode residue \u2014 the live probe result is `probedBestHwaccel@<nodeId>` and `projectStore` deliberately drops the bare key, so nothing can read it"
60400
+ },
60401
+ {
60402
+ namespace: "pipeline-analytics",
60403
+ collection: "addon-settings",
60404
+ row: "root",
60405
+ keys: ["mediaAttachPolicy"],
60406
+ perNodeKeys: [],
60407
+ reason: "deleted 2026-08-28 (D272) \u2014 absent from MediaSettingsSchema, so the value was never even resolved, let alone read"
60408
+ }
60409
+ ];
60230
60410
  var RETIRED_SETTINGS_ROWS = [
60231
60411
  {
60232
60412
  collection: "addon-settings",
@@ -62363,8 +62543,8 @@ var require_device_manager_addon = __commonJS({
62363
62543
  [Symbol.toStringTag]: { value: "Module" }
62364
62544
  });
62365
62545
  require_chunk_Cek0wNdY();
62366
- var require_dist10 = require_dist_B_mBrEz9();
62367
- var require_retired_settings_keys = require_retired_settings_keys_Davtjo5p();
62546
+ var require_dist10 = require_dist_DdEXjTDo();
62547
+ var require_retired_settings_keys = require_retired_settings_keys_BXaTgLyZ();
62368
62548
  var node_crypto = __require("crypto");
62369
62549
  var _camstack_types_node = require_node();
62370
62550
  var JOB_HISTORY = 20;
@@ -67113,7 +67293,7 @@ var require_hub_forwarder = __commonJS({
67113
67293
  [Symbol.toStringTag]: { value: "Module" }
67114
67294
  });
67115
67295
  require_chunk_Cek0wNdY();
67116
- var require_dist10 = require_dist_B_mBrEz9();
67296
+ var require_dist10 = require_dist_DdEXjTDo();
67117
67297
  var require_formatter = require_formatter_DqAKDlvN();
67118
67298
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
67119
67299
  var HubForwarderDestination = class {
@@ -67250,7 +67430,7 @@ var require_liveness_monitor_addon = __commonJS({
67250
67430
  "use strict";
67251
67431
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
67252
67432
  require_chunk_Cek0wNdY();
67253
- var require_dist10 = require_dist_B_mBrEz9();
67433
+ var require_dist10 = require_dist_DdEXjTDo();
67254
67434
  var NO_DEVICES = "liveness:no-devices";
67255
67435
  var ALL_OFFLINE = "liveness:all-devices-offline";
67256
67436
  var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
@@ -67440,7 +67620,7 @@ var require_local_auth_addon = __commonJS({
67440
67620
  [Symbol.toStringTag]: { value: "Module" }
67441
67621
  });
67442
67622
  var require_chunk = require_chunk_Cek0wNdY();
67443
- var require_dist10 = require_dist_B_mBrEz9();
67623
+ var require_dist10 = require_dist_DdEXjTDo();
67444
67624
  var node_crypto = __require("crypto");
67445
67625
  node_crypto = require_chunk.__toESM(node_crypto);
67446
67626
  var crypto$1 = __require("crypto");
@@ -75253,7 +75433,7 @@ var require_loki_logging = __commonJS({
75253
75433
  [Symbol.toStringTag]: { value: "Module" }
75254
75434
  });
75255
75435
  require_chunk_Cek0wNdY();
75256
- var require_dist10 = require_dist_B_mBrEz9();
75436
+ var require_dist10 = require_dist_DdEXjTDo();
75257
75437
  function sanitizeLabelName(raw) {
75258
75438
  const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
75259
75439
  return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
@@ -75818,7 +75998,7 @@ var require_native_metrics_addon = __commonJS({
75818
75998
  [Symbol.toStringTag]: { value: "Module" }
75819
75999
  });
75820
76000
  var require_chunk = require_chunk_Cek0wNdY();
75821
- var require_dist10 = require_dist_B_mBrEz9();
76001
+ var require_dist10 = require_dist_DdEXjTDo();
75822
76002
  var node_fs_promises = __require("fs/promises");
75823
76003
  var node_child_process = __require("child_process");
75824
76004
  var node_util = __require("util");
@@ -77775,7 +77955,7 @@ var require_filesystem_storage_addon = __commonJS({
77775
77955
  [Symbol.toStringTag]: { value: "Module" }
77776
77956
  });
77777
77957
  var require_chunk = require_chunk_Cek0wNdY();
77778
- var require_dist10 = require_dist_B_mBrEz9();
77958
+ var require_dist10 = require_dist_DdEXjTDo();
77779
77959
  var node_crypto = __require("crypto");
77780
77960
  var node_fs_promises = __require("fs/promises");
77781
77961
  var node_path = __require("path");
@@ -78891,8 +79071,8 @@ var require_sqlite_settings_addon = __commonJS({
78891
79071
  [Symbol.toStringTag]: { value: "Module" }
78892
79072
  });
78893
79073
  var require_chunk = require_chunk_Cek0wNdY();
78894
- var require_dist10 = require_dist_B_mBrEz9();
78895
- var require_retired_settings_keys = require_retired_settings_keys_Davtjo5p();
79074
+ var require_dist10 = require_dist_DdEXjTDo();
79075
+ var require_retired_settings_keys = require_retired_settings_keys_BXaTgLyZ();
78896
79076
  var node_crypto = __require("crypto");
78897
79077
  var node_fs = __require("fs");
78898
79078
  var node_module = __require("module");
@@ -81171,7 +81351,7 @@ var require_storage_orchestrator_addon = __commonJS({
81171
81351
  [Symbol.toStringTag]: { value: "Module" }
81172
81352
  });
81173
81353
  var require_chunk = require_chunk_Cek0wNdY();
81174
- var require_dist10 = require_dist_B_mBrEz9();
81354
+ var require_dist10 = require_dist_DdEXjTDo();
81175
81355
  var node_crypto = __require("crypto");
81176
81356
  var node_fs_promises = __require("fs/promises");
81177
81357
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -83052,7 +83232,7 @@ var require_system_config_addon = __commonJS({
83052
83232
  [Symbol.toStringTag]: { value: "Module" }
83053
83233
  });
83054
83234
  require_chunk_Cek0wNdY();
83055
- var require_dist10 = require_dist_B_mBrEz9();
83235
+ var require_dist10 = require_dist_DdEXjTDo();
83056
83236
  var SECTION_TITLES = {
83057
83237
  server: "Server",
83058
83238
  auth: "Authentication"
@@ -101113,7 +101293,7 @@ var require_winston_logging = __commonJS({
101113
101293
  [Symbol.toStringTag]: { value: "Module" }
101114
101294
  });
101115
101295
  var require_chunk = require_chunk_Cek0wNdY();
101116
- var require_dist10 = require_dist_B_mBrEz9();
101296
+ var require_dist10 = require_dist_DdEXjTDo();
101117
101297
  var require_formatter = require_formatter_DqAKDlvN();
101118
101298
  var node_path = __require("path");
101119
101299
  node_path = require_chunk.__toESM(node_path);
@@ -103056,9 +103236,9 @@ var require_event_category_BaEgqJNv = __commonJS({
103056
103236
  }
103057
103237
  });
103058
103238
 
103059
- // ../types/dist/sleep-9d8tJRbO.js
103060
- var require_sleep_9d8tJRbO = __commonJS({
103061
- "../types/dist/sleep-9d8tJRbO.js"(exports) {
103239
+ // ../types/dist/sleep-DUxF5DdC.js
103240
+ var require_sleep_DUxF5DdC = __commonJS({
103241
+ "../types/dist/sleep-DUxF5DdC.js"(exports) {
103062
103242
  "use strict";
103063
103243
  var require_event_category = require_event_category_BaEgqJNv();
103064
103244
  var zod = require_zod();
@@ -103547,6 +103727,40 @@ var require_sleep_9d8tJRbO = __commonJS({
103547
103727
  deviceSettingsSchema() {
103548
103728
  return null;
103549
103729
  }
103730
+ /**
103731
+ * INTEGRATION-LEVEL SETTINGS — declare which of this addon's global sections
103732
+ * ARE the configuration of its integration.
103733
+ *
103734
+ * Return the `ConfigSection.id`s, from {@link globalSettingsSchema}, that an
103735
+ * operator should find on the addon's integration page (System →
103736
+ * Integrations → <name>) rather than only in the cluster-wide list of every
103737
+ * addon. Empty (the default) means the addon has no integration-level
103738
+ * settings and no such surface is offered — this is opt-in, because whether
103739
+ * an addon's configuration IS its integration's configuration depends on the
103740
+ * nature of the integration.
103741
+ *
103742
+ * WHAT THIS IS NOT. It is not a scope. The selected sections keep living in
103743
+ * the ONE global schema, in the ONE addon store, written by the ONE
103744
+ * `updateGlobalSettings` path. There is deliberately no
103745
+ * `updateIntegrationSettings`: a second write path is how a surface acquires
103746
+ * a second store key, and this repo has shipped that twice (`btmPath@hub`,
103747
+ * D266). Selecting sections cannot introduce a key that selecting cannot.
103748
+ *
103749
+ * WHY IT IS A LIST OF SECTION IDS AND NOT A MARKER ON THE SECTION.
103750
+ * `ConfigFieldBase` used to carry `scope?: 'device' | 'global'` and it was
103751
+ * removed with the reason recorded at
103752
+ * `packages/types/src/interfaces/config-ui.ts:249` — *"a field's scope is
103753
+ * determined by WHICH schema it lives in, not by a field-level marker."* A
103754
+ * marker sprinkled across sections also has to borrow a field that already
103755
+ * means something else; borrowing `section.tab` put the literal word
103756
+ * "integration" into an operator-facing tab bar, because `tab` means "how to
103757
+ * GROUP this visually" and cannot also mean "where this lives" (D269
103758
+ * supersedes D268). One declaration, in one place, next to the schema whose
103759
+ * ids it names.
103760
+ */
103761
+ integrationSettingSections() {
103762
+ return [];
103763
+ }
103550
103764
  async getGlobalSettings(overlay, cap, nodeId) {
103551
103765
  const schema = this.globalSettingsSchema(cap);
103552
103766
  if (!schema) return { sections: [] };
@@ -103557,6 +103771,55 @@ var require_sleep_9d8tJRbO = __commonJS({
103557
103771
  } : projected);
103558
103772
  }
103559
103773
  /**
103774
+ * The integration-level view of this addon's settings: exactly the sections
103775
+ * named by {@link integrationSettingSections}, hydrated from the SAME store
103776
+ * `getGlobalSettings` reads, and narrowed to cluster-scoped fields.
103777
+ *
103778
+ * Returns `null` when the addon declared nothing — an addon that opts out has
103779
+ * no integration settings surface at all, rather than an empty one that reads
103780
+ * as a failed load.
103781
+ *
103782
+ * Three properties hold BY CONSTRUCTION, which is why they are here in core
103783
+ * and not in whichever UI happens to render this:
103784
+ *
103785
+ * 1. **One key.** The payload is a SUBSET of the global schema, so a field
103786
+ * shown here is the same field, with the same bare key, that the addon's
103787
+ * own page shows. There is no integration-specific writer — callers save
103788
+ * through `updateGlobalSettings` — so a second store key is unreachable,
103789
+ * not merely discouraged.
103790
+ * 2. **No node scope.** `perNode: true` fields are DROPPED. Their store key
103791
+ * is `<key>@<nodeId>` and an integration is not a node; whichever node
103792
+ * such a field silently picked would be a wrong answer for the operator
103793
+ * who opened the page (D266).
103794
+ * 3. **No silent typo.** A declared id that names no section throws. The
103795
+ * alternative — skip it — turns a rename into a surface that quietly
103796
+ * empties, which looks exactly like an addon with nothing to configure.
103797
+ */
103798
+ async getIntegrationSettings(nodeId) {
103799
+ const declared = this.integrationSettingSections();
103800
+ if (declared.length === 0) return null;
103801
+ const schema = this.globalSettingsSchema();
103802
+ if (!schema) throw new Error(`${this.constructor.name}: integrationSettingSections() names [${declared.join(", ")}] but globalSettingsSchema() returns null.`);
103803
+ const byId = new Map(schema.sections.map((section) => [section.id, section]));
103804
+ const sections = [];
103805
+ for (const id of declared) {
103806
+ const section = byId.get(id);
103807
+ if (!section) throw new Error(`${this.constructor.name}: integrationSettingSections() names unknown section "${id}". Known sections: [${[...byId.keys()].join(", ")}].`);
103808
+ const fields = dropPerNodeFields(section.fields);
103809
+ if (fields.length === 0) continue;
103810
+ sections.push({
103811
+ ...section,
103812
+ fields
103813
+ });
103814
+ }
103815
+ if (sections.length === 0) return null;
103816
+ const projected = await this.resolveGlobalStore(nodeId);
103817
+ return hydrateSchema({
103818
+ ...schema,
103819
+ sections
103820
+ }, projected);
103821
+ }
103822
+ /**
103560
103823
  * The raw addon store PROJECTED onto the target node's bare per-node keys:
103561
103824
  * every `perNode: true` field carries THAT node's scoped value on its bare
103562
103825
  * key (absent scoped key ⇒ key absent, so the schema `default` wins — no
@@ -103856,6 +104119,33 @@ var require_sleep_9d8tJRbO = __commonJS({
103856
104119
  return {};
103857
104120
  }
103858
104121
  };
104122
+ function dropPerNodeFields(fields) {
104123
+ const kept = [];
104124
+ for (const field of fields) {
104125
+ if (field.type === "group") {
104126
+ const inner = dropPerNodeFields(field.fields);
104127
+ if (inner.length > 0) kept.push({
104128
+ ...field,
104129
+ fields: inner
104130
+ });
104131
+ continue;
104132
+ }
104133
+ if (field.type === "sub-tabs") {
104134
+ const tabs = field.tabs.map((tab) => ({
104135
+ ...tab,
104136
+ fields: dropPerNodeFields(tab.fields)
104137
+ })).filter((tab) => tab.fields.length > 0);
104138
+ if (tabs.length > 0) kept.push({
104139
+ ...field,
104140
+ tabs
104141
+ });
104142
+ continue;
104143
+ }
104144
+ if ("perNode" in field && field.perNode === true) continue;
104145
+ kept.push(field);
104146
+ }
104147
+ return kept;
104148
+ }
103859
104149
  function collectPerNodeFieldKeys(fields) {
103860
104150
  const collected = [];
103861
104151
  for (const field of fields) {
@@ -106612,7 +106902,7 @@ var require_addon = __commonJS({
106612
106902
  "use strict";
106613
106903
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
106614
106904
  var require_event_category = require_event_category_BaEgqJNv();
106615
- var require_sleep = require_sleep_9d8tJRbO();
106905
+ var require_sleep = require_sleep_DUxF5DdC();
106616
106906
  var require_err_msg = require_err_msg_COpsHMw2();
106617
106907
  var CAP_INPUT_DEFAULTS = Object.freeze({
106618
106908
  "addons": { "getLogs": { "limit": 100 } },
@@ -113490,12 +113780,12 @@ var require_dist2 = __commonJS({
113490
113780
  }
113491
113781
  });
113492
113782
 
113493
- // ../system/dist/manifest-python-deps-BV_Cy99l.js
113494
- var require_manifest_python_deps_BV_Cy99l = __commonJS({
113495
- "../system/dist/manifest-python-deps-BV_Cy99l.js"(exports) {
113783
+ // ../system/dist/manifest-python-deps-CivtrAha.js
113784
+ var require_manifest_python_deps_CivtrAha = __commonJS({
113785
+ "../system/dist/manifest-python-deps-CivtrAha.js"(exports) {
113496
113786
  "use strict";
113497
113787
  var require_chunk = require_chunk_Cek0wNdY();
113498
- require_dist_B_mBrEz9();
113788
+ require_dist_DdEXjTDo();
113499
113789
  var node_crypto = __require("crypto");
113500
113790
  node_crypto = require_chunk.__toESM(node_crypto);
113501
113791
  var _camstack_types_node = require_node();
@@ -124610,7 +124900,7 @@ var require_dist3 = __commonJS({
124610
124900
  "use strict";
124611
124901
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
124612
124902
  var require_chunk = require_chunk_Cek0wNdY();
124613
- var require_dist10 = require_dist_B_mBrEz9();
124903
+ var require_dist10 = require_dist_DdEXjTDo();
124614
124904
  var require_builtins_alerts_alerts_addon = require_alerts_addon();
124615
124905
  require_alerts();
124616
124906
  var require_formatter = require_formatter_DqAKDlvN();
@@ -124636,7 +124926,7 @@ var require_dist3 = __commonJS({
124636
124926
  var require_builtins_winston_logging_index = require_winston_logging();
124637
124927
  var require_file_data_plane = require_file_data_plane_DO8KbxCe();
124638
124928
  var require_tls$1 = require_tls_u8QCJCFE();
124639
- var require_manifest_python_deps = require_manifest_python_deps_BV_Cy99l();
124929
+ var require_manifest_python_deps = require_manifest_python_deps_CivtrAha();
124640
124930
  var require_resource_monitor = require_resource_monitor_CdnzxBLP();
124641
124931
  var require_custom_action_registry = require_custom_action_registry_jY0NOZK8();
124642
124932
  var zod = require_zod();
@@ -205352,7 +205642,7 @@ var require_dist4 = __commonJS({
205352
205642
  "use strict";
205353
205643
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
205354
205644
  var require_event_category = require_event_category_BaEgqJNv();
205355
- var require_sleep = require_sleep_9d8tJRbO();
205645
+ var require_sleep = require_sleep_DUxF5DdC();
205356
205646
  var require_canonical_hash = require_canonical_hash_DNV8S5ET();
205357
205647
  var require_enums2 = require_enums();
205358
205648
  var require_err_msg = require_err_msg_COpsHMw2();
@@ -209005,6 +209295,10 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
209005
209295
  kind: "mutation",
209006
209296
  auth: "admin"
209007
209297
  }),
209298
+ getIntegrationSettings: require_sleep.method(zod.z.object({
209299
+ addonId: zod.z.string(),
209300
+ nodeId: zod.z.string().optional()
209301
+ }), SettingsSchemaWithValuesSchema.nullable()),
209008
209302
  getDeviceSettings: require_sleep.method(zod.z.object({
209009
209303
  addonId: zod.z.string(),
209010
209304
  deviceId: zod.z.number(),
@@ -209284,8 +209578,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
209284
209578
  ];
209285
209579
  var DEFAULT_AUDIO_ANALYZER_CONFIG = {
209286
209580
  audioBackend: "auto",
209287
- probedBestAudioBackend: "",
209288
- selectedAudioModel: ""
209581
+ probedBestAudioBackend: ""
209289
209582
  };
209290
209583
  var audioAnalyzerCapability = {
209291
209584
  name: "audio-analyzer",
@@ -235180,6 +235473,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
235180
235473
  addonId: null,
235181
235474
  access: "view"
235182
235475
  },
235476
+ "addonSettings.getIntegrationSettings": {
235477
+ capName: "addon-settings",
235478
+ capScope: "system",
235479
+ addonId: null,
235480
+ access: "view"
235481
+ },
235183
235482
  "addonSettings.updateDeviceSettings": {
235184
235483
  capName: "addon-settings",
235185
235484
  capScope: "system",
@@ -243382,7 +243681,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
243382
243681
  },
243383
243682
  addonSettings: {
243384
243683
  getGlobalSettings: (input) => dispatch("addonSettings", "getGlobalSettings", "query", input),
243385
- updateGlobalSettings: (input) => dispatch("addonSettings", "updateGlobalSettings", "mutation", input)
243684
+ updateGlobalSettings: (input) => dispatch("addonSettings", "updateGlobalSettings", "mutation", input),
243685
+ getIntegrationSettings: (input) => dispatch("addonSettings", "getIntegrationSettings", "query", input)
243386
243686
  },
243387
243687
  addonWidgets: { listWidgets: (input) => dispatch("addonWidgets", "listWidgets", "query", input) },
243388
243688
  alerts: {
@@ -412451,6 +412751,16 @@ var require_addon_settings_provider = __commonJS({
412451
412751
  }
412452
412752
  return forkedUpdate(input.addonId, "updateGlobalSettings", { patch: input.patch, ...input.nodeId ? { nodeId: input.nodeId } : {} }, input.nodeId);
412453
412753
  },
412754
+ async getIntegrationSettings(input) {
412755
+ if (gateway.isInProcess(input.addonId, input.nodeId)) {
412756
+ const addon = getAddon(input.addonId);
412757
+ if (!addon || typeof addon.getIntegrationSettings !== "function")
412758
+ return null;
412759
+ const result = await addon.getIntegrationSettings(input.nodeId);
412760
+ return result ? reshapeForOutput(result) : null;
412761
+ }
412762
+ return forkedGet(input.addonId, "getIntegrationSettings", { ...input.nodeId ? { nodeId: input.nodeId } : {} }, input.nodeId);
412763
+ },
412454
412764
  async getDeviceSettings(input) {
412455
412765
  if (gateway.isInProcess(input.addonId, input.nodeId)) {
412456
412766
  const addon = getAddon(input.addonId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "camstack",
3
- "version": "1.2.52",
3
+ "version": "1.2.53",
4
4
  "description": "CLI tool for managing and running CamStack server",
5
5
  "keywords": [
6
6
  "camstack",