@camstack/system 1.2.83 → 1.2.85

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 (66) hide show
  1. package/dist/addon-runner.js +1 -1
  2. package/dist/addon-runner.mjs +1 -1
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.js +1 -1
  8. package/dist/builtins/alerts/alerts.addon.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/console-logging/index.js +1 -1
  12. package/dist/builtins/console-logging/index.mjs +1 -1
  13. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  14. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  15. package/dist/builtins/device-manager/device-manager.addon.js +32 -2
  16. package/dist/builtins/device-manager/device-manager.addon.mjs +32 -2
  17. package/dist/builtins/device-manager/device-meta-actions.d.ts +12 -0
  18. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  19. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  20. package/dist/builtins/hub-forwarder/index.js +1 -1
  21. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  22. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  23. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  24. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  25. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  26. package/dist/builtins/local-network/local-network.addon.js +1 -1
  27. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  28. package/dist/builtins/loki-logging/index.js +1 -1
  29. package/dist/builtins/loki-logging/index.mjs +1 -1
  30. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  31. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  32. package/dist/builtins/platform-probe/index.js +1 -1
  33. package/dist/builtins/platform-probe/index.mjs +1 -1
  34. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  35. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  36. package/dist/builtins/snapshot/index.js +66 -11
  37. package/dist/builtins/snapshot/index.mjs +66 -11
  38. package/dist/builtins/snapshot/snapshot.addon.d.ts +39 -3
  39. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +30 -10
  40. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +31 -11
  41. package/dist/builtins/sqlite-storage/path-guard.d.ts +8 -1
  42. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +1 -1
  43. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
  44. package/dist/builtins/storage-orchestrator/index.d.ts +5 -6
  45. package/dist/builtins/storage-orchestrator/index.mjs +1 -1
  46. package/dist/builtins/storage-orchestrator/provider-discovery.d.ts +1 -1
  47. package/dist/builtins/storage-orchestrator/storage-migration-coordinator.d.ts +1 -1
  48. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +1 -1
  49. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1252 -1191
  50. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1253 -1192
  51. package/dist/builtins/system-config/system-config.addon.js +1 -1
  52. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  53. package/dist/builtins/winston-logging/index.js +1 -1
  54. package/dist/builtins/winston-logging/index.mjs +1 -1
  55. package/dist/{dist-EAvNJD24.mjs → dist-DWuNENzg.mjs} +527 -11
  56. package/dist/{dist-CqiMPPs0.js → dist-DiScsb8j.js} +527 -11
  57. package/dist/index.js +83 -18
  58. package/dist/index.mjs +84 -19
  59. package/dist/kernel/addon-installer.d.ts +58 -6
  60. package/dist/kernel/moleculer/event-bus-core.d.ts +47 -1
  61. package/dist/kernel/transport/child-cap-protocol.d.ts +32 -0
  62. package/dist/kernel/transport/hub-cap-forward.d.ts +8 -0
  63. package/dist/kernel/transport/parent-unowned-call.d.ts +19 -0
  64. package/dist/{manifest-python-deps-DG_LLcZF.js → manifest-python-deps-C0zkl1Ae.js} +133 -53
  65. package/dist/{manifest-python-deps-C1DMs1J9.mjs → manifest-python-deps-fWzdEasI.mjs} +133 -53
  66. package/package.json +6 -1
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- const require_manifest_python_deps = require("./manifest-python-deps-DG_LLcZF.js");
2
+ const require_manifest_python_deps = require("./manifest-python-deps-C0zkl1Ae.js");
3
3
  const require_custom_action_registry = require("./custom-action-registry-jY0NOZK8.js");
4
4
  let node_path = require("node:path");
5
5
  node_path = require_chunk.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { I as createUdsLoggerWithControl, L as LocalChildClient, at as setWorkerNativeCapsChangeListener, bt as installManifestNativeDeps, i as createUdsAddonContext, nt as getWorkerNativeCapProvider, rt as getWorkerNativeCapSnapshot, st as validateProviderRegistrations, t as installManifestPythonDeps, xt as resolveAddonClass } from "./manifest-python-deps-C1DMs1J9.mjs";
1
+ import { I as createUdsLoggerWithControl, L as LocalChildClient, at as setWorkerNativeCapsChangeListener, bt as installManifestNativeDeps, i as createUdsAddonContext, nt as getWorkerNativeCapProvider, rt as getWorkerNativeCapSnapshot, st as validateProviderRegistrations, t as installManifestPythonDeps, xt as resolveAddonClass } from "./manifest-python-deps-fWzdEasI.mjs";
2
2
  import { t as CustomActionRegistry } from "./custom-action-registry-F__gp_VX.mjs";
3
3
  import { register } from "node:module";
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-CqiMPPs0.js");
6
+ const require_dist = require("../../dist-DiScsb8j.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 { Ht as EventCategory, gt as BaseAddon, ht as errMsg, v as addonPagesCapability } from "../../dist-EAvNJD24.mjs";
1
+ import { Ht as EventCategory, gt as BaseAddon, ht as errMsg, v as addonPagesCapability } from "../../dist-DWuNENzg.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-CqiMPPs0.js");
6
+ const require_dist = require("../../dist-DiScsb8j.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 { Ht as EventCategory, b as addonWidgetsCapability, gt as BaseAddon, ht as errMsg } from "../../dist-EAvNJD24.mjs";
1
+ import { Ht as EventCategory, b as addonWidgetsCapability, gt as BaseAddon, ht as errMsg } from "../../dist-DWuNENzg.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-CqiMPPs0.js");
6
+ const require_dist = require("../../dist-DiScsb8j.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 { Ht as EventCategory, gt as BaseAddon, ht as errMsg, kt as createEvent, n as AlertSchema, x as alertsCapability } from "../../dist-EAvNJD24.mjs";
1
+ import { Ht as EventCategory, gt as BaseAddon, ht as errMsg, kt as createEvent, n as AlertSchema, x as alertsCapability } from "../../dist-DWuNENzg.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
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CqiMPPs0.js");
6
+ const require_dist = require("../../dist-DiScsb8j.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 { C as backupCapability, Ht as EventCategory, gt as BaseAddon } from "../../dist-EAvNJD24.mjs";
1
+ import { C as backupCapability, Ht as EventCategory, gt as BaseAddon } from "../../dist-DWuNENzg.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";
@@ -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-CqiMPPs0.js");
6
+ const require_dist = require("../../dist-DiScsb8j.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 { J as logDestinationCapability, gt as BaseAddon } from "../../dist-EAvNJD24.mjs";
1
+ import { J as logDestinationCapability, gt as BaseAddon } from "../../dist-DWuNENzg.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-CqiMPPs0.js");
3
+ const require_dist = require("../../dist-DiScsb8j.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 { D as coreBlockIdFromAddonId, E as coreBlockAddonId, O as coreBlocksCapability, c as CoreBlockSchema, gt as BaseAddon, l as DeclaredDevices, o as CORE_BLOCKS_ADDON_ID, s as CORE_BLOCK_ADDON_PREFIX } from "../../dist-EAvNJD24.mjs";
1
+ import { D as coreBlockIdFromAddonId, E as coreBlockAddonId, O as coreBlocksCapability, c as CoreBlockSchema, gt as BaseAddon, l as DeclaredDevices, o as CORE_BLOCKS_ADDON_ID, s as CORE_BLOCK_ADDON_PREFIX } from "../../dist-DWuNENzg.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,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-CqiMPPs0.js");
6
+ const require_dist = require("../../dist-DiScsb8j.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let _camstack_types_node = require("@camstack/types/node");
9
9
  //#region src/builtins/device-manager/adoption-job-engine.ts
@@ -3705,10 +3705,22 @@ async function setMetadata(pctx, input) {
3705
3705
  * lifecycle gating is the driver's responsibility (BaseDevice
3706
3706
  * exposes `this.disabled` for the driver to consult at the top
3707
3707
  * of its lifecycle methods).
3708
+ *
3709
+ * TWO events leave here, and they are not the same message:
3710
+ *
3711
+ * - `device.meta-changed` fires on EVERY call. It is the meta-surface echo
3712
+ * every UI redraws on, and idempotence is not interesting to it.
3713
+ * - `device.disabled` / `device.enabled` fire ONLY when the flag actually
3714
+ * MOVES. They are the lifecycle announcement — the operator asked to be
3715
+ * told when a camera is switched off, and a write that re-states a flag the
3716
+ * device already had is not that. Telemetry semantics (D8) are fine here: a
3717
+ * dropped announcement costs a notification, never durable state, and the
3718
+ * meta row is the authority either way (D62 — this switch stores nothing of
3719
+ * its own).
3708
3720
  */
3709
3721
  async function setDisabled(pctx, input) {
3710
3722
  const { deviceId, disabled } = input;
3711
- await pctx.metaStore.withMetaWriteLock(async () => {
3723
+ const moved = await pctx.metaStore.withMetaWriteLock(async () => {
3712
3724
  const persisted = await pctx.metaStore.resolvePersistedById(deviceId);
3713
3725
  if (!persisted) throw new Error(`[device-manager] setDisabled: unknown device id=${deviceId}`);
3714
3726
  const { meta: m } = persisted;
@@ -3721,6 +3733,10 @@ async function setDisabled(pctx, input) {
3721
3733
  disabled
3722
3734
  }
3723
3735
  } });
3736
+ return {
3737
+ changed: (m.disabled ?? false) !== disabled,
3738
+ integrationId: m.integrationId ?? ""
3739
+ };
3724
3740
  });
3725
3741
  pctx.host.ctx.eventBus.emit({
3726
3742
  id: (0, node_crypto.randomUUID)(),
@@ -3736,6 +3752,20 @@ async function setDisabled(pctx, input) {
3736
3752
  value: disabled
3737
3753
  }
3738
3754
  });
3755
+ if (!moved.changed) return;
3756
+ pctx.host.ctx.eventBus.emit({
3757
+ id: (0, node_crypto.randomUUID)(),
3758
+ timestamp: /* @__PURE__ */ new Date(),
3759
+ source: {
3760
+ type: "device",
3761
+ id: deviceId
3762
+ },
3763
+ category: disabled ? require_dist.EventCategory.DeviceDisabled : require_dist.EventCategory.DeviceEnabled,
3764
+ data: {
3765
+ deviceId,
3766
+ integrationId: moved.integrationId
3767
+ }
3768
+ });
3739
3769
  }
3740
3770
  async function loadRuntimeState(pctx, input) {
3741
3771
  const { deviceId } = input;
@@ -1,4 +1,4 @@
1
- import { Ht as EventCategory, I as enumerateItemArrayFields, L as enumerateSchemaFields, M as deviceStateCapability, N as deviceStatusCapability, Pt as isDeviceConfigCap, Q as normalizeUnit, St as DeviceType, T as buildStreamParamsConfigSchema, Tt as WELL_KNOWN_TAB_MAP, Vt as sleep, a as CAP_NAMES_WITH_STATUS, bt as DeviceFeature, et as parseStreamParamsFormPatch, gt as BaseAddon, ht as errMsg, j as deviceManagerCapability, p as STREAM_PROFILE_META, t as ALL_CAPABILITY_DEFINITIONS, u as DeviceStatusSchema, xt as DeviceRole } from "../../dist-EAvNJD24.mjs";
1
+ import { Ht as EventCategory, I as enumerateItemArrayFields, L as enumerateSchemaFields, M as deviceStateCapability, N as deviceStatusCapability, Pt as isDeviceConfigCap, Q as normalizeUnit, St as DeviceType, T as buildStreamParamsConfigSchema, Tt as WELL_KNOWN_TAB_MAP, Vt as sleep, a as CAP_NAMES_WITH_STATUS, bt as DeviceFeature, et as parseStreamParamsFormPatch, gt as BaseAddon, ht as errMsg, j as deviceManagerCapability, p as STREAM_PROFILE_META, t as ALL_CAPABILITY_DEFINITIONS, u as DeviceStatusSchema, xt as DeviceRole } from "../../dist-DWuNENzg.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { canonicalDeviceFingerprint } from "@camstack/types/node";
4
4
  //#region src/builtins/device-manager/adoption-job-engine.ts
@@ -3700,10 +3700,22 @@ async function setMetadata(pctx, input) {
3700
3700
  * lifecycle gating is the driver's responsibility (BaseDevice
3701
3701
  * exposes `this.disabled` for the driver to consult at the top
3702
3702
  * of its lifecycle methods).
3703
+ *
3704
+ * TWO events leave here, and they are not the same message:
3705
+ *
3706
+ * - `device.meta-changed` fires on EVERY call. It is the meta-surface echo
3707
+ * every UI redraws on, and idempotence is not interesting to it.
3708
+ * - `device.disabled` / `device.enabled` fire ONLY when the flag actually
3709
+ * MOVES. They are the lifecycle announcement — the operator asked to be
3710
+ * told when a camera is switched off, and a write that re-states a flag the
3711
+ * device already had is not that. Telemetry semantics (D8) are fine here: a
3712
+ * dropped announcement costs a notification, never durable state, and the
3713
+ * meta row is the authority either way (D62 — this switch stores nothing of
3714
+ * its own).
3703
3715
  */
3704
3716
  async function setDisabled(pctx, input) {
3705
3717
  const { deviceId, disabled } = input;
3706
- await pctx.metaStore.withMetaWriteLock(async () => {
3718
+ const moved = await pctx.metaStore.withMetaWriteLock(async () => {
3707
3719
  const persisted = await pctx.metaStore.resolvePersistedById(deviceId);
3708
3720
  if (!persisted) throw new Error(`[device-manager] setDisabled: unknown device id=${deviceId}`);
3709
3721
  const { meta: m } = persisted;
@@ -3716,6 +3728,10 @@ async function setDisabled(pctx, input) {
3716
3728
  disabled
3717
3729
  }
3718
3730
  } });
3731
+ return {
3732
+ changed: (m.disabled ?? false) !== disabled,
3733
+ integrationId: m.integrationId ?? ""
3734
+ };
3719
3735
  });
3720
3736
  pctx.host.ctx.eventBus.emit({
3721
3737
  id: randomUUID(),
@@ -3731,6 +3747,20 @@ async function setDisabled(pctx, input) {
3731
3747
  value: disabled
3732
3748
  }
3733
3749
  });
3750
+ if (!moved.changed) return;
3751
+ pctx.host.ctx.eventBus.emit({
3752
+ id: randomUUID(),
3753
+ timestamp: /* @__PURE__ */ new Date(),
3754
+ source: {
3755
+ type: "device",
3756
+ id: deviceId
3757
+ },
3758
+ category: disabled ? EventCategory.DeviceDisabled : EventCategory.DeviceEnabled,
3759
+ data: {
3760
+ deviceId,
3761
+ integrationId: moved.integrationId
3762
+ }
3763
+ });
3734
3764
  }
3735
3765
  async function loadRuntimeState(pctx, input) {
3736
3766
  const { deviceId } = input;
@@ -141,6 +141,18 @@ export declare function setMetadata(pctx: ProviderContext, input: Parameters<IDe
141
141
  * lifecycle gating is the driver's responsibility (BaseDevice
142
142
  * exposes `this.disabled` for the driver to consult at the top
143
143
  * of its lifecycle methods).
144
+ *
145
+ * TWO events leave here, and they are not the same message:
146
+ *
147
+ * - `device.meta-changed` fires on EVERY call. It is the meta-surface echo
148
+ * every UI redraws on, and idempotence is not interesting to it.
149
+ * - `device.disabled` / `device.enabled` fire ONLY when the flag actually
150
+ * MOVES. They are the lifecycle announcement — the operator asked to be
151
+ * told when a camera is switched off, and a write that re-states a flag the
152
+ * device already had is not that. Telemetry semantics (D8) are fine here: a
153
+ * dropped announcement costs a notification, never durable state, and the
154
+ * meta row is the authority either way (D62 — this switch stores nothing of
155
+ * its own).
144
156
  */
145
157
  export declare function setDisabled(pctx: ProviderContext, input: Parameters<IDeviceManagerProvider['setDisabled']>[0]): ReturnType<IDeviceManagerProvider['setDisabled']>;
146
158
  export declare function loadRuntimeState(pctx: ProviderContext, input: Parameters<IDeviceManagerProvider['loadRuntimeState']>[0]): ReturnType<IDeviceManagerProvider['loadRuntimeState']>;
@@ -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-CqiMPPs0.js");
6
+ const require_dist = require("../../dist-DiScsb8j.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 { Ht as EventCategory, P as doorbellCapability, St as DeviceType, U as isSameAddonId, gt as BaseAddon, ht as errMsg, kt as createEvent, w as bareAddonId } from "../../dist-EAvNJD24.mjs";
1
+ import { Ht as EventCategory, P as doorbellCapability, St as DeviceType, U as isSameAddonId, gt as BaseAddon, ht as errMsg, kt as createEvent, w as bareAddonId } from "../../dist-DWuNENzg.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-CqiMPPs0.js");
6
+ const require_dist = require("../../dist-DiScsb8j.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 { J as logDestinationCapability, gt as BaseAddon } from "../../dist-EAvNJD24.mjs";
1
+ import { J as logDestinationCapability, gt as BaseAddon } from "../../dist-DWuNENzg.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-CqiMPPs0.js");
3
+ const require_dist = require("../../dist-DiScsb8j.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 { Ht as EventCategory, gt as BaseAddon, ht as errMsg, kt as createEvent } from "../../dist-EAvNJD24.mjs";
1
+ import { Ht as EventCategory, gt as BaseAddon, ht as errMsg, kt as createEvent } from "../../dist-DWuNENzg.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-CqiMPPs0.js");
6
+ const require_dist = require("../../dist-DiScsb8j.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 { S as authProviderCapability, _ as UserRecordSchema, ft as userManagementCapability, gt as BaseAddon, m as ScopedTokenSchema, r as ApiKeyRecordSchema } from "../../dist-EAvNJD24.mjs";
2
+ import { S as authProviderCapability, _ as UserRecordSchema, ft as userManagementCapability, gt as BaseAddon, m as ScopedTokenSchema, r as ApiKeyRecordSchema } from "../../dist-DWuNENzg.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-CqiMPPs0.js");
3
+ const require_dist = require("../../dist-DiScsb8j.js");
4
4
  let node_os = require("node:os");
5
5
  node_os = require_chunk.__toESM(node_os);
6
6
  //#region src/builtins/local-network/local-network.addon.ts
@@ -1,4 +1,4 @@
1
- import { Ht as EventCategory, gt as BaseAddon, q as localNetworkCapability } from "../../dist-EAvNJD24.mjs";
1
+ import { Ht as EventCategory, gt as BaseAddon, q as localNetworkCapability } from "../../dist-DWuNENzg.mjs";
2
2
  import * as os from "node:os";
3
3
  //#region src/builtins/local-network/local-network.addon.ts
4
4
  /**
@@ -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-CqiMPPs0.js");
6
+ const require_dist = require("../../dist-DiScsb8j.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 { J as logDestinationCapability, gt as BaseAddon } from "../../dist-EAvNJD24.mjs";
1
+ import { J as logDestinationCapability, gt as BaseAddon } from "../../dist-DWuNENzg.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-CqiMPPs0.js");
6
+ const require_dist = require("../../dist-DiScsb8j.js");
7
7
  let node_child_process = require("node:child_process");
8
8
  let node_util = require("node:util");
9
9
  let node_os = require("node:os");
@@ -1,4 +1,4 @@
1
- import { Ht as EventCategory, Z as metricsProviderCapability, gt as BaseAddon, kt as createEvent } from "../../dist-EAvNJD24.mjs";
1
+ import { Ht as EventCategory, Z as metricsProviderCapability, gt as BaseAddon, kt as createEvent } from "../../dist-DWuNENzg.mjs";
2
2
  import { execFile, execFileSync } from "node:child_process";
3
3
  import { promisify } from "node:util";
4
4
  import * as os from "node:os";
@@ -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-CqiMPPs0.js");
6
+ const require_dist = require("../../dist-DiScsb8j.js");
7
7
  let node_child_process = require("node:child_process");
8
8
  let node_util = require("node:util");
9
9
  let node_os = require("node:os");
@@ -1,5 +1,5 @@
1
1
  import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
2
- import { F as enumerateInferenceDevices, Ht as EventCategory, at as scoreRuntimes, gt as BaseAddon, ht as errMsg, jt as emitReadiness, tt as platformProbeCapability } from "../../dist-EAvNJD24.mjs";
2
+ import { F as enumerateInferenceDevices, Ht as EventCategory, at as scoreRuntimes, gt as BaseAddon, ht as errMsg, jt as emitReadiness, tt as platformProbeCapability } from "../../dist-DWuNENzg.mjs";
3
3
  import { execFile } from "node:child_process";
4
4
  import { promisify } from "node:util";
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-CqiMPPs0.js");
6
+ const require_dist = require("../../dist-DiScsb8j.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 { Ht as EventCategory, gt as BaseAddon } from "../../dist-EAvNJD24.mjs";
1
+ import { Ht as EventCategory, gt as BaseAddon } from "../../dist-DWuNENzg.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-CqiMPPs0.js");
6
+ const require_dist = require("../../dist-DiScsb8j.js");
7
7
  let zod = require("zod");
8
8
  let node_crypto = require("node:crypto");
9
9
  let _camstack_types_node = require("@camstack/types/node");
@@ -871,6 +871,11 @@ var SnapshotAddon = class SnapshotAddon extends require_dist.BaseAddon {
871
871
  * currently holding, which is a fleet-wide blank grid after each restart. */
872
872
  linkSecret = null;
873
873
  /**
874
+ * In-flight `serveLinkDataPlane` attempt, so the retry below cannot start a
875
+ * second one while the first is still awaiting the settings store.
876
+ */
877
+ linkPlaneAttempt = null;
878
+ /**
874
879
  * How a full frame becomes a card-sized one. Defaults to the real ffmpeg
875
880
  * resize; injectable so the wrapper's own logic is testable without spawning a
876
881
  * process for every fixture. `JpegResizer` was declared for exactly this and
@@ -899,7 +904,7 @@ var SnapshotAddon = class SnapshotAddon extends require_dist.BaseAddon {
899
904
  if (typeof deviceId === "number") this.evictRemovedDevice(deviceId, "device-unregistered");
900
905
  });
901
906
  await this.serveMediaDataPlane();
902
- await this.serveLinkDataPlane();
907
+ await this.ensureLinkDataPlane();
903
908
  return [{
904
909
  capability: require_dist.snapshotCapability,
905
910
  provider
@@ -932,6 +937,33 @@ var SnapshotAddon = class SnapshotAddon extends require_dist.BaseAddon {
932
937
  }
933
938
  }
934
939
  /**
940
+ * Serve the link plane if it is not already served — at init, and again on
941
+ * the first `getSnapshotLinks` after an attempt that could not finish.
942
+ *
943
+ * The retry is not defensive padding, it is the whole reason this wrapper
944
+ * exists. The plane needs a persisted signing secret, and the secret needs
945
+ * the settings store; this addon now boots in its OWN process, so "the store
946
+ * is not answering yet" is a state it can genuinely observe. A one-shot
947
+ * attempt turns that instant into a permanent condition: `linkDataPlane`
948
+ * stays null, every `getSnapshotLinks` row answers `url: null`, and every
949
+ * tile on every grid stays blank until someone restarts the runner. Nothing
950
+ * in the logs would say why, because nothing failed twice.
951
+ *
952
+ * Single-flighted: a burst of link requests joins one attempt.
953
+ */
954
+ async ensureLinkDataPlane() {
955
+ if (this.linkDataPlane !== null) return;
956
+ if (this.linkPlaneAttempt !== null) {
957
+ await this.linkPlaneAttempt;
958
+ return;
959
+ }
960
+ const attempt = this.serveLinkDataPlane().finally(() => {
961
+ this.linkPlaneAttempt = null;
962
+ });
963
+ this.linkPlaneAttempt = attempt;
964
+ await attempt;
965
+ }
966
+ /**
935
967
  * Serve the PUBLIC link plane: `GET /addon/snapshot/link/<deviceId>.jpg?w&v&exp&sig`.
936
968
  *
937
969
  * `access: 'public'` is deliberate and is the only interesting decision here.
@@ -1000,20 +1032,42 @@ var SnapshotAddon = class SnapshotAddon extends require_dist.BaseAddon {
1000
1032
  linkRoutePrefix() {
1001
1033
  return `/addon/${require_dist.bareAddonId(this.ctx.id)}/link`;
1002
1034
  }
1003
- /** Mint-once-and-persist the signing secret. Null if it cannot be stored — a
1004
- * secret held only in memory would break every outstanding link on restart,
1005
- * so no plane is better than a plane whose links die unpredictably. */
1035
+ /**
1036
+ * Mint-once-and-persist the signing secret. Null when it cannot be
1037
+ * ESTABLISHED a secret held only in memory would break every outstanding
1038
+ * link on restart, so no plane is better than a plane whose links die
1039
+ * unpredictably.
1040
+ *
1041
+ * A null is never cached, so the next `getSnapshotLinks` tries again. That
1042
+ * matters now the wrapper runs in its own process: the settings store can be
1043
+ * a moment behind at boot, and this used to be a one-shot.
1044
+ *
1045
+ * The read-back is the gate. `state.get()` cannot distinguish "no secret
1046
+ * yet" from "the store could not be read" — `readAddonStoreWithRetry` gives
1047
+ * up and answers `{}` — so a store that is merely slow reads as empty and
1048
+ * this would mint a SECOND secret over a perfectly good first one, silently
1049
+ * invalidating every link a client is holding. Reading back what we just
1050
+ * wrote is the only evidence available at this layer that the store is
1051
+ * actually answering, and a mismatch means it is not.
1052
+ */
1006
1053
  async ensureLinkSecret() {
1007
1054
  if (this.linkSecret !== null) return this.linkSecret;
1008
1055
  try {
1009
1056
  const state = this.state("snapshotLinkSecret", zod.z.string(), "");
1010
- let secret = await state.get();
1011
- if (secret === "") {
1012
- secret = (0, node_crypto.randomUUID)().replace(/-/g, "");
1013
- await state.set(secret);
1057
+ const stored = await state.get();
1058
+ if (stored !== "") {
1059
+ this.linkSecret = stored;
1060
+ return stored;
1061
+ }
1062
+ const minted = (0, node_crypto.randomUUID)().replace(/-/g, "");
1063
+ await state.set(minted);
1064
+ const confirmed = await state.get();
1065
+ if (confirmed !== minted) {
1066
+ this.ctx.logger.warn("snapshot: link secret did not persist — deferring the link plane rather than signing with a secret that will not survive", { meta: { confirmedEmpty: confirmed === "" } });
1067
+ return null;
1014
1068
  }
1015
- this.linkSecret = secret;
1016
- return secret;
1069
+ this.linkSecret = minted;
1070
+ return minted;
1017
1071
  } catch (err) {
1018
1072
  this.ctx.logger.warn("snapshot: could not establish link signing secret", { meta: { error: require_dist.errMsg(err) } });
1019
1073
  return null;
@@ -1044,6 +1098,7 @@ var SnapshotAddon = class SnapshotAddon extends require_dist.BaseAddon {
1044
1098
  * forward.
1045
1099
  */
1046
1100
  async getSnapshotLinks(input) {
1101
+ await this.ensureLinkDataPlane();
1047
1102
  const secret = await this.ensureLinkSecret();
1048
1103
  const expMs = snapshotLinkExpiry(Date.now(), SNAPSHOT_LINK_TTL_MS);
1049
1104
  const served = this.linkDataPlane !== null && secret !== null;