@camstack/system 1.2.66 → 1.2.67

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 (53) 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/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  8. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  9. package/dist/builtins/console-logging/index.js +1 -1
  10. package/dist/builtins/console-logging/index.mjs +1 -1
  11. package/dist/builtins/core-blocks/blocks-integration.d.ts +24 -0
  12. package/dist/builtins/core-blocks/core-blocks.addon.d.ts +14 -0
  13. package/dist/builtins/core-blocks/core-blocks.addon.js +99 -2
  14. package/dist/builtins/core-blocks/core-blocks.addon.mjs +99 -2
  15. package/dist/builtins/device-manager/device-manager.addon.js +3 -1
  16. package/dist/builtins/device-manager/device-manager.addon.mjs +3 -1
  17. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  18. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  19. package/dist/builtins/hub-forwarder/index.js +1 -1
  20. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  21. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  22. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  23. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  24. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  25. package/dist/builtins/local-network/local-network.addon.js +1 -1
  26. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  27. package/dist/builtins/loki-logging/index.js +1 -1
  28. package/dist/builtins/loki-logging/index.mjs +1 -1
  29. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  30. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  31. package/dist/builtins/platform-probe/index.js +1 -1
  32. package/dist/builtins/platform-probe/index.mjs +1 -1
  33. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  34. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  35. package/dist/builtins/snapshot/index.js +1 -1
  36. package/dist/builtins/snapshot/index.mjs +1 -1
  37. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  38. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  39. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +1 -1
  40. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
  41. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  42. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  43. package/dist/builtins/system-config/system-config.addon.js +1 -1
  44. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  45. package/dist/builtins/winston-logging/index.js +1 -1
  46. package/dist/builtins/winston-logging/index.mjs +1 -1
  47. package/dist/{dist-DqPyKwVB.mjs → dist-CbS6uoSb.mjs} +2465 -2075
  48. package/dist/{dist-CIkGOAcz.js → dist-NRYjb8qK.js} +1891 -1489
  49. package/dist/index.js +28 -4
  50. package/dist/index.mjs +28 -4
  51. package/dist/kernel/cap-router-builder.d.ts +19 -0
  52. package/dist/kernel/capability-registry.d.ts +14 -1
  53. 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-CIkGOAcz.js");
6
+ const require_dist = require("../../dist-NRYjb8qK.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_fs = require("node:fs");
@@ -1,4 +1,4 @@
1
- import { Lt as EventCategory, dt as BaseAddon, h as addonPagesCapability, ut as errMsg } from "../../dist-DqPyKwVB.mjs";
1
+ import { _ as addonPagesCapability, ft as errMsg, pt as BaseAddon, zt as EventCategory } from "../../dist-CbS6uoSb.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as fs 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-CIkGOAcz.js");
6
+ const require_dist = require("../../dist-NRYjb8qK.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_fs = require("node:fs");
@@ -1,4 +1,4 @@
1
- import { Lt as EventCategory, _ as addonWidgetsCapability, dt as BaseAddon, ut as errMsg } from "../../dist-DqPyKwVB.mjs";
1
+ import { ft as errMsg, pt as BaseAddon, y as addonWidgetsCapability, zt as EventCategory } from "../../dist-CbS6uoSb.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as fs from "node:fs";
@@ -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-CIkGOAcz.js");
6
+ const require_dist = require("../../dist-NRYjb8qK.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 { Lt as EventCategory, dt as BaseAddon, n as AlertSchema, ut as errMsg, v as alertsCapability, wt as createEvent } from "../../dist-DqPyKwVB.mjs";
1
+ import { Et as createEvent, b as alertsCapability, ft as errMsg, n as AlertSchema, pt as BaseAddon, zt as EventCategory } from "../../dist-CbS6uoSb.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-CIkGOAcz.js");
6
+ const require_dist = require("../../dist-NRYjb8qK.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let node_fs = require("node:fs");
9
9
  let node_fs$1 = require_chunk.__toESM(node_fs, 1);
@@ -1,4 +1,4 @@
1
- import { Lt as EventCategory, b as backupCapability, dt as BaseAddon } from "../../dist-DqPyKwVB.mjs";
1
+ import { S as backupCapability, pt as BaseAddon, zt as EventCategory } from "../../dist-CbS6uoSb.mjs";
2
2
  import { randomBytes, randomUUID } from "node:crypto";
3
3
  import * as fs from "node:fs";
4
4
  import Kt from "node:fs";
@@ -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-CIkGOAcz.js");
6
+ const require_dist = require("../../dist-NRYjb8qK.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 { G as logDestinationCapability, dt as BaseAddon } from "../../dist-DqPyKwVB.mjs";
1
+ import { pt as BaseAddon, q as logDestinationCapability } from "../../dist-CbS6uoSb.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 = {
@@ -0,0 +1,24 @@
1
+ import { DeclaredDevicePorts } from '@camstack/types';
2
+ /** Operator-visible name of the shared integration. Seeded once, at create. */
3
+ export declare const BLOCKS_INTEGRATION_NAME = "Blocks";
4
+ /**
5
+ * The declared-device ports minus the owner — `reconcileBlocksIntegration`
6
+ * pins that itself, because getting it wrong is the entire bug above.
7
+ */
8
+ export type BlocksIntegrationPorts = Omit<DeclaredDevicePorts, 'addonId'>;
9
+ /**
10
+ * Reconcile the shared integration. Idempotent; safe on every boot.
11
+ *
12
+ * Returns its id, or `null` when this node does not own the declaration (it is
13
+ * hub-owned, like every declaration).
14
+ *
15
+ * **The trap, stated where it lives:** `devices: []` makes every row the index
16
+ * returns under this integration a withdrawal candidate. That is safe ONLY
17
+ * because `listOwnDevices` is scoped to `core-blocks` and a block's devices are
18
+ * owned by `core-block-<uuid>` — so the listing is empty. Two ways to destroy
19
+ * that: wire `listOwnDevices` without its owner filter (it would then answer
20
+ * with every device on the cluster, and the sweep would delete the blocks'), or
21
+ * make `core-blocks` declare a device of its own. Neither is worth the
22
+ * convenience; the spec test pins both.
23
+ */
24
+ export declare function reconcileBlocksIntegration(ports: BlocksIntegrationPorts): Promise<string | null>;
@@ -12,6 +12,8 @@ export declare class CoreBlocksAddon extends BaseAddon {
12
12
  private transpiler;
13
13
  private typeDefs;
14
14
  private supervisor;
15
+ /** The shared integration, or null when this node does not own it. */
16
+ private blocksIntegrationId;
15
17
  protected onInitialize(): Promise<ProviderRegistration[]>;
16
18
  protected onShutdown(): Promise<void>;
17
19
  /**
@@ -22,6 +24,18 @@ export declare class CoreBlocksAddon extends BaseAddon {
22
24
  * no cluster broker, which is the only door to `$process.*`.
23
25
  */
24
26
  private buildSupervisor;
27
+ /**
28
+ * Own the integration blocks hang their devices from.
29
+ *
30
+ * A block cannot: every integration read is gated on the installed-addon set
31
+ * and `core-block-<uuid>` is never in it, so a block's get-or-create minted a
32
+ * fresh invisible row on every start. `core-blocks` IS installed, so it does
33
+ * this once here and a block names the result by id.
34
+ *
35
+ * Failure is a feature missing, not a boot failure — blocks that declare no
36
+ * devices are unaffected, and the next boot converges.
37
+ */
38
+ private declareBlocksIntegration;
25
39
  /**
26
40
  * Reconcile now, after a write.
27
41
  *
@@ -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-CIkGOAcz.js");
3
+ const require_dist = require("../../dist-NRYjb8qK.js");
4
4
  let node_crypto = require("node:crypto");
5
5
  let node_path = require("node:path");
6
6
  node_path = require_chunk.__toESM(node_path);
@@ -667,6 +667,61 @@ var CoreBlockStore = class {
667
667
  }
668
668
  };
669
669
  //#endregion
670
+ //#region src/builtins/core-blocks/blocks-integration.ts
671
+ /**
672
+ * The integration every block's devices hang from.
673
+ *
674
+ * ## Why it is not the block's own
675
+ *
676
+ * A core block CANNOT own an integration, and the failure was silent and
677
+ * accumulating. Every integration READ is gated on the installed-addon set
678
+ * (`addon-registry.service.ts` → `createFilteredRegistry`), and a block's addon
679
+ * id — `core-block-<uuid>` — is never in that set: a block is materialised and
680
+ * spawned, not installed. So `getIntegrationByAddonId` answered `null` forever
681
+ * while `createIntegration` succeeded, and every block start minted a fresh row
682
+ * that `listIntegrations` then hid and `integrations.delete` refused to remove.
683
+ * Measured on the live hub 2026-08-09: `int_0021` and `int_0022`, two starts of
684
+ * one block, invisible and undeletable through the API.
685
+ *
686
+ * `core-blocks` is an installed builtin, so it passes the filter. It reconciles
687
+ * ONE integration; a block names that integration by id
688
+ * (`DeclaredDevicesSpec.integrationId`) and mints nothing. Ownership of the
689
+ * DEVICE is untouched — that is `(addonId, stableId)`, and `listOwnDevices`
690
+ * stays addon-scoped, which is what keeps one block's withdrawal sweep away
691
+ * from another block's devices now that the integration no longer distinguishes
692
+ * them.
693
+ *
694
+ * The block-side read is
695
+ * `ctx.api.integrations.getByAddonId.query({ addonId: CORE_BLOCKS_ADDON_ID })`
696
+ * — legal, because `core-blocks` is installed. No new cap method is needed.
697
+ */
698
+ /** Operator-visible name of the shared integration. Seeded once, at create. */
699
+ var BLOCKS_INTEGRATION_NAME = "Blocks";
700
+ /**
701
+ * Reconcile the shared integration. Idempotent; safe on every boot.
702
+ *
703
+ * Returns its id, or `null` when this node does not own the declaration (it is
704
+ * hub-owned, like every declaration).
705
+ *
706
+ * **The trap, stated where it lives:** `devices: []` makes every row the index
707
+ * returns under this integration a withdrawal candidate. That is safe ONLY
708
+ * because `listOwnDevices` is scoped to `core-blocks` and a block's devices are
709
+ * owned by `core-block-<uuid>` — so the listing is empty. Two ways to destroy
710
+ * that: wire `listOwnDevices` without its owner filter (it would then answer
711
+ * with every device on the cluster, and the sweep would delete the blocks'), or
712
+ * make `core-blocks` declare a device of its own. Neither is worth the
713
+ * convenience; the spec test pins both.
714
+ */
715
+ async function reconcileBlocksIntegration(ports) {
716
+ return (await new require_dist.DeclaredDevices({
717
+ ...ports,
718
+ addonId: require_dist.CORE_BLOCKS_ADDON_ID
719
+ }).reconcile({
720
+ integrationName: BLOCKS_INTEGRATION_NAME,
721
+ devices: []
722
+ })).integrationId;
723
+ }
724
+ //#endregion
670
725
  //#region src/builtins/core-blocks/core-blocks.addon.ts
671
726
  /**
672
727
  * The `core-blocks` provider — CRUD over user-authored TypeScript, with the
@@ -710,6 +765,8 @@ var CoreBlocksAddon = class extends require_dist.BaseAddon {
710
765
  transpiler = null;
711
766
  typeDefs = null;
712
767
  supervisor = null;
768
+ /** The shared integration, or null when this node does not own it. */
769
+ blocksIntegrationId = null;
713
770
  async onInitialize() {
714
771
  const settingsStore = this.ctx.api?.settingsStore;
715
772
  if (!settingsStore) throw new Error("core-blocks: settings-store API not available — refusing to register the cap");
@@ -720,12 +777,14 @@ var CoreBlocksAddon = class extends require_dist.BaseAddon {
720
777
  });
721
778
  await this.store.load();
722
779
  this.transpiler = await loadTranspiler(this.ctx.logger);
780
+ this.blocksIntegrationId = await this.declareBlocksIntegration();
723
781
  this.supervisor = this.buildSupervisor();
724
782
  this.supervisor?.start(CONVERGE_INTERVAL_MS);
725
783
  this.ctx.logger.info("core-blocks ready", { meta: {
726
784
  blocks: this.store.list().length,
727
785
  compile: this.transpiler !== null,
728
- supervising: this.supervisor !== null
786
+ supervising: this.supervisor !== null,
787
+ integrationId: this.blocksIntegrationId
729
788
  } });
730
789
  return [{
731
790
  capability: require_dist.coreBlocksCapability,
@@ -782,6 +841,44 @@ var CoreBlocksAddon = class extends require_dist.BaseAddon {
782
841
  });
783
842
  }
784
843
  /**
844
+ * Own the integration blocks hang their devices from.
845
+ *
846
+ * A block cannot: every integration read is gated on the installed-addon set
847
+ * and `core-block-<uuid>` is never in it, so a block's get-or-create minted a
848
+ * fresh invisible row on every start. `core-blocks` IS installed, so it does
849
+ * this once here and a block names the result by id.
850
+ *
851
+ * Failure is a feature missing, not a boot failure — blocks that declare no
852
+ * devices are unaffected, and the next boot converges.
853
+ */
854
+ async declareBlocksIntegration() {
855
+ const api = this.ctx.api;
856
+ if (!api?.integrations || !api?.deviceManager) {
857
+ this.ctx.logger.warn("core-blocks: no integrations API — a block that declares a device will have nowhere to hang it");
858
+ return null;
859
+ }
860
+ try {
861
+ return await reconcileBlocksIntegration({
862
+ logger: this.ctx.logger.child("integration"),
863
+ devices: this.ctx.kernel.devices,
864
+ localNodeId: this.ctx.kernel.localNodeId,
865
+ getIntegration: async (addonId) => api.integrations.getByAddonId.query({ addonId }),
866
+ createIntegration: async (input) => api.integrations.create.mutate(input),
867
+ updateIntegration: async ({ id, info }) => {
868
+ await api.integrations.update.mutate({
869
+ id,
870
+ info,
871
+ skipRestart: true
872
+ });
873
+ },
874
+ listOwnDevices: async () => api.deviceManager.listAll.query({ addonId: require_dist.CORE_BLOCKS_ADDON_ID })
875
+ });
876
+ } catch (err) {
877
+ this.ctx.logger.warn("core-blocks: the shared integration could not be reconciled", { meta: { error: err instanceof Error ? err.message : String(err) } });
878
+ return null;
879
+ }
880
+ }
881
+ /**
785
882
  * Reconcile now, after a write.
786
883
  *
787
884
  * The timer would get there on its own, but an operator who presses Save and
@@ -1,4 +1,4 @@
1
- import { C as coreBlockAddonId, T as coreBlocksCapability, dt as BaseAddon, o as CORE_BLOCK_ADDON_PREFIX, s as CoreBlockSchema, w as coreBlockIdFromAddonId } from "../../dist-DqPyKwVB.mjs";
1
+ import { D as coreBlocksCapability, E as coreBlockIdFromAddonId, T as coreBlockAddonId, c as CoreBlockSchema, l as DeclaredDevices, o as CORE_BLOCKS_ADDON_ID, pt as BaseAddon, s as CORE_BLOCK_ADDON_PREFIX } from "../../dist-CbS6uoSb.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import * as path$1 from "node:path";
4
4
  import * as fs from "node:fs/promises";
@@ -663,6 +663,61 @@ var CoreBlockStore = class {
663
663
  }
664
664
  };
665
665
  //#endregion
666
+ //#region src/builtins/core-blocks/blocks-integration.ts
667
+ /**
668
+ * The integration every block's devices hang from.
669
+ *
670
+ * ## Why it is not the block's own
671
+ *
672
+ * A core block CANNOT own an integration, and the failure was silent and
673
+ * accumulating. Every integration READ is gated on the installed-addon set
674
+ * (`addon-registry.service.ts` → `createFilteredRegistry`), and a block's addon
675
+ * id — `core-block-<uuid>` — is never in that set: a block is materialised and
676
+ * spawned, not installed. So `getIntegrationByAddonId` answered `null` forever
677
+ * while `createIntegration` succeeded, and every block start minted a fresh row
678
+ * that `listIntegrations` then hid and `integrations.delete` refused to remove.
679
+ * Measured on the live hub 2026-08-09: `int_0021` and `int_0022`, two starts of
680
+ * one block, invisible and undeletable through the API.
681
+ *
682
+ * `core-blocks` is an installed builtin, so it passes the filter. It reconciles
683
+ * ONE integration; a block names that integration by id
684
+ * (`DeclaredDevicesSpec.integrationId`) and mints nothing. Ownership of the
685
+ * DEVICE is untouched — that is `(addonId, stableId)`, and `listOwnDevices`
686
+ * stays addon-scoped, which is what keeps one block's withdrawal sweep away
687
+ * from another block's devices now that the integration no longer distinguishes
688
+ * them.
689
+ *
690
+ * The block-side read is
691
+ * `ctx.api.integrations.getByAddonId.query({ addonId: CORE_BLOCKS_ADDON_ID })`
692
+ * — legal, because `core-blocks` is installed. No new cap method is needed.
693
+ */
694
+ /** Operator-visible name of the shared integration. Seeded once, at create. */
695
+ var BLOCKS_INTEGRATION_NAME = "Blocks";
696
+ /**
697
+ * Reconcile the shared integration. Idempotent; safe on every boot.
698
+ *
699
+ * Returns its id, or `null` when this node does not own the declaration (it is
700
+ * hub-owned, like every declaration).
701
+ *
702
+ * **The trap, stated where it lives:** `devices: []` makes every row the index
703
+ * returns under this integration a withdrawal candidate. That is safe ONLY
704
+ * because `listOwnDevices` is scoped to `core-blocks` and a block's devices are
705
+ * owned by `core-block-<uuid>` — so the listing is empty. Two ways to destroy
706
+ * that: wire `listOwnDevices` without its owner filter (it would then answer
707
+ * with every device on the cluster, and the sweep would delete the blocks'), or
708
+ * make `core-blocks` declare a device of its own. Neither is worth the
709
+ * convenience; the spec test pins both.
710
+ */
711
+ async function reconcileBlocksIntegration(ports) {
712
+ return (await new DeclaredDevices({
713
+ ...ports,
714
+ addonId: CORE_BLOCKS_ADDON_ID
715
+ }).reconcile({
716
+ integrationName: BLOCKS_INTEGRATION_NAME,
717
+ devices: []
718
+ })).integrationId;
719
+ }
720
+ //#endregion
666
721
  //#region src/builtins/core-blocks/core-blocks.addon.ts
667
722
  /**
668
723
  * The `core-blocks` provider — CRUD over user-authored TypeScript, with the
@@ -706,6 +761,8 @@ var CoreBlocksAddon = class extends BaseAddon {
706
761
  transpiler = null;
707
762
  typeDefs = null;
708
763
  supervisor = null;
764
+ /** The shared integration, or null when this node does not own it. */
765
+ blocksIntegrationId = null;
709
766
  async onInitialize() {
710
767
  const settingsStore = this.ctx.api?.settingsStore;
711
768
  if (!settingsStore) throw new Error("core-blocks: settings-store API not available — refusing to register the cap");
@@ -716,12 +773,14 @@ var CoreBlocksAddon = class extends BaseAddon {
716
773
  });
717
774
  await this.store.load();
718
775
  this.transpiler = await loadTranspiler(this.ctx.logger);
776
+ this.blocksIntegrationId = await this.declareBlocksIntegration();
719
777
  this.supervisor = this.buildSupervisor();
720
778
  this.supervisor?.start(CONVERGE_INTERVAL_MS);
721
779
  this.ctx.logger.info("core-blocks ready", { meta: {
722
780
  blocks: this.store.list().length,
723
781
  compile: this.transpiler !== null,
724
- supervising: this.supervisor !== null
782
+ supervising: this.supervisor !== null,
783
+ integrationId: this.blocksIntegrationId
725
784
  } });
726
785
  return [{
727
786
  capability: coreBlocksCapability,
@@ -778,6 +837,44 @@ var CoreBlocksAddon = class extends BaseAddon {
778
837
  });
779
838
  }
780
839
  /**
840
+ * Own the integration blocks hang their devices from.
841
+ *
842
+ * A block cannot: every integration read is gated on the installed-addon set
843
+ * and `core-block-<uuid>` is never in it, so a block's get-or-create minted a
844
+ * fresh invisible row on every start. `core-blocks` IS installed, so it does
845
+ * this once here and a block names the result by id.
846
+ *
847
+ * Failure is a feature missing, not a boot failure — blocks that declare no
848
+ * devices are unaffected, and the next boot converges.
849
+ */
850
+ async declareBlocksIntegration() {
851
+ const api = this.ctx.api;
852
+ if (!api?.integrations || !api?.deviceManager) {
853
+ this.ctx.logger.warn("core-blocks: no integrations API — a block that declares a device will have nowhere to hang it");
854
+ return null;
855
+ }
856
+ try {
857
+ return await reconcileBlocksIntegration({
858
+ logger: this.ctx.logger.child("integration"),
859
+ devices: this.ctx.kernel.devices,
860
+ localNodeId: this.ctx.kernel.localNodeId,
861
+ getIntegration: async (addonId) => api.integrations.getByAddonId.query({ addonId }),
862
+ createIntegration: async (input) => api.integrations.create.mutate(input),
863
+ updateIntegration: async ({ id, info }) => {
864
+ await api.integrations.update.mutate({
865
+ id,
866
+ info,
867
+ skipRestart: true
868
+ });
869
+ },
870
+ listOwnDevices: async () => api.deviceManager.listAll.query({ addonId: CORE_BLOCKS_ADDON_ID })
871
+ });
872
+ } catch (err) {
873
+ this.ctx.logger.warn("core-blocks: the shared integration could not be reconciled", { meta: { error: err instanceof Error ? err.message : String(err) } });
874
+ return null;
875
+ }
876
+ }
877
+ /**
781
878
  * Reconcile now, after a write.
782
879
  *
783
880
  * The timer would get there on its own, but an operator who presses Save and
@@ -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-CIkGOAcz.js");
6
+ const require_dist = require("../../dist-NRYjb8qK.js");
7
7
  let _camstack_types_node = require("@camstack/types/node");
8
8
  let node_crypto = require("node:crypto");
9
9
  //#region src/builtins/device-manager/day-night-config-schema.ts
@@ -683,6 +683,8 @@ async function listPersistedByAddon(pctx, input) {
683
683
  });
684
684
  }
685
685
  async function listAll(pctx, input) {
686
+ const ownerFilter = Reflect.get(input, "addonId");
687
+ if (ownerFilter !== void 0 && (typeof ownerFilter !== "string" || ownerFilter.length === 0)) throw new Error(`deviceManager.listAll: addonId must be a non-empty string or be omitted — got ${JSON.stringify(ownerFilter)}. An owner filter that is present but empty is never widened to every device. On an addon context the id is \`ctx.id\`; there is no \`ctx.addonId\`.`);
686
688
  const { addonId } = input;
687
689
  const slim = input.projection === "slim";
688
690
  const camerasOnly = input.isCamera === true;
@@ -1,4 +1,4 @@
1
- import { A as deviceStatusCapability, It as sleep, Lt as EventCategory, N as enumerateItemArrayFields, O as deviceManagerCapability, P as enumerateSchemaFields, Q as parseStreamParamsFormPatch, S as buildStreamParamsConfigSchema, Y as normalizeUnit, _t as DeviceType, a as CAP_NAMES_WITH_STATUS, bt as WELL_KNOWN_TAB_MAP, c as DeviceStatusSchema, d as STREAM_PROFILE_META, dt as BaseAddon, gt as DeviceRole, ht as DeviceFeature, k as deviceStateCapability, kt as isDeviceConfigCap, t as ALL_CAPABILITY_DEFINITIONS, ut as errMsg } from "../../dist-DqPyKwVB.mjs";
1
+ import { A as deviceManagerCapability, F as enumerateItemArrayFields, I as enumerateSchemaFields, M as deviceStatusCapability, Rt as sleep, St as WELL_KNOWN_TAB_MAP, Z as normalizeUnit, _t as DeviceFeature, a as CAP_NAMES_WITH_STATUS, et as parseStreamParamsFormPatch, ft as errMsg, j as deviceStateCapability, jt as isDeviceConfigCap, p as STREAM_PROFILE_META, pt as BaseAddon, t as ALL_CAPABILITY_DEFINITIONS, u as DeviceStatusSchema, vt as DeviceRole, w as buildStreamParamsConfigSchema, yt as DeviceType, zt as EventCategory } from "../../dist-CbS6uoSb.mjs";
2
2
  import { canonicalDeviceFingerprint } from "@camstack/types/node";
3
3
  import { randomUUID } from "node:crypto";
4
4
  //#region src/builtins/device-manager/day-night-config-schema.ts
@@ -678,6 +678,8 @@ async function listPersistedByAddon(pctx, input) {
678
678
  });
679
679
  }
680
680
  async function listAll(pctx, input) {
681
+ const ownerFilter = Reflect.get(input, "addonId");
682
+ if (ownerFilter !== void 0 && (typeof ownerFilter !== "string" || ownerFilter.length === 0)) throw new Error(`deviceManager.listAll: addonId must be a non-empty string or be omitted — got ${JSON.stringify(ownerFilter)}. An owner filter that is present but empty is never widened to every device. On an addon context the id is \`ctx.id\`; there is no \`ctx.addonId\`.`);
681
683
  const { addonId } = input;
682
684
  const slim = input.projection === "slim";
683
685
  const camerasOnly = input.isCamera === true;
@@ -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-CIkGOAcz.js");
6
+ const require_dist = require("../../dist-NRYjb8qK.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 isSameAddonId, Lt as EventCategory, _t as DeviceType, dt as BaseAddon, j as doorbellCapability, ut as errMsg, wt as createEvent, x as bareAddonId } from "../../dist-DqPyKwVB.mjs";
1
+ import { C as bareAddonId, Et as createEvent, H as isSameAddonId, N as doorbellCapability, ft as errMsg, pt as BaseAddon, yt as DeviceType, zt as EventCategory } from "../../dist-CbS6uoSb.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-CIkGOAcz.js");
6
+ const require_dist = require("../../dist-NRYjb8qK.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 { G as logDestinationCapability, dt as BaseAddon } from "../../dist-DqPyKwVB.mjs";
1
+ import { pt as BaseAddon, q as logDestinationCapability } from "../../dist-CbS6uoSb.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-CIkGOAcz.js");
3
+ const require_dist = require("../../dist-NRYjb8qK.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 { Lt as EventCategory, dt as BaseAddon, ut as errMsg, wt as createEvent } from "../../dist-DqPyKwVB.mjs";
1
+ import { Et as createEvent, ft as errMsg, pt as BaseAddon, zt as EventCategory } from "../../dist-CbS6uoSb.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-CIkGOAcz.js");
6
+ const require_dist = require("../../dist-NRYjb8qK.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 { X as oauthIntegrationCapability, dt as BaseAddon, f as ScopedTokenSchema, m as UserRecordSchema, r as ApiKeyRecordSchema, st as userManagementCapability, y as authProviderCapability } from "../../dist-DqPyKwVB.mjs";
2
+ import { Q as oauthIntegrationCapability, g as UserRecordSchema, lt as userManagementCapability, m as ScopedTokenSchema, pt as BaseAddon, r as ApiKeyRecordSchema, x as authProviderCapability } from "../../dist-CbS6uoSb.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-CIkGOAcz.js");
3
+ const require_dist = require("../../dist-NRYjb8qK.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 { Lt as EventCategory, W as localNetworkCapability, dt as BaseAddon } from "../../dist-DqPyKwVB.mjs";
1
+ import { K as localNetworkCapability, pt as BaseAddon, zt as EventCategory } from "../../dist-CbS6uoSb.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-CIkGOAcz.js");
6
+ const require_dist = require("../../dist-NRYjb8qK.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 { G as logDestinationCapability, dt as BaseAddon } from "../../dist-DqPyKwVB.mjs";
1
+ import { pt as BaseAddon, q as logDestinationCapability } from "../../dist-CbS6uoSb.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-CIkGOAcz.js");
6
+ const require_dist = require("../../dist-NRYjb8qK.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_chunk.__toESM(node_fs);
9
9
  let node_util = require("node:util");
@@ -1,4 +1,4 @@
1
- import { J as metricsProviderCapability, Lt as EventCategory, dt as BaseAddon, wt as createEvent } from "../../dist-DqPyKwVB.mjs";
1
+ import { Et as createEvent, X as metricsProviderCapability, pt as BaseAddon, zt as EventCategory } from "../../dist-CbS6uoSb.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import { promisify } from "node:util";
4
4
  import { execFile, execFileSync } from "node:child_process";
@@ -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-CIkGOAcz.js");
6
+ const require_dist = require("../../dist-NRYjb8qK.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_chunk.__toESM(node_fs);
9
9
  let node_util = require("node:util");
@@ -1,5 +1,5 @@
1
1
  import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
2
- import { $ as platformProbeCapability, Et as emitReadiness, Lt as EventCategory, M as enumerateInferenceDevices, dt as BaseAddon, tt as scoreRuntimes, ut as errMsg } from "../../dist-DqPyKwVB.mjs";
2
+ import { Ot as emitReadiness, P as enumerateInferenceDevices, ft as errMsg, pt as BaseAddon, rt as scoreRuntimes, tt as platformProbeCapability, zt as EventCategory } from "../../dist-CbS6uoSb.mjs";
3
3
  import * as fs from "node:fs";
4
4
  import { promisify } from "node:util";
5
5
  import { execFile } from "node:child_process";
@@ -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-CIkGOAcz.js");
6
+ const require_dist = require("../../dist-NRYjb8qK.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 { Lt as EventCategory, dt as BaseAddon } from "../../dist-DqPyKwVB.mjs";
1
+ import { pt as BaseAddon, zt as EventCategory } from "../../dist-CbS6uoSb.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
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CIkGOAcz.js");
6
+ const require_dist = require("../../dist-NRYjb8qK.js");
7
7
  let node_child_process = require("node:child_process");
8
8
  //#region src/builtins/snapshot/snapshot-coalescing.ts
9
9
  /**