@camstack/system 1.2.268 → 1.2.269

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.
Files changed (59) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/autotrack/index.js +1 -1
  8. package/dist/builtins/autotrack/index.mjs +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  11. package/dist/builtins/camera-grid/grid-compositor.d.ts +8 -0
  12. package/dist/builtins/camera-grid/grid-instances.d.ts +5 -0
  13. package/dist/builtins/camera-grid/grid-profiles.d.ts +9 -0
  14. package/dist/builtins/camera-grid/grid-tile-feed.d.ts +12 -0
  15. package/dist/builtins/camera-grid/index.js +80 -14
  16. package/dist/builtins/camera-grid/index.mjs +80 -14
  17. package/dist/builtins/console-logging/index.js +1 -1
  18. package/dist/builtins/console-logging/index.mjs +1 -1
  19. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  20. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  21. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  22. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  23. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  24. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  25. package/dist/builtins/hub-forwarder/index.js +1 -1
  26. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  27. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  28. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  29. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  30. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  31. package/dist/builtins/local-network/local-network.addon.js +1 -1
  32. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  33. package/dist/builtins/loki-logging/index.js +1 -1
  34. package/dist/builtins/loki-logging/index.mjs +1 -1
  35. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  36. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  37. package/dist/builtins/platform-probe/index.js +1 -1
  38. package/dist/builtins/platform-probe/index.mjs +1 -1
  39. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  40. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  41. package/dist/builtins/snapshot/index.js +1 -1
  42. package/dist/builtins/snapshot/index.mjs +1 -1
  43. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  44. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  45. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  46. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  47. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  48. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  49. package/dist/builtins/system-config/system-config.addon.js +1 -1
  50. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  51. package/dist/builtins/winston-logging/index.js +1 -1
  52. package/dist/builtins/winston-logging/index.mjs +1 -1
  53. package/dist/{dist-DWNlc-PG.js → dist-BTpfLiUX.js} +16 -1
  54. package/dist/{dist-BYnnRAYv.mjs → dist-CqmImT7i.mjs} +16 -1
  55. package/dist/index.js +1 -1
  56. package/dist/index.mjs +1 -1
  57. package/dist/{retired-settings-keys-C_MtEB25.mjs → retired-settings-keys-C8nV-AUi.mjs} +1 -1
  58. package/dist/{retired-settings-keys-BjSmLqyQ.js → retired-settings-keys-D_DIuT_N.js} +1 -1
  59. package/package.json +1 -1
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { P as addonPagesCapability, Pn as EventCategory, an as errMsg, sn as BaseAddon } from "../../dist-BYnnRAYv.mjs";
1
+ import { P as addonPagesCapability, Pn as EventCategory, an as errMsg, sn as BaseAddon } from "../../dist-CqmImT7i.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { I as addonWidgetsCapability, Pn as EventCategory, an as errMsg, sn as BaseAddon } from "../../dist-BYnnRAYv.mjs";
1
+ import { I as addonWidgetsCapability, Pn as EventCategory, an as errMsg, sn as BaseAddon } from "../../dist-CqmImT7i.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  //#region src/builtins/alerts/prune-policy.ts
8
8
  /**
9
9
  * Ids older than `cutoffMs`, oldest first.
@@ -1,4 +1,4 @@
1
- import { L as alertsCapability, Pn as EventCategory, an as errMsg, bn as createEvent, n as AlertSchema, sn as BaseAddon } from "../../dist-BYnnRAYv.mjs";
1
+ import { L as alertsCapability, Pn as EventCategory, an as errMsg, bn as createEvent, n as AlertSchema, sn as BaseAddon } from "../../dist-CqmImT7i.mjs";
2
2
  //#region src/builtins/alerts/prune-policy.ts
3
3
  /**
4
4
  * Ids older than `cutoffMs`, oldest first.
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  //#region src/builtins/autotrack/autotrack-config.ts
8
8
  var DEFAULT_AUTOTRACK_LOOP_CONFIG = {
9
9
  deadZoneFraction: .5,
@@ -1,4 +1,4 @@
1
- import { B as bareAddonId, En as isEvent, It as ptzAutotrackCapability, Pn as EventCategory, an as errMsg, h as FIRST_LEVEL_MACRO_CLASSES, sn as BaseAddon } from "../../dist-BYnnRAYv.mjs";
1
+ import { B as bareAddonId, En as isEvent, It as ptzAutotrackCapability, Pn as EventCategory, an as errMsg, h as FIRST_LEVEL_MACRO_CLASSES, sn as BaseAddon } from "../../dist-CqmImT7i.mjs";
2
2
  //#region src/builtins/autotrack/autotrack-config.ts
3
3
  var DEFAULT_AUTOTRACK_LOOP_CONFIG = {
4
4
  deadZoneFraction: .5,
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let buffer = require("buffer");
9
9
  let node_fs_promises = require("node:fs/promises");
@@ -1,4 +1,4 @@
1
- import { Dt as mayWriteToLocation, Pn as EventCategory, Zt as storageOccupancyCapability, sn as BaseAddon, z as backupCapability } from "../../dist-BYnnRAYv.mjs";
1
+ import { Dt as mayWriteToLocation, Pn as EventCategory, Zt as storageOccupancyCapability, sn as BaseAddon, z as backupCapability } from "../../dist-CqmImT7i.mjs";
2
2
  import { randomBytes, randomUUID } from "node:crypto";
3
3
  import { Buffer as Buffer$1 } from "buffer";
4
4
  import * as fsp from "node:fs/promises";
@@ -31,6 +31,14 @@ export declare class GridCompositor {
31
31
  * flicker. The canvas is never cleared between ticks for the same reason.
32
32
  */
33
33
  tick(): boolean;
34
+ /**
35
+ * A tile's decoder was replaced: drop its half frame, keep its picture.
36
+ *
37
+ * Without this the new decoder's bytes would be appended to the old one's
38
+ * partial frame and every frame after would be sheared — raw video has no
39
+ * framing to re-sync on.
40
+ */
41
+ resetTile(tileId: string): void;
34
42
  /** After this, a tick emits nothing — a dead child must not keep writing. */
35
43
  close(): void;
36
44
  }
@@ -26,6 +26,11 @@ declare const GridInstanceSchema: z.ZodObject<{
26
26
  name: z.ZodString;
27
27
  enabled: z.ZodBoolean;
28
28
  fps: z.ZodNumber;
29
+ publishedProfiles: z.ZodDefault<z.ZodArray<z.ZodEnum<{
30
+ high: "high";
31
+ mid: "mid";
32
+ low: "low";
33
+ }>>>;
29
34
  cells: z.ZodDefault<z.ZodArray<z.ZodObject<{
30
35
  deviceId: z.ZodNumber;
31
36
  source: z.ZodObject<{
@@ -27,6 +27,13 @@ export interface GridSourceCell {
27
27
  }
28
28
  export interface GridProfilePlansInput {
29
29
  readonly cells: readonly GridSourceCell[];
30
+ /**
31
+ * Which profiles the operator wants published. A profile that is not in here
32
+ * is still SIZED and reported — the editor shows what it would come out as,
33
+ * so turning it on is a decision made with the number in view — but it is not
34
+ * offered to the broker.
35
+ */
36
+ readonly publishedProfiles: readonly CamProfile[];
30
37
  /**
31
38
  * Which streams this source serves, or `null` when nobody could say. `null`
32
39
  * is not an empty list: an unanswerable question is a refusal, and saying so
@@ -38,6 +45,8 @@ export type GridProfilePlan = {
38
45
  readonly profile: CamProfile;
39
46
  readonly offered: true;
40
47
  readonly canvas: PixelSize;
48
+ /** False ⇒ serviceable, sized, and deliberately not published. */
49
+ readonly published: boolean;
41
50
  } | {
42
51
  readonly profile: CamProfile;
43
52
  readonly offered: false;
@@ -33,6 +33,18 @@ export declare class GridTileFeed {
33
33
  latest(): Buffer | null;
34
34
  /** Has a new frame arrived since the last {@link latest}? */
35
35
  hasFresh(): boolean;
36
+ /**
37
+ * Forget the half frame, keep the picture.
38
+ *
39
+ * Raw video has no framing: the feed cuts on a fixed length, so bytes from a
40
+ * NEW decoder concatenated onto a partial frame from the old one would be
41
+ * misaligned for ever — every frame after it sheared, with nothing to
42
+ * re-sync on. Called whenever a tile's decoder is replaced.
43
+ *
44
+ * The newest frame is kept deliberately: the cell holds its last picture
45
+ * while its decoder comes back, instead of blinking to black.
46
+ */
47
+ resetPartial(): void;
36
48
  /** For the bound in the spec: one frame plus at most a partial one. */
37
49
  bufferedBytes(): number;
38
50
  }
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  let zod = require("zod");
8
8
  let _camstack_types_node = require("@camstack/types/node");
9
9
  let node_path = require("node:path");
@@ -273,6 +273,23 @@ var GridInstanceSchema = zod.z.object({
273
273
  */
274
274
  fps: zod.z.number().int().min(1).max(60),
275
275
  /**
276
+ * Which profiles this grid PUBLISHES, of the ones it could serve.
277
+ *
278
+ * Default `mid` + `low`, and the default is the finding: a grid's `high` is
279
+ * composed of its sources' `high`, so on this fleet it is a 4K canvas built
280
+ * from 4K decodes — a thing to opt into, not to be handed by a viewer's
281
+ * adaptive climbing to the top rung it can see.
282
+ *
283
+ * Absent on rows written before this, which is why the default lives in the
284
+ * schema rather than at the call site: an old grid starts publishing the two
285
+ * cheap rungs without anyone touching it.
286
+ */
287
+ publishedProfiles: zod.z.array(zod.z.enum([
288
+ "high",
289
+ "mid",
290
+ "low"
291
+ ])).max(3).default(["mid", "low"]),
292
+ /**
276
293
  * Empty is the NORMAL freshly-created state: a grid is created from the
277
294
  * integration page with a name only, and laid out afterwards in the camera's
278
295
  * own settings. A grid with no cells declares its camera and emits nothing.
@@ -1161,7 +1178,8 @@ function gridProfilePlans(input) {
1161
1178
  return {
1162
1179
  profile,
1163
1180
  offered: true,
1164
- canvas
1181
+ canvas,
1182
+ published: input.publishedProfiles.includes(profile)
1165
1183
  };
1166
1184
  });
1167
1185
  }
@@ -2059,6 +2077,20 @@ var GridTileFeed = class {
2059
2077
  hasFresh() {
2060
2078
  return this.fresh;
2061
2079
  }
2080
+ /**
2081
+ * Forget the half frame, keep the picture.
2082
+ *
2083
+ * Raw video has no framing: the feed cuts on a fixed length, so bytes from a
2084
+ * NEW decoder concatenated onto a partial frame from the old one would be
2085
+ * misaligned for ever — every frame after it sheared, with nothing to
2086
+ * re-sync on. Called whenever a tile's decoder is replaced.
2087
+ *
2088
+ * The newest frame is kept deliberately: the cell holds its last picture
2089
+ * while its decoder comes back, instead of blinking to black.
2090
+ */
2091
+ resetPartial() {
2092
+ this.partial = Buffer.alloc(0);
2093
+ }
2062
2094
  /** For the bound in the spec: one frame plus at most a partial one. */
2063
2095
  bufferedBytes() {
2064
2096
  return (this.newest?.length ?? 0) + this.partial.length;
@@ -2142,6 +2174,16 @@ var GridCompositor = class {
2142
2174
  }
2143
2175
  return this.deps.write(this.canvas.buffer);
2144
2176
  }
2177
+ /**
2178
+ * A tile's decoder was replaced: drop its half frame, keep its picture.
2179
+ *
2180
+ * Without this the new decoder's bytes would be appended to the old one's
2181
+ * partial frame and every frame after would be sheared — raw video has no
2182
+ * framing to re-sync on.
2183
+ */
2184
+ resetTile(tileId) {
2185
+ this.placed.get(tileId)?.feed.resetPartial();
2186
+ }
2145
2187
  /** After this, a tick emits nothing — a dead child must not keep writing. */
2146
2188
  close() {
2147
2189
  this.closed = true;
@@ -2328,13 +2370,22 @@ function gridCanvasEncoderInvocation(input) {
2328
2370
  * is how a stalled encoder becomes an OOM, and a dropped frame in a live
2329
2371
  * composite costs nothing anybody can see.
2330
2372
  *
2331
- * ## Any child exiting ends the composition
2373
+ * ## A tile comes back; only the ENCODER ends the composition
2374
+ *
2375
+ * Every child exiting used to end it, on the reasoning that a frozen tile is a
2376
+ * nicer picture and a worse fact. In practice it produced neither: a 4K source
2377
+ * ended mid-run (`code=0` — the input simply finished), the composition tore
2378
+ * down, the consumer re-dialled, and the grid served nothing but restarts. That
2379
+ * is what the operator saw as the `auto` rung being stuck.
2380
+ *
2381
+ * A canvas makes the better answer cheap: while a decoder comes back, its cell
2382
+ * holds the last frame it produced and every other cell carries on. The bound
2383
+ * (`TILE_MAX_RESTARTS`) keeps a source that is genuinely gone from respawning
2384
+ * for ever — and when it is reached, the composition does end, with the tile
2385
+ * named.
2332
2386
  *
2333
- * Same policy as the single child (`maxRestarts: 0`): the session tells its
2334
- * consumers why and tears down, and the next read starts a cold composition
2335
- * with freshly acquired sources. Leaving a dead tile frozen while the others
2336
- * run would be a nicer picture and a worse fact — the operator would be looking
2337
- * at a camera that stopped minutes ago with nothing saying so.
2387
+ * The encoder is different: nothing downstream of it exists without it, so its
2388
+ * exit is the composition's exit, first time.
2338
2389
  */
2339
2390
  /**
2340
2391
  * The encoder must produce its first bytes quickly, because the tick feeds it a
@@ -2348,6 +2399,15 @@ var ENCODER_FIRST_DATA_MS = 5e3;
2348
2399
  * already on screen.
2349
2400
  */
2350
2401
  var DECODER_FIRST_DATA_MS = 15e3;
2402
+ /**
2403
+ * How many times a tile's decoder may come back before the composition gives up
2404
+ * on it.
2405
+ *
2406
+ * The bound exists so a source that is genuinely gone still ends the
2407
+ * composition rather than respawning for ever; below it, a decoder that dies is
2408
+ * simply replaced while its cell holds the last frame it produced.
2409
+ */
2410
+ var TILE_MAX_RESTARTS = 5;
2351
2411
  async function startGridCompositorChild(ctx, plan, sources, onEnded) {
2352
2412
  const tilePlan = buildGridTilePlan({
2353
2413
  canvas: {
@@ -2457,14 +2517,17 @@ async function startGridCompositorChild(ctx, plan, sources, onEnded) {
2457
2517
  height: tile.size.height
2458
2518
  },
2459
2519
  firstDataTimeoutMs: DECODER_FIRST_DATA_MS,
2460
- maxRestarts: 0,
2520
+ maxRestarts: TILE_MAX_RESTARTS,
2461
2521
  onChild: (child) => {
2462
2522
  child.stdout?.on("data", (chunk) => {
2463
2523
  compositor.onTile(tile.id, chunk);
2464
2524
  });
2465
2525
  child.stderr?.on("data", () => {});
2466
2526
  },
2467
- onExit: (exit) => end(`tile ${tile.id}:${exit.classification}`)
2527
+ onExit: () => {
2528
+ compositor.resetTile(tile.id);
2529
+ },
2530
+ onGiveUp: (error) => end(`tile ${tile.id}: ${error.message}`)
2468
2531
  });
2469
2532
  decoders.push(decoder);
2470
2533
  await decoder.start();
@@ -2831,6 +2894,7 @@ var CameraGridAddon = class extends require_dist.BaseAddon {
2831
2894
  }
2832
2895
  for (const instance of instances) {
2833
2896
  const plans = gridProfilePlans({
2897
+ publishedProfiles: instance.publishedProfiles,
2834
2898
  cells: instance.cells.map((cell) => ({
2835
2899
  deviceId: cell.deviceId,
2836
2900
  source: cell.source,
@@ -2875,7 +2939,7 @@ var CameraGridAddon = class extends require_dist.BaseAddon {
2875
2939
  tags: { deviceId },
2876
2940
  meta: {
2877
2941
  instanceId: instance.id,
2878
- offered: Object.fromEntries(plans.filter((row) => row.offered).map((row) => [row.profile, row.offered ? `${String(row.canvas.width)}x${String(row.canvas.height)}` : ""]))
2942
+ offered: Object.fromEntries(plans.filter((row) => row.offered && row.published).map((row) => [row.profile, row.offered ? `${String(row.canvas.width)}x${String(row.canvas.height)}` : ""]))
2879
2943
  }
2880
2944
  });
2881
2945
  }
@@ -2902,7 +2966,7 @@ var CameraGridAddon = class extends require_dist.BaseAddon {
2902
2966
  for (const profile of GRID_PROFILES) {
2903
2967
  const key = sessionKey(instanceId, profile);
2904
2968
  const plan = this.planFor(instanceId, profile);
2905
- if (plan === null || !plan.offered) {
2969
+ if (plan === null || !plan.offered || !plan.published) {
2906
2970
  const stale = this.sessions.get(key);
2907
2971
  if (stale) {
2908
2972
  this.sessions.delete(key);
@@ -2972,7 +3036,7 @@ var CameraGridAddon = class extends require_dist.BaseAddon {
2972
3036
  resolvePlan: () => {
2973
3037
  const instance = this.instanceById(instanceId);
2974
3038
  const plan = this.planFor(instanceId, profile);
2975
- if (!instance || plan === null || !plan.offered) return null;
3039
+ if (!instance || plan === null || !plan.offered || !plan.published) return null;
2976
3040
  return gridPlanFor(instance, {
2977
3041
  encoder: "libx264",
2978
3042
  decodeHwAccel: null,
@@ -3063,7 +3127,7 @@ var CameraGridAddon = class extends require_dist.BaseAddon {
3063
3127
  const relay = this.relay;
3064
3128
  const plans = this.plansFor(instanceId);
3065
3129
  if (!instance || !relay || plans === null) return null;
3066
- return plans.filter((row) => row.offered).map((row) => gridStreamDescriptor({
3130
+ return plans.filter((row) => row.offered && row.published).map((row) => gridStreamDescriptor({
3067
3131
  profile: row.profile,
3068
3132
  width: row.offered ? row.canvas.width : 0,
3069
3133
  height: row.offered ? row.canvas.height : 0,
@@ -3098,6 +3162,7 @@ var CameraGridAddon = class extends require_dist.BaseAddon {
3098
3162
  profiles: (this.plansByInstanceId.get(instance.id) ?? []).map((row) => ({
3099
3163
  profile: row.profile,
3100
3164
  offered: row.offered,
3165
+ published: row.offered && row.published,
3101
3166
  missingSources: row.offered ? [] : [...row.missingSources],
3102
3167
  canvas: row.offered ? `${String(row.canvas.width)}x${String(row.canvas.height)}` : ""
3103
3168
  }))
@@ -3110,6 +3175,7 @@ var CameraGridAddon = class extends require_dist.BaseAddon {
3110
3175
  ...instance,
3111
3176
  ...input.name !== void 0 ? { name: input.name.trim() } : {},
3112
3177
  ...input.fps !== void 0 ? { fps: input.fps } : {},
3178
+ ...input.publishedProfiles !== void 0 ? { publishedProfiles: [...input.publishedProfiles] } : {},
3113
3179
  cells: input.cells.map((cell) => ({ ...cell }))
3114
3180
  };
3115
3181
  this.ctx.logger.info("camera grid: layout saved", {
@@ -1,4 +1,4 @@
1
- import { $t as streamCatalogCapability, At as motionCapability, Jt as snapshotCapability, Pn as EventCategory, U as cameraGridLayoutCapability, Y as declarationOwnerNodeId, an as errMsg, b as MOTION_CLOSE_AFTER_MS, bn as createEvent, dn as DeviceFeature, f as DERIVED_CAMERA_SILENCED_CAP_NAMES, i as BaseDevice, on as buildFfmpegArgs, p as DeclaredDevices, pn as DeviceType, sn as BaseAddon, wn as hydrateSchema } from "../../dist-BYnnRAYv.mjs";
1
+ import { $t as streamCatalogCapability, At as motionCapability, Jt as snapshotCapability, Pn as EventCategory, U as cameraGridLayoutCapability, Y as declarationOwnerNodeId, an as errMsg, b as MOTION_CLOSE_AFTER_MS, bn as createEvent, dn as DeviceFeature, f as DERIVED_CAMERA_SILENCED_CAP_NAMES, i as BaseDevice, on as buildFfmpegArgs, p as DeclaredDevices, pn as DeviceType, sn as BaseAddon, wn as hydrateSchema } from "../../dist-CqmImT7i.mjs";
2
2
  import { z } from "zod";
3
3
  import { FfmpegProcess } from "@camstack/types/node";
4
4
  import { join } from "node:path";
@@ -268,6 +268,23 @@ var GridInstanceSchema = z.object({
268
268
  */
269
269
  fps: z.number().int().min(1).max(60),
270
270
  /**
271
+ * Which profiles this grid PUBLISHES, of the ones it could serve.
272
+ *
273
+ * Default `mid` + `low`, and the default is the finding: a grid's `high` is
274
+ * composed of its sources' `high`, so on this fleet it is a 4K canvas built
275
+ * from 4K decodes — a thing to opt into, not to be handed by a viewer's
276
+ * adaptive climbing to the top rung it can see.
277
+ *
278
+ * Absent on rows written before this, which is why the default lives in the
279
+ * schema rather than at the call site: an old grid starts publishing the two
280
+ * cheap rungs without anyone touching it.
281
+ */
282
+ publishedProfiles: z.array(z.enum([
283
+ "high",
284
+ "mid",
285
+ "low"
286
+ ])).max(3).default(["mid", "low"]),
287
+ /**
271
288
  * Empty is the NORMAL freshly-created state: a grid is created from the
272
289
  * integration page with a name only, and laid out afterwards in the camera's
273
290
  * own settings. A grid with no cells declares its camera and emits nothing.
@@ -1156,7 +1173,8 @@ function gridProfilePlans(input) {
1156
1173
  return {
1157
1174
  profile,
1158
1175
  offered: true,
1159
- canvas
1176
+ canvas,
1177
+ published: input.publishedProfiles.includes(profile)
1160
1178
  };
1161
1179
  });
1162
1180
  }
@@ -2054,6 +2072,20 @@ var GridTileFeed = class {
2054
2072
  hasFresh() {
2055
2073
  return this.fresh;
2056
2074
  }
2075
+ /**
2076
+ * Forget the half frame, keep the picture.
2077
+ *
2078
+ * Raw video has no framing: the feed cuts on a fixed length, so bytes from a
2079
+ * NEW decoder concatenated onto a partial frame from the old one would be
2080
+ * misaligned for ever — every frame after it sheared, with nothing to
2081
+ * re-sync on. Called whenever a tile's decoder is replaced.
2082
+ *
2083
+ * The newest frame is kept deliberately: the cell holds its last picture
2084
+ * while its decoder comes back, instead of blinking to black.
2085
+ */
2086
+ resetPartial() {
2087
+ this.partial = Buffer.alloc(0);
2088
+ }
2057
2089
  /** For the bound in the spec: one frame plus at most a partial one. */
2058
2090
  bufferedBytes() {
2059
2091
  return (this.newest?.length ?? 0) + this.partial.length;
@@ -2137,6 +2169,16 @@ var GridCompositor = class {
2137
2169
  }
2138
2170
  return this.deps.write(this.canvas.buffer);
2139
2171
  }
2172
+ /**
2173
+ * A tile's decoder was replaced: drop its half frame, keep its picture.
2174
+ *
2175
+ * Without this the new decoder's bytes would be appended to the old one's
2176
+ * partial frame and every frame after would be sheared — raw video has no
2177
+ * framing to re-sync on.
2178
+ */
2179
+ resetTile(tileId) {
2180
+ this.placed.get(tileId)?.feed.resetPartial();
2181
+ }
2140
2182
  /** After this, a tick emits nothing — a dead child must not keep writing. */
2141
2183
  close() {
2142
2184
  this.closed = true;
@@ -2323,13 +2365,22 @@ function gridCanvasEncoderInvocation(input) {
2323
2365
  * is how a stalled encoder becomes an OOM, and a dropped frame in a live
2324
2366
  * composite costs nothing anybody can see.
2325
2367
  *
2326
- * ## Any child exiting ends the composition
2368
+ * ## A tile comes back; only the ENCODER ends the composition
2369
+ *
2370
+ * Every child exiting used to end it, on the reasoning that a frozen tile is a
2371
+ * nicer picture and a worse fact. In practice it produced neither: a 4K source
2372
+ * ended mid-run (`code=0` — the input simply finished), the composition tore
2373
+ * down, the consumer re-dialled, and the grid served nothing but restarts. That
2374
+ * is what the operator saw as the `auto` rung being stuck.
2375
+ *
2376
+ * A canvas makes the better answer cheap: while a decoder comes back, its cell
2377
+ * holds the last frame it produced and every other cell carries on. The bound
2378
+ * (`TILE_MAX_RESTARTS`) keeps a source that is genuinely gone from respawning
2379
+ * for ever — and when it is reached, the composition does end, with the tile
2380
+ * named.
2327
2381
  *
2328
- * Same policy as the single child (`maxRestarts: 0`): the session tells its
2329
- * consumers why and tears down, and the next read starts a cold composition
2330
- * with freshly acquired sources. Leaving a dead tile frozen while the others
2331
- * run would be a nicer picture and a worse fact — the operator would be looking
2332
- * at a camera that stopped minutes ago with nothing saying so.
2382
+ * The encoder is different: nothing downstream of it exists without it, so its
2383
+ * exit is the composition's exit, first time.
2333
2384
  */
2334
2385
  /**
2335
2386
  * The encoder must produce its first bytes quickly, because the tick feeds it a
@@ -2343,6 +2394,15 @@ var ENCODER_FIRST_DATA_MS = 5e3;
2343
2394
  * already on screen.
2344
2395
  */
2345
2396
  var DECODER_FIRST_DATA_MS = 15e3;
2397
+ /**
2398
+ * How many times a tile's decoder may come back before the composition gives up
2399
+ * on it.
2400
+ *
2401
+ * The bound exists so a source that is genuinely gone still ends the
2402
+ * composition rather than respawning for ever; below it, a decoder that dies is
2403
+ * simply replaced while its cell holds the last frame it produced.
2404
+ */
2405
+ var TILE_MAX_RESTARTS = 5;
2346
2406
  async function startGridCompositorChild(ctx, plan, sources, onEnded) {
2347
2407
  const tilePlan = buildGridTilePlan({
2348
2408
  canvas: {
@@ -2452,14 +2512,17 @@ async function startGridCompositorChild(ctx, plan, sources, onEnded) {
2452
2512
  height: tile.size.height
2453
2513
  },
2454
2514
  firstDataTimeoutMs: DECODER_FIRST_DATA_MS,
2455
- maxRestarts: 0,
2515
+ maxRestarts: TILE_MAX_RESTARTS,
2456
2516
  onChild: (child) => {
2457
2517
  child.stdout?.on("data", (chunk) => {
2458
2518
  compositor.onTile(tile.id, chunk);
2459
2519
  });
2460
2520
  child.stderr?.on("data", () => {});
2461
2521
  },
2462
- onExit: (exit) => end(`tile ${tile.id}:${exit.classification}`)
2522
+ onExit: () => {
2523
+ compositor.resetTile(tile.id);
2524
+ },
2525
+ onGiveUp: (error) => end(`tile ${tile.id}: ${error.message}`)
2463
2526
  });
2464
2527
  decoders.push(decoder);
2465
2528
  await decoder.start();
@@ -2826,6 +2889,7 @@ var CameraGridAddon = class extends BaseAddon {
2826
2889
  }
2827
2890
  for (const instance of instances) {
2828
2891
  const plans = gridProfilePlans({
2892
+ publishedProfiles: instance.publishedProfiles,
2829
2893
  cells: instance.cells.map((cell) => ({
2830
2894
  deviceId: cell.deviceId,
2831
2895
  source: cell.source,
@@ -2870,7 +2934,7 @@ var CameraGridAddon = class extends BaseAddon {
2870
2934
  tags: { deviceId },
2871
2935
  meta: {
2872
2936
  instanceId: instance.id,
2873
- offered: Object.fromEntries(plans.filter((row) => row.offered).map((row) => [row.profile, row.offered ? `${String(row.canvas.width)}x${String(row.canvas.height)}` : ""]))
2937
+ offered: Object.fromEntries(plans.filter((row) => row.offered && row.published).map((row) => [row.profile, row.offered ? `${String(row.canvas.width)}x${String(row.canvas.height)}` : ""]))
2874
2938
  }
2875
2939
  });
2876
2940
  }
@@ -2897,7 +2961,7 @@ var CameraGridAddon = class extends BaseAddon {
2897
2961
  for (const profile of GRID_PROFILES) {
2898
2962
  const key = sessionKey(instanceId, profile);
2899
2963
  const plan = this.planFor(instanceId, profile);
2900
- if (plan === null || !plan.offered) {
2964
+ if (plan === null || !plan.offered || !plan.published) {
2901
2965
  const stale = this.sessions.get(key);
2902
2966
  if (stale) {
2903
2967
  this.sessions.delete(key);
@@ -2967,7 +3031,7 @@ var CameraGridAddon = class extends BaseAddon {
2967
3031
  resolvePlan: () => {
2968
3032
  const instance = this.instanceById(instanceId);
2969
3033
  const plan = this.planFor(instanceId, profile);
2970
- if (!instance || plan === null || !plan.offered) return null;
3034
+ if (!instance || plan === null || !plan.offered || !plan.published) return null;
2971
3035
  return gridPlanFor(instance, {
2972
3036
  encoder: "libx264",
2973
3037
  decodeHwAccel: null,
@@ -3058,7 +3122,7 @@ var CameraGridAddon = class extends BaseAddon {
3058
3122
  const relay = this.relay;
3059
3123
  const plans = this.plansFor(instanceId);
3060
3124
  if (!instance || !relay || plans === null) return null;
3061
- return plans.filter((row) => row.offered).map((row) => gridStreamDescriptor({
3125
+ return plans.filter((row) => row.offered && row.published).map((row) => gridStreamDescriptor({
3062
3126
  profile: row.profile,
3063
3127
  width: row.offered ? row.canvas.width : 0,
3064
3128
  height: row.offered ? row.canvas.height : 0,
@@ -3093,6 +3157,7 @@ var CameraGridAddon = class extends BaseAddon {
3093
3157
  profiles: (this.plansByInstanceId.get(instance.id) ?? []).map((row) => ({
3094
3158
  profile: row.profile,
3095
3159
  offered: row.offered,
3160
+ published: row.offered && row.published,
3096
3161
  missingSources: row.offered ? [] : [...row.missingSources],
3097
3162
  canvas: row.offered ? `${String(row.canvas.width)}x${String(row.canvas.height)}` : ""
3098
3163
  }))
@@ -3105,6 +3170,7 @@ var CameraGridAddon = class extends BaseAddon {
3105
3170
  ...instance,
3106
3171
  ...input.name !== void 0 ? { name: input.name.trim() } : {},
3107
3172
  ...input.fps !== void 0 ? { fps: input.fps } : {},
3173
+ ...input.publishedProfiles !== void 0 ? { publishedProfiles: [...input.publishedProfiles] } : {},
3108
3174
  cells: input.cells.map((cell) => ({ ...cell }))
3109
3175
  };
3110
3176
  this.ctx.logger.info("camera grid: layout saved", {
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/console-logging/console-destination.ts
9
9
  var LEVEL_RANK = {
@@ -1,4 +1,4 @@
1
- import { Ct as logDestinationCapability, sn as BaseAddon } from "../../dist-BYnnRAYv.mjs";
1
+ import { Ct as logDestinationCapability, sn as BaseAddon } from "../../dist-CqmImT7i.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/console-logging/console-destination.ts
4
4
  var LEVEL_RANK = {
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-DWNlc-PG.js");
3
+ const require_dist = require("../../dist-BTpfLiUX.js");
4
4
  let node_crypto = require("node:crypto");
5
5
  let node_fs_promises = require("node:fs/promises");
6
6
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -1,4 +1,4 @@
1
- import { G as coreBlockAddonId, K as coreBlockIdFromAddonId, an as errMsg, c as CORE_BLOCKS_ADDON_ID, l as CORE_BLOCK_ADDON_PREFIX, p as DeclaredDevices, q as coreBlocksCapability, sn as BaseAddon, u as CoreBlockSchema } from "../../dist-BYnnRAYv.mjs";
1
+ import { G as coreBlockAddonId, K as coreBlockIdFromAddonId, an as errMsg, c as CORE_BLOCKS_ADDON_ID, l as CORE_BLOCK_ADDON_PREFIX, p as DeclaredDevices, q as coreBlocksCapability, sn as BaseAddon, u as CoreBlockSchema } from "../../dist-CqmImT7i.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import * as fs from "node:fs/promises";
4
4
  import * as path$1 from "node:path";
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
7
- const require_retired_settings_keys = require("../../retired-settings-keys-BjSmLqyQ.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
+ const require_retired_settings_keys = require("../../retired-settings-keys-D_DIuT_N.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let _camstack_types_node = require("@camstack/types/node");
10
10
  //#region src/builtins/device-manager/adoption-job-engine.ts
@@ -1,5 +1,5 @@
1
- import { $ as deviceStateCapability, D as STREAM_PROFILE_META, H as buildStreamParamsConfigSchema, Ht as runtimeStatePolicyFor, Nn as sleep$1, Nt as parseStreamParamsFormPatch, Pn as EventCategory, Q as deviceManagerCapability, St as locationIconKey, Tn as isDeviceConfigCap, an as errMsg, dn as DeviceFeature, et as deviceStatusCapability, fn as DeviceRole, gn as WELL_KNOWN_TAB_MAP, it as enumerateSchemaFields, jt as normalizeUnit, m as DeviceStatusSchema, o as CAMERA_SWITCH_ORDER, pn as DeviceType, qt as siteZoneKey, rt as enumerateItemArrayFields, s as CAP_NAMES_WITH_STATUS, sn as BaseAddon, t as ALL_CAPABILITY_DEFINITIONS, v as LocationIconIdSchema, zt as resolveContainerPrimaryChild } from "../../dist-BYnnRAYv.mjs";
2
- import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-C_MtEB25.mjs";
1
+ import { $ as deviceStateCapability, D as STREAM_PROFILE_META, H as buildStreamParamsConfigSchema, Ht as runtimeStatePolicyFor, Nn as sleep$1, Nt as parseStreamParamsFormPatch, Pn as EventCategory, Q as deviceManagerCapability, St as locationIconKey, Tn as isDeviceConfigCap, an as errMsg, dn as DeviceFeature, et as deviceStatusCapability, fn as DeviceRole, gn as WELL_KNOWN_TAB_MAP, it as enumerateSchemaFields, jt as normalizeUnit, m as DeviceStatusSchema, o as CAMERA_SWITCH_ORDER, pn as DeviceType, qt as siteZoneKey, rt as enumerateItemArrayFields, s as CAP_NAMES_WITH_STATUS, sn as BaseAddon, t as ALL_CAPABILITY_DEFINITIONS, v as LocationIconIdSchema, zt as resolveContainerPrimaryChild } from "../../dist-CqmImT7i.mjs";
2
+ import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-C8nV-AUi.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { canonicalDeviceFingerprint } from "@camstack/types/node";
5
5
  //#region src/builtins/device-manager/adoption-job-engine.ts
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  //#region src/builtins/doorbell/trigger-engine.ts
8
8
  /**
9
9
  * Pure trigger logic for the virtual-doorbell builtin — edge detection over
@@ -1,4 +1,4 @@
1
- import { B as bareAddonId, Pn as EventCategory, T as SOURCE_DEVICE_TYPES, an as errMsg, at as evaluateSensorEdge, bn as createEvent, gt as isSourceCap, ht as isSameAddonId, pn as DeviceType, sn as BaseAddon, tt as doorbellCapability, w as SOURCE_CAPS } from "../../dist-BYnnRAYv.mjs";
1
+ import { B as bareAddonId, Pn as EventCategory, T as SOURCE_DEVICE_TYPES, an as errMsg, at as evaluateSensorEdge, bn as createEvent, gt as isSourceCap, ht as isSameAddonId, pn as DeviceType, sn as BaseAddon, tt as doorbellCapability, w as SOURCE_CAPS } from "../../dist-CqmImT7i.mjs";
2
2
  //#region src/builtins/doorbell/trigger-engine.ts
3
3
  /**
4
4
  * Pure trigger logic for the virtual-doorbell builtin — edge detection over
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
9
9
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -1,4 +1,4 @@
1
- import { Ct as logDestinationCapability, sn as BaseAddon } from "../../dist-BYnnRAYv.mjs";
1
+ import { Ct as logDestinationCapability, sn as BaseAddon } from "../../dist-CqmImT7i.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
4
4
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-DWNlc-PG.js");
3
+ const require_dist = require("../../dist-BTpfLiUX.js");
4
4
  //#region src/builtins/liveness-monitor/liveness-checks.ts
5
5
  var NO_DEVICES = "liveness:no-devices";
6
6
  var ALL_OFFLINE = "liveness:all-devices-offline";
@@ -1,4 +1,4 @@
1
- import { C as RecordingStatusSchema, Pn as EventCategory, an as errMsg, bn as createEvent, sn as BaseAddon } from "../../dist-BYnnRAYv.mjs";
1
+ import { C as RecordingStatusSchema, Pn as EventCategory, an as errMsg, bn as createEvent, sn as BaseAddon } from "../../dist-CqmImT7i.mjs";
2
2
  //#region src/builtins/liveness-monitor/liveness-checks.ts
3
3
  var NO_DEVICES = "liveness:no-devices";
4
4
  var ALL_OFFLINE = "liveness:all-devices-offline";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  node_crypto = require_chunk.__toESM(node_crypto);
9
9
  let crypto$1 = require("crypto");
@@ -1,5 +1,5 @@
1
1
  import { i as __require, o as __toESM, t as __commonJSMin } from "../../chunk-CNf5ZN-e.mjs";
2
- import { N as UserRecordSchema, O as ScopedTokenSchema, R as authProviderCapability, r as ApiKeyRecordSchema, sn as BaseAddon, tn as userManagementCapability } from "../../dist-BYnnRAYv.mjs";
2
+ import { N as UserRecordSchema, O as ScopedTokenSchema, R as authProviderCapability, r as ApiKeyRecordSchema, sn as BaseAddon, tn as userManagementCapability } from "../../dist-CqmImT7i.mjs";
3
3
  import * as crypto$2 from "node:crypto";
4
4
  import { createHash, randomUUID, timingSafeEqual } from "node:crypto";
5
5
  import nodeCrypto from "crypto";
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-DWNlc-PG.js");
3
+ const require_dist = require("../../dist-BTpfLiUX.js");
4
4
  const require_tls = require("../../tls-BFs4PzoW.js");
5
5
  let node_fs_promises = require("node:fs/promises");
6
6
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Pn as EventCategory, sn as BaseAddon, xt as localNetworkCapability } from "../../dist-BYnnRAYv.mjs";
1
+ import { Pn as EventCategory, sn as BaseAddon, xt as localNetworkCapability } from "../../dist-CqmImT7i.mjs";
2
2
  import { S as readLanHttpState, _ as DEFAULT_LAN_HTTP_PORT, a as writeTlsMode, i as writeExtraSans, l as reissueTlsLeaf, n as readTlsAccessStatus, o as validateUploadedTls, r as readTlsMode, s as ensureTlsCert, t as readExtraSans, y as applyLanHttp } from "../../tls-2xbgg78V.mjs";
3
3
  import { mkdir, writeFile } from "node:fs/promises";
4
4
  import { join } from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  //#region src/builtins/loki-logging/loki-payload.ts
8
8
  /**
9
9
  * Loki rejects a label name that is not a valid Prometheus label
@@ -1,4 +1,4 @@
1
- import { Ct as logDestinationCapability, sn as BaseAddon } from "../../dist-BYnnRAYv.mjs";
1
+ import { Ct as logDestinationCapability, sn as BaseAddon } from "../../dist-CqmImT7i.mjs";
2
2
  //#region src/builtins/loki-logging/loki-payload.ts
3
3
  /**
4
4
  * Loki rejects a label name that is not a valid Prometheus label
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  let node_fs_promises = require("node:fs/promises");
8
8
  let node_fs = require("node:fs");
9
9
  node_fs = require_chunk.__toESM(node_fs);
@@ -1,4 +1,4 @@
1
- import { Lt as reducePoints, Ot as metricsProviderCapability, Pn as EventCategory, Rt as resolveBucketMs, an as errMsg, bn as createEvent, lt as foldSnapshotByFunction, sn as BaseAddon } from "../../dist-BYnnRAYv.mjs";
1
+ import { Lt as reducePoints, Ot as metricsProviderCapability, Pn as EventCategory, Rt as resolveBucketMs, an as errMsg, bn as createEvent, lt as foldSnapshotByFunction, sn as BaseAddon } from "../../dist-CqmImT7i.mjs";
2
2
  import { access, readFile, readdir } from "node:fs/promises";
3
3
  import * as fs from "node:fs";
4
4
  import { constants, readFileSync } from "node:fs";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_chunk.__toESM(node_fs);
9
9
  let node_child_process = require("node:child_process");
@@ -1,5 +1,5 @@
1
1
  import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
2
- import { Gt as scoreRuntimes, Pn as EventCategory, Pt as platformProbeCapability, Sn as emitReadiness, an as errMsg, nt as enumerateInferenceDevices, sn as BaseAddon } from "../../dist-BYnnRAYv.mjs";
2
+ import { Gt as scoreRuntimes, Pn as EventCategory, Pt as platformProbeCapability, Sn as emitReadiness, an as errMsg, nt as enumerateInferenceDevices, sn as BaseAddon } from "../../dist-CqmImT7i.mjs";
3
3
  import * as fs from "node:fs";
4
4
  import { execFile } from "node:child_process";
5
5
  import * as os from "node:os";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
8
8
  /**
9
9
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -1,4 +1,4 @@
1
- import { Pn as EventCategory, sn as BaseAddon } from "../../dist-BYnnRAYv.mjs";
1
+ import { Pn as EventCategory, sn as BaseAddon } from "../../dist-CqmImT7i.mjs";
2
2
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
3
3
  /**
4
4
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  const require_snapshot_courtesy = require("../../snapshot-courtesy-Cqs7GkOV.js");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
@@ -1,4 +1,4 @@
1
- import { B as bareAddonId, Dn as nodePin, Jt as snapshotCapability, Pn as EventCategory, V as batteryCapability, Z as deriveBatteryPresence, a as BatteryStatusSchema, an as errMsg, dn as DeviceFeature, en as streamQualityLabel, pn as DeviceType, sn as BaseAddon } from "../../dist-BYnnRAYv.mjs";
1
+ import { B as bareAddonId, Dn as nodePin, Jt as snapshotCapability, Pn as EventCategory, V as batteryCapability, Z as deriveBatteryPresence, a as BatteryStatusSchema, an as errMsg, dn as DeviceFeature, en as streamQualityLabel, pn as DeviceType, sn as BaseAddon } from "../../dist-CqmImT7i.mjs";
2
2
  import { i as renderCourtesyOverlayJpeg, n as courtesyCacheKey, r as renderCourtesyJpeg } from "../../snapshot-courtesy-CZPvteq2.mjs";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let node_fs_promises = require("node:fs/promises");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Qt as storageProviderCapability, ct as filesystemBrowseCapability, sn as BaseAddon } from "../../dist-BYnnRAYv.mjs";
1
+ import { Qt as storageProviderCapability, ct as filesystemBrowseCapability, sn as BaseAddon } from "../../dist-CqmImT7i.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { mkdir, readdir, realpath, statfs } from "node:fs/promises";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  const require_hub_cap_forward = require("../../hub-cap-forward-DmHNjbB0.js");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
@@ -1,4 +1,4 @@
1
- import { A as StorageLocationModeSchema, Bt as resolveLocationMode, Dt as mayWriteToLocation, Et as mayReadLocation, Kt as settingsStoreCapability, M as StorageMigrationJobSchema, On as parseJsonObject, Pn as EventCategory, W as collectSecretConfigKeys, Xt as storageMigrationCapability, Yt as storageCapability, _ as LegacyStorageLocationDefaultSchema, in as withLocationMode, j as StorageLocationTypeSchema, k as StorageCleanupJobSchema, kt as modeMayWrite, ot as evictionPolicyOfLocation, pt as isLocationEnabled, sn as BaseAddon, ut as gbToBytes, x as REDACTED_SECRET, yt as legacyModeOf } from "../../dist-BYnnRAYv.mjs";
1
+ import { A as StorageLocationModeSchema, Bt as resolveLocationMode, Dt as mayWriteToLocation, Et as mayReadLocation, Kt as settingsStoreCapability, M as StorageMigrationJobSchema, On as parseJsonObject, Pn as EventCategory, W as collectSecretConfigKeys, Xt as storageMigrationCapability, Yt as storageCapability, _ as LegacyStorageLocationDefaultSchema, in as withLocationMode, j as StorageLocationTypeSchema, k as StorageCleanupJobSchema, kt as modeMayWrite, ot as evictionPolicyOfLocation, pt as isLocationEnabled, sn as BaseAddon, ut as gbToBytes, x as REDACTED_SECRET, yt as legacyModeOf } from "../../dist-CqmImT7i.mjs";
2
2
  import { t as HUB_CAP_FWD_ACTION } from "../../hub-cap-forward-BdT320dq.mjs";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  //#region src/builtins/system-config/system-config.addon.ts
8
8
  /**
9
9
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -1,4 +1,4 @@
1
- import { an as errMsg, sn as BaseAddon, wn as hydrateSchema } from "../../dist-BYnnRAYv.mjs";
1
+ import { an as errMsg, sn as BaseAddon, wn as hydrateSchema } from "../../dist-CqmImT7i.mjs";
2
2
  //#region src/builtins/system-config/system-config.addon.ts
3
3
  /**
4
4
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DWNlc-PG.js");
6
+ const require_dist = require("../../dist-BTpfLiUX.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  let node_path = require("node:path");
9
9
  node_path = require_chunk.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { Ct as logDestinationCapability, sn as BaseAddon } from "../../dist-BYnnRAYv.mjs";
1
+ import { Ct as logDestinationCapability, sn as BaseAddon } from "../../dist-CqmImT7i.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  import * as path$1 from "node:path";
4
4
  import path from "node:path";
@@ -31221,7 +31221,16 @@ var GridProfileOfferSchema = zod.z.object({
31221
31221
  * grid came out as, and because it is the number an operator would otherwise
31222
31222
  * expect to type.
31223
31223
  */
31224
- canvas: zod.z.string()
31224
+ canvas: zod.z.string(),
31225
+ /**
31226
+ * Whether this profile is PUBLISHED, of the ones the grid could serve.
31227
+ *
31228
+ * A grid's `high` is composed of its sources' `high`, so on a 4K fleet it is
31229
+ * a 4K canvas built from 4K decodes — something to opt into, not something a
31230
+ * viewer's adaptive should be handed by climbing to the top rung it can see.
31231
+ * Default is `mid` + `low`.
31232
+ */
31233
+ published: zod.z.boolean()
31225
31234
  });
31226
31235
  var GridLayoutViewSchema = zod.z.object({
31227
31236
  /** The persisted grid row this camera was declared from. */
@@ -31244,6 +31253,12 @@ var GridLayoutPatchSchema = zod.z.object({
31244
31253
  deviceId: zod.z.number().int().nonnegative(),
31245
31254
  name: zod.z.string().min(1).max(160).optional(),
31246
31255
  fps: zod.z.number().int().min(1).max(60).optional(),
31256
+ /** Which profiles to publish. See `GridProfileOffer.published`. */
31257
+ publishedProfiles: zod.z.array(zod.z.enum([
31258
+ "high",
31259
+ "mid",
31260
+ "low"
31261
+ ])).max(3).optional(),
31247
31262
  /**
31248
31263
  * The whole cell list at once. A per-cell patch would need an ordering the
31249
31264
  * editor does not have, and a half-applied layout is a picture nobody asked
@@ -31221,7 +31221,16 @@ var GridProfileOfferSchema = z.object({
31221
31221
  * grid came out as, and because it is the number an operator would otherwise
31222
31222
  * expect to type.
31223
31223
  */
31224
- canvas: z.string()
31224
+ canvas: z.string(),
31225
+ /**
31226
+ * Whether this profile is PUBLISHED, of the ones the grid could serve.
31227
+ *
31228
+ * A grid's `high` is composed of its sources' `high`, so on a 4K fleet it is
31229
+ * a 4K canvas built from 4K decodes — something to opt into, not something a
31230
+ * viewer's adaptive should be handed by climbing to the top rung it can see.
31231
+ * Default is `mid` + `low`.
31232
+ */
31233
+ published: z.boolean()
31225
31234
  });
31226
31235
  var GridLayoutViewSchema = z.object({
31227
31236
  /** The persisted grid row this camera was declared from. */
@@ -31244,6 +31253,12 @@ var GridLayoutPatchSchema = z.object({
31244
31253
  deviceId: z.number().int().nonnegative(),
31245
31254
  name: z.string().min(1).max(160).optional(),
31246
31255
  fps: z.number().int().min(1).max(60).optional(),
31256
+ /** Which profiles to publish. See `GridProfileOffer.published`. */
31257
+ publishedProfiles: z.array(z.enum([
31258
+ "high",
31259
+ "mid",
31260
+ "low"
31261
+ ])).max(3).optional(),
31247
31262
  /**
31248
31263
  * The whole cell list at once. A per-cell patch would need an ordering the
31249
31264
  * editor does not have, and a half-applied layout is a picture nobody asked
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("./chunk-Cek0wNdY.js");
3
- const require_dist = require("./dist-DWNlc-PG.js");
3
+ const require_dist = require("./dist-BTpfLiUX.js");
4
4
  const require_builtins_alerts_alerts_addon = require("./builtins/alerts/alerts.addon.js");
5
5
  require("./builtins/alerts/index.js");
6
6
  const require_formatter = require("./formatter-DqAKDlvN.js");
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as __toCommonJS, i as __require, n as __esmMin, o as __toESM$1, r as __exportAll, t as __commonJSMin$1 } from "./chunk-CNf5ZN-e.mjs";
2
- import { An as readinessKey, Cn as expandCapMethods, F as addonSettingsCapability, Ft as procedureAuthKey, Mn as scopeKey, Mt as objectInputDeclaresAddonId, On as parseJsonObject, Pn as EventCategory$1, S as RUNTIME_DEFAULTS, Tt as looseSchema, Ut as scopesAllowAddon, Vt as resolveMethodAuth, Wt as scopesAllowDeviceCap, _n as asJsonObject$1, _t as isVoidInput, an as errMsg$1, bn as createEvent, bt as lifecycleJobSchema, cn as DATAPLANE_SECRET_HEADER$1, dt as isArrayOutputSchema, ft as isCollectionArrayMethod, g as LOG_LEVEL_RANK, hn as ReadinessTimeoutError, jn as resolveCapMount, kn as parseJsonUnknown, ln as DEVICE_SETTINGS_CONTRIBUTION_METHODS, mn as ReadinessRegistry, mt as isObjectInput, st as extractNestedAddonId, t as ALL_CAPABILITY_DEFINITIONS, un as DEVICE_STATUS_METHOD, vn as asNumber, vt as kebabToCamel, wt as logLevelAtMost, xn as emitDownForOwnedCaps, y as METHOD_ACCESS_MAP, yn as asString$1 } from "./dist-BYnnRAYv.mjs";
2
+ import { An as readinessKey, Cn as expandCapMethods, F as addonSettingsCapability, Ft as procedureAuthKey, Mn as scopeKey, Mt as objectInputDeclaresAddonId, On as parseJsonObject, Pn as EventCategory$1, S as RUNTIME_DEFAULTS, Tt as looseSchema, Ut as scopesAllowAddon, Vt as resolveMethodAuth, Wt as scopesAllowDeviceCap, _n as asJsonObject$1, _t as isVoidInput, an as errMsg$1, bn as createEvent, bt as lifecycleJobSchema, cn as DATAPLANE_SECRET_HEADER$1, dt as isArrayOutputSchema, ft as isCollectionArrayMethod, g as LOG_LEVEL_RANK, hn as ReadinessTimeoutError, jn as resolveCapMount, kn as parseJsonUnknown, ln as DEVICE_SETTINGS_CONTRIBUTION_METHODS, mn as ReadinessRegistry, mt as isObjectInput, st as extractNestedAddonId, t as ALL_CAPABILITY_DEFINITIONS, un as DEVICE_STATUS_METHOD, vn as asNumber, vt as kebabToCamel, wt as logLevelAtMost, xn as emitDownForOwnedCaps, y as METHOD_ACCESS_MAP, yn as asString$1 } from "./dist-CqmImT7i.mjs";
3
3
  import { AlertCenterAddon } from "./builtins/alerts/alerts.addon.mjs";
4
4
  import "./builtins/alerts/index.mjs";
5
5
  import { t as formatLogLine } from "./formatter-B7qW8bPJ.mjs";
@@ -1,4 +1,4 @@
1
- import { _n as asJsonObject } from "./dist-BYnnRAYv.mjs";
1
+ import { _n as asJsonObject } from "./dist-CqmImT7i.mjs";
2
2
  //#region src/builtins/sqlite-storage/retired-settings-keys.ts
3
3
  /**
4
4
  * Is THIS node the one whose settings store is the cluster's authority?
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-DWNlc-PG.js");
1
+ const require_dist = require("./dist-BTpfLiUX.js");
2
2
  //#region src/builtins/sqlite-storage/retired-settings-keys.ts
3
3
  /**
4
4
  * Is THIS node the one whose settings store is the cluster's authority?
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/system",
3
- "version": "1.2.268",
3
+ "version": "1.2.269",
4
4
  "description": "Core addon for CamStack — builtins, pipeline, process management, auth, logging, events",
5
5
  "keywords": [
6
6
  "camstack",