@camstack/system 1.2.90 → 1.2.92

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/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 +25 -1
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +25 -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/blocks-integration.d.ts +34 -1
  14. package/dist/builtins/core-blocks/core-block-store.d.ts +11 -0
  15. package/dist/builtins/core-blocks/core-blocks.addon.js +80 -22
  16. package/dist/builtins/core-blocks/core-blocks.addon.mjs +80 -22
  17. package/dist/builtins/device-manager/device-manager.addon.js +1 -1
  18. package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
  19. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  20. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  21. package/dist/builtins/hub-forwarder/index.js +1 -1
  22. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  23. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  24. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  25. package/dist/builtins/local-auth/auth-schema.d.ts +42 -0
  26. package/dist/builtins/local-auth/local-auth.addon.js +43 -1
  27. package/dist/builtins/local-auth/local-auth.addon.mjs +43 -1
  28. package/dist/builtins/local-network/local-network.addon.js +1 -1
  29. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  30. package/dist/builtins/loki-logging/index.js +1 -1
  31. package/dist/builtins/loki-logging/index.mjs +1 -1
  32. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  33. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  34. package/dist/builtins/platform-probe/index.js +1 -1
  35. package/dist/builtins/platform-probe/index.mjs +1 -1
  36. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  37. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  38. package/dist/builtins/snapshot/index.js +1 -1
  39. package/dist/builtins/snapshot/index.mjs +1 -1
  40. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  41. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  42. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +16 -5
  43. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +16 -5
  44. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +19 -2
  45. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +19 -2
  46. package/dist/builtins/storage-orchestrator/storage-orchestrator.service.d.ts +12 -0
  47. package/dist/builtins/system-config/system-config.addon.js +1 -1
  48. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  49. package/dist/builtins/winston-logging/index.js +1 -1
  50. package/dist/builtins/winston-logging/index.mjs +1 -1
  51. package/dist/{dist-CYqL8oZq.js → dist-BH2tZ9ZN.js} +525 -47
  52. package/dist/{dist-DMhxx5Kr.mjs → dist-DXDvrBi0.mjs} +525 -47
  53. package/dist/index.js +78 -6
  54. package/dist/index.mjs +78 -6
  55. package/dist/kernel/addon-health-monitor.d.ts +10 -1
  56. package/dist/logging/log-manager.d.ts +28 -0
  57. package/dist/{manifest-python-deps-C0zkl1Ae.js → manifest-python-deps-BZO_eMn-.js} +1 -7
  58. package/dist/{manifest-python-deps-fWzdEasI.mjs → manifest-python-deps-D3hP9W8D.mjs} +2 -8
  59. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- const require_manifest_python_deps = require("./manifest-python-deps-C0zkl1Ae.js");
2
+ const require_manifest_python_deps = require("./manifest-python-deps-BZO_eMn-.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-fWzdEasI.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-D3hP9W8D.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-CYqL8oZq.js");
6
+ const require_dist = require("../../dist-BH2tZ9ZN.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 { Ut as EventCategory, _t as BaseAddon, gt as errMsg, v as addonPagesCapability } from "../../dist-DMhxx5Kr.mjs";
1
+ import { Ut as EventCategory, _t as BaseAddon, gt as errMsg, v as addonPagesCapability } from "../../dist-DXDvrBi0.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-CYqL8oZq.js");
6
+ const require_dist = require("../../dist-BH2tZ9ZN.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 { Ut as EventCategory, _t as BaseAddon, b as addonWidgetsCapability, gt as errMsg } from "../../dist-DMhxx5Kr.mjs";
1
+ import { Ut as EventCategory, _t as BaseAddon, b as addonWidgetsCapability, gt as errMsg } from "../../dist-DXDvrBi0.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-CYqL8oZq.js");
6
+ const require_dist = require("../../dist-BH2tZ9ZN.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 { At as createEvent, Ut as EventCategory, _t as BaseAddon, gt as errMsg, n as AlertSchema, x as alertsCapability } from "../../dist-DMhxx5Kr.mjs";
1
+ import { At as createEvent, Ut as EventCategory, _t as BaseAddon, gt as errMsg, n as AlertSchema, x as alertsCapability } from "../../dist-DXDvrBi0.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-CYqL8oZq.js");
6
+ const require_dist = require("../../dist-BH2tZ9ZN.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");
@@ -4204,6 +4204,18 @@ function previewNextRuns(cron, count, after) {
4204
4204
  }
4205
4205
  //#endregion
4206
4206
  //#region src/builtins/backup-orchestrator/destination-policy.ts
4207
+ /**
4208
+ * @durable class=config owner=backup-orchestrator
4209
+ * write="`ensureDefault` auto-creates one row the first time a `backups` storage
4210
+ * location is listed (enabled, retentionCount 7, cron `0 3 * * *`), and an operator
4211
+ * edit rewrites it (`upsert` = delete + insert, carrying `last_run_at` forward so a
4212
+ * label change does not reset the schedule baseline)"
4213
+ * retention="none — `delete(locationId)` exists but NOTHING calls it, so a policy row
4214
+ * outlives the `StorageLocation` it names and the table only ever grows, by one row
4215
+ * per backups location ever seen. Losing a row is recoverable: the next
4216
+ * `listDestinations` pass re-creates it from the defaults, which silently discards
4217
+ * the operator's own retention count and cron."
4218
+ */
4207
4219
  var BACKUP_DESTINATION_POLICIES = "backup_destination_policies";
4208
4220
  var BACKUP_DESTINATION_POLICIES_COLUMNS = [
4209
4221
  {
@@ -4590,6 +4602,18 @@ var LocationManifestLock = class {
4590
4602
  };
4591
4603
  //#endregion
4592
4604
  //#region src/builtins/backup-orchestrator/schedule-store.ts
4605
+ /**
4606
+ * @durable class=config owner=backup-orchestrator
4607
+ * write="an operator creating or editing a schedule (`upsert`, implemented as
4608
+ * delete + insert: cron, enabled, the fan-out `location_ids`, `retention_count`,
4609
+ * optional `data_sources`), plus a `last_run_at` stamp the orchestrator writes on
4610
+ * every due tick BEFORE the archive runs, so an upload outliving the tick cannot
4611
+ * fire the same schedule twice"
4612
+ * retention="only the operator's `delete(id)` — no sweep, no expiry, and `retention_count`
4613
+ * bounds the ARCHIVES at the destination, never these rows. Losing the table stops
4614
+ * every scheduled backup silently: no cron remains to fire, and the fan-out sets and
4615
+ * retention counts are gone with it."
4616
+ */
4593
4617
  var BACKUP_SCHEDULES = "backup_schedules";
4594
4618
  var BACKUP_SCHEDULES_COLUMNS = [
4595
4619
  {
@@ -1,4 +1,4 @@
1
- import { C as backupCapability, Ut as EventCategory, _t as BaseAddon } from "../../dist-DMhxx5Kr.mjs";
1
+ import { C as backupCapability, Ut as EventCategory, _t as BaseAddon } from "../../dist-DXDvrBi0.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";
@@ -4189,6 +4189,18 @@ function previewNextRuns(cron, count, after) {
4189
4189
  }
4190
4190
  //#endregion
4191
4191
  //#region src/builtins/backup-orchestrator/destination-policy.ts
4192
+ /**
4193
+ * @durable class=config owner=backup-orchestrator
4194
+ * write="`ensureDefault` auto-creates one row the first time a `backups` storage
4195
+ * location is listed (enabled, retentionCount 7, cron `0 3 * * *`), and an operator
4196
+ * edit rewrites it (`upsert` = delete + insert, carrying `last_run_at` forward so a
4197
+ * label change does not reset the schedule baseline)"
4198
+ * retention="none — `delete(locationId)` exists but NOTHING calls it, so a policy row
4199
+ * outlives the `StorageLocation` it names and the table only ever grows, by one row
4200
+ * per backups location ever seen. Losing a row is recoverable: the next
4201
+ * `listDestinations` pass re-creates it from the defaults, which silently discards
4202
+ * the operator's own retention count and cron."
4203
+ */
4192
4204
  var BACKUP_DESTINATION_POLICIES = "backup_destination_policies";
4193
4205
  var BACKUP_DESTINATION_POLICIES_COLUMNS = [
4194
4206
  {
@@ -4575,6 +4587,18 @@ var LocationManifestLock = class {
4575
4587
  };
4576
4588
  //#endregion
4577
4589
  //#region src/builtins/backup-orchestrator/schedule-store.ts
4590
+ /**
4591
+ * @durable class=config owner=backup-orchestrator
4592
+ * write="an operator creating or editing a schedule (`upsert`, implemented as
4593
+ * delete + insert: cron, enabled, the fan-out `location_ids`, `retention_count`,
4594
+ * optional `data_sources`), plus a `last_run_at` stamp the orchestrator writes on
4595
+ * every due tick BEFORE the archive runs, so an upload outliving the tick cannot
4596
+ * fire the same schedule twice"
4597
+ * retention="only the operator's `delete(id)` — no sweep, no expiry, and `retention_count`
4598
+ * bounds the ARCHIVES at the destination, never these rows. Losing the table stops
4599
+ * every scheduled backup silently: no cron remains to fire, and the fan-out sets and
4600
+ * retention counts are gone with it."
4601
+ */
4578
4602
  var BACKUP_SCHEDULES = "backup_schedules";
4579
4603
  var BACKUP_SCHEDULES_COLUMNS = [
4580
4604
  {
@@ -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-CYqL8oZq.js");
6
+ const require_dist = require("../../dist-BH2tZ9ZN.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, _t as BaseAddon } from "../../dist-DMhxx5Kr.mjs";
1
+ import { J as logDestinationCapability, _t as BaseAddon } from "../../dist-DXDvrBi0.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,4 +1,4 @@
1
- import { DeclaredDevicePorts } from '@camstack/types';
1
+ import { DeclaredDevicePorts, IScopedLogger } from '@camstack/types';
2
2
  /** Operator-visible name of the shared integration. Seeded once, at create. */
3
3
  export declare const BLOCKS_INTEGRATION_NAME = "Blocks";
4
4
  /**
@@ -22,3 +22,36 @@ export type BlocksIntegrationPorts = Omit<DeclaredDevicePorts, 'addonId'>;
22
22
  * convenience; the spec test pins both.
23
23
  */
24
24
  export declare function reconcileBlocksIntegration(ports: BlocksIntegrationPorts): Promise<string | null>;
25
+ /** What {@link armBlocksIntegration} needs, and the seam its spec drives. */
26
+ export interface ArmBlocksIntegrationOptions {
27
+ readonly logger: IScopedLogger;
28
+ readonly reconcile: () => Promise<string | null>;
29
+ readonly onLanded: (integrationId: string | null) => void;
30
+ readonly retryDelayMs?: number;
31
+ readonly maxAttempts?: number;
32
+ }
33
+ /** A reconcile running in the background, and a handle to await in tests. */
34
+ export interface ArmedBlocksIntegration {
35
+ readonly settled: Promise<void>;
36
+ }
37
+ /**
38
+ * Arm the shared integration WITHOUT putting it on the boot path (D167).
39
+ *
40
+ * `integrations` is a hub-core namespace served by the `$core-caps` Moleculer
41
+ * service, and that service is registered only after `app.init()` returns —
42
+ * which is where the entire addon boot runs. During boot the read is therefore
43
+ * not slow, it is unanswerable: it burns the full 30 s discovery deadline and
44
+ * fast-fails, with no boot-window retry (core caps are excluded from it).
45
+ *
46
+ * Awaited inside `onInitialize()`, that 30 s sat on the hub's SERIAL in-process
47
+ * builtin loop and delayed `device-manager` — which every provider runner waits
48
+ * on before it may publish a single capability. Measured 2026-08-15: builtins
49
+ * stalled t0+10 s → t0+40 s, and the first camera's first keyframe landed at
50
+ * t0+75 s.
51
+ *
52
+ * So: arm it, let boot finish, and let the retry land it a few seconds later. A
53
+ * block that declares a device before it lands finds `null` and gets the
54
+ * integration on the next converge tick — which was already the documented
55
+ * behaviour when the API was unavailable.
56
+ */
57
+ export declare function armBlocksIntegration(options: ArmBlocksIntegrationOptions): ArmedBlocksIntegration;
@@ -1,4 +1,15 @@
1
1
  import { CoreBlock, CoreBlockInput, IScopedLogger, SettingsStoreClient } from '@camstack/types';
2
+ /**
3
+ * @durable class=config owner=core-blocks
4
+ * write="`create` and `update` persist the operator's block after it compiles;
5
+ * `setStatus` rewrites what the runner reported (status, `lastChangedAt`,
6
+ * `lastError`) — so a running block is written on every state change"
7
+ * retention="`delete` only, when the operator removes a block. Nothing ages out,
8
+ * and a row whose JSON stops validating is left on disk rather than repaired.
9
+ * The real loss path is a `declareCollection` REBUILD — a legacy (id, data)
10
+ * shape, or a new PK/UNIQUE/NOT-NULL-without-default column — which drops the
11
+ * table and takes the operator's source code with it."
12
+ */
2
13
  export declare const CORE_BLOCKS_COLLECTION = "core_blocks";
3
14
  export declare const CORE_BLOCKS_COLUMNS: readonly [{
4
15
  readonly name: "id";
@@ -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-CYqL8oZq.js");
3
+ const require_dist = require("../../dist-BH2tZ9ZN.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);
@@ -495,6 +495,17 @@ async function compileBlock(transpiler, code) {
495
495
  * is a fork failure the operator meets minutes later inside a log — so the
496
496
  * compile happens where they are still looking at the editor.
497
497
  */
498
+ /**
499
+ * @durable class=config owner=core-blocks
500
+ * write="`create` and `update` persist the operator's block after it compiles;
501
+ * `setStatus` rewrites what the runner reported (status, `lastChangedAt`,
502
+ * `lastError`) — so a running block is written on every state change"
503
+ * retention="`delete` only, when the operator removes a block. Nothing ages out,
504
+ * and a row whose JSON stops validating is left on disk rather than repaired.
505
+ * The real loss path is a `declareCollection` REBUILD — a legacy (id, data)
506
+ * shape, or a new PK/UNIQUE/NOT-NULL-without-default column — which drops the
507
+ * table and takes the operator's source code with it."
508
+ */
498
509
  var CORE_BLOCKS_COLLECTION = "core_blocks";
499
510
  var CORE_BLOCKS_COLUMNS = [
500
511
  {
@@ -721,6 +732,52 @@ async function reconcileBlocksIntegration(ports) {
721
732
  devices: []
722
733
  })).integrationId;
723
734
  }
735
+ /** Retry cadence for {@link armBlocksIntegration}. The gap it waits out is the
736
+ * rest of hub boot — `$core-caps` is registered once `app.init()` returns — so
737
+ * seconds, and a handful of them, is the right shape. */
738
+ var ARM_RETRY_DELAY_MS = 5e3;
739
+ /** Bounded, because an integration that will never reconcile must say so once
740
+ * rather than log forever. Roughly two minutes at the default cadence. */
741
+ var ARM_MAX_ATTEMPTS = 24;
742
+ /**
743
+ * Arm the shared integration WITHOUT putting it on the boot path (D167).
744
+ *
745
+ * `integrations` is a hub-core namespace served by the `$core-caps` Moleculer
746
+ * service, and that service is registered only after `app.init()` returns —
747
+ * which is where the entire addon boot runs. During boot the read is therefore
748
+ * not slow, it is unanswerable: it burns the full 30 s discovery deadline and
749
+ * fast-fails, with no boot-window retry (core caps are excluded from it).
750
+ *
751
+ * Awaited inside `onInitialize()`, that 30 s sat on the hub's SERIAL in-process
752
+ * builtin loop and delayed `device-manager` — which every provider runner waits
753
+ * on before it may publish a single capability. Measured 2026-08-15: builtins
754
+ * stalled t0+10 s → t0+40 s, and the first camera's first keyframe landed at
755
+ * t0+75 s.
756
+ *
757
+ * So: arm it, let boot finish, and let the retry land it a few seconds later. A
758
+ * block that declares a device before it lands finds `null` and gets the
759
+ * integration on the next converge tick — which was already the documented
760
+ * behaviour when the API was unavailable.
761
+ */
762
+ function armBlocksIntegration(options) {
763
+ const retryDelayMs = options.retryDelayMs ?? ARM_RETRY_DELAY_MS;
764
+ const maxAttempts = options.maxAttempts ?? ARM_MAX_ATTEMPTS;
765
+ return { settled: (async () => {
766
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
767
+ options.onLanded(await options.reconcile());
768
+ return;
769
+ } catch (err) {
770
+ if (attempt === maxAttempts) {
771
+ options.logger.warn("core-blocks: the shared integration never reconciled — a block that declares a device has nowhere to hang it", { meta: {
772
+ attempts: attempt,
773
+ error: require_dist.errMsg(err)
774
+ } });
775
+ return;
776
+ }
777
+ await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
778
+ }
779
+ })() };
780
+ }
724
781
  //#endregion
725
782
  //#region src/builtins/core-blocks/core-blocks.addon.ts
726
783
  /**
@@ -777,7 +834,13 @@ var CoreBlocksAddon = class extends require_dist.BaseAddon {
777
834
  });
778
835
  await this.store.load();
779
836
  this.transpiler = await loadTranspiler(this.ctx.logger);
780
- this.blocksIntegrationId = await this.declareBlocksIntegration();
837
+ armBlocksIntegration({
838
+ logger: this.ctx.logger,
839
+ reconcile: () => this.declareBlocksIntegration(),
840
+ onLanded: (integrationId) => {
841
+ this.blocksIntegrationId = integrationId;
842
+ }
843
+ });
781
844
  this.supervisor = this.buildSupervisor();
782
845
  this.supervisor?.start(CONVERGE_INTERVAL_MS);
783
846
  this.ctx.logger.info("core-blocks ready", { meta: {
@@ -857,26 +920,21 @@ var CoreBlocksAddon = class extends require_dist.BaseAddon {
857
920
  this.ctx.logger.warn("core-blocks: no integrations API — a block that declares a device will have nowhere to hang it");
858
921
  return null;
859
922
  }
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
- }
923
+ return reconcileBlocksIntegration({
924
+ logger: this.ctx.logger.child("integration"),
925
+ devices: this.ctx.kernel.devices,
926
+ localNodeId: this.ctx.kernel.localNodeId,
927
+ getIntegration: async (addonId) => api.integrations.getByAddonId.query({ addonId }),
928
+ createIntegration: async (input) => api.integrations.create.mutate(input),
929
+ updateIntegration: async ({ id, info }) => {
930
+ await api.integrations.update.mutate({
931
+ id,
932
+ info,
933
+ skipRestart: true
934
+ });
935
+ },
936
+ listOwnDevices: async () => api.deviceManager.listAll.query({ addonId: require_dist.CORE_BLOCKS_ADDON_ID })
937
+ });
880
938
  }
881
939
  /**
882
940
  * Reconcile now, after a write.
@@ -1,4 +1,4 @@
1
- import { D as coreBlockIdFromAddonId, E as coreBlockAddonId, O as coreBlocksCapability, _t as BaseAddon, c as CoreBlockSchema, l as DeclaredDevices, o as CORE_BLOCKS_ADDON_ID, s as CORE_BLOCK_ADDON_PREFIX } from "../../dist-DMhxx5Kr.mjs";
1
+ import { D as coreBlockIdFromAddonId, E as coreBlockAddonId, O as coreBlocksCapability, _t as BaseAddon, c as CoreBlockSchema, gt as errMsg, l as DeclaredDevices, o as CORE_BLOCKS_ADDON_ID, s as CORE_BLOCK_ADDON_PREFIX } from "../../dist-DXDvrBi0.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";
@@ -491,6 +491,17 @@ async function compileBlock(transpiler, code) {
491
491
  * is a fork failure the operator meets minutes later inside a log — so the
492
492
  * compile happens where they are still looking at the editor.
493
493
  */
494
+ /**
495
+ * @durable class=config owner=core-blocks
496
+ * write="`create` and `update` persist the operator's block after it compiles;
497
+ * `setStatus` rewrites what the runner reported (status, `lastChangedAt`,
498
+ * `lastError`) — so a running block is written on every state change"
499
+ * retention="`delete` only, when the operator removes a block. Nothing ages out,
500
+ * and a row whose JSON stops validating is left on disk rather than repaired.
501
+ * The real loss path is a `declareCollection` REBUILD — a legacy (id, data)
502
+ * shape, or a new PK/UNIQUE/NOT-NULL-without-default column — which drops the
503
+ * table and takes the operator's source code with it."
504
+ */
494
505
  var CORE_BLOCKS_COLLECTION = "core_blocks";
495
506
  var CORE_BLOCKS_COLUMNS = [
496
507
  {
@@ -717,6 +728,52 @@ async function reconcileBlocksIntegration(ports) {
717
728
  devices: []
718
729
  })).integrationId;
719
730
  }
731
+ /** Retry cadence for {@link armBlocksIntegration}. The gap it waits out is the
732
+ * rest of hub boot — `$core-caps` is registered once `app.init()` returns — so
733
+ * seconds, and a handful of them, is the right shape. */
734
+ var ARM_RETRY_DELAY_MS = 5e3;
735
+ /** Bounded, because an integration that will never reconcile must say so once
736
+ * rather than log forever. Roughly two minutes at the default cadence. */
737
+ var ARM_MAX_ATTEMPTS = 24;
738
+ /**
739
+ * Arm the shared integration WITHOUT putting it on the boot path (D167).
740
+ *
741
+ * `integrations` is a hub-core namespace served by the `$core-caps` Moleculer
742
+ * service, and that service is registered only after `app.init()` returns —
743
+ * which is where the entire addon boot runs. During boot the read is therefore
744
+ * not slow, it is unanswerable: it burns the full 30 s discovery deadline and
745
+ * fast-fails, with no boot-window retry (core caps are excluded from it).
746
+ *
747
+ * Awaited inside `onInitialize()`, that 30 s sat on the hub's SERIAL in-process
748
+ * builtin loop and delayed `device-manager` — which every provider runner waits
749
+ * on before it may publish a single capability. Measured 2026-08-15: builtins
750
+ * stalled t0+10 s → t0+40 s, and the first camera's first keyframe landed at
751
+ * t0+75 s.
752
+ *
753
+ * So: arm it, let boot finish, and let the retry land it a few seconds later. A
754
+ * block that declares a device before it lands finds `null` and gets the
755
+ * integration on the next converge tick — which was already the documented
756
+ * behaviour when the API was unavailable.
757
+ */
758
+ function armBlocksIntegration(options) {
759
+ const retryDelayMs = options.retryDelayMs ?? ARM_RETRY_DELAY_MS;
760
+ const maxAttempts = options.maxAttempts ?? ARM_MAX_ATTEMPTS;
761
+ return { settled: (async () => {
762
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
763
+ options.onLanded(await options.reconcile());
764
+ return;
765
+ } catch (err) {
766
+ if (attempt === maxAttempts) {
767
+ options.logger.warn("core-blocks: the shared integration never reconciled — a block that declares a device has nowhere to hang it", { meta: {
768
+ attempts: attempt,
769
+ error: errMsg(err)
770
+ } });
771
+ return;
772
+ }
773
+ await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
774
+ }
775
+ })() };
776
+ }
720
777
  //#endregion
721
778
  //#region src/builtins/core-blocks/core-blocks.addon.ts
722
779
  /**
@@ -773,7 +830,13 @@ var CoreBlocksAddon = class extends BaseAddon {
773
830
  });
774
831
  await this.store.load();
775
832
  this.transpiler = await loadTranspiler(this.ctx.logger);
776
- this.blocksIntegrationId = await this.declareBlocksIntegration();
833
+ armBlocksIntegration({
834
+ logger: this.ctx.logger,
835
+ reconcile: () => this.declareBlocksIntegration(),
836
+ onLanded: (integrationId) => {
837
+ this.blocksIntegrationId = integrationId;
838
+ }
839
+ });
777
840
  this.supervisor = this.buildSupervisor();
778
841
  this.supervisor?.start(CONVERGE_INTERVAL_MS);
779
842
  this.ctx.logger.info("core-blocks ready", { meta: {
@@ -853,26 +916,21 @@ var CoreBlocksAddon = class extends BaseAddon {
853
916
  this.ctx.logger.warn("core-blocks: no integrations API — a block that declares a device will have nowhere to hang it");
854
917
  return null;
855
918
  }
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
- }
919
+ return reconcileBlocksIntegration({
920
+ logger: this.ctx.logger.child("integration"),
921
+ devices: this.ctx.kernel.devices,
922
+ localNodeId: this.ctx.kernel.localNodeId,
923
+ getIntegration: async (addonId) => api.integrations.getByAddonId.query({ addonId }),
924
+ createIntegration: async (input) => api.integrations.create.mutate(input),
925
+ updateIntegration: async ({ id, info }) => {
926
+ await api.integrations.update.mutate({
927
+ id,
928
+ info,
929
+ skipRestart: true
930
+ });
931
+ },
932
+ listOwnDevices: async () => api.deviceManager.listAll.query({ addonId: CORE_BLOCKS_ADDON_ID })
933
+ });
876
934
  }
877
935
  /**
878
936
  * Reconcile now, after a write.
@@ -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-CYqL8oZq.js");
6
+ const require_dist = require("../../dist-BH2tZ9ZN.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
@@ -1,4 +1,4 @@
1
- import { Ct as DeviceType, Et as WELL_KNOWN_TAB_MAP, Ft as isDeviceConfigCap, Ht as sleep, I as enumerateItemArrayFields, L as enumerateSchemaFields, M as deviceStateCapability, N as deviceStatusCapability, Q as normalizeUnit, St as DeviceRole, T as buildStreamParamsConfigSchema, Ut as EventCategory, _t as BaseAddon, a as CAP_NAMES_WITH_STATUS, et as parseStreamParamsFormPatch, gt as errMsg, j as deviceManagerCapability, p as STREAM_PROFILE_META, rt as runtimeStatePolicyFor, t as ALL_CAPABILITY_DEFINITIONS, u as DeviceStatusSchema, xt as DeviceFeature } from "../../dist-DMhxx5Kr.mjs";
1
+ import { Ct as DeviceType, Et as WELL_KNOWN_TAB_MAP, Ft as isDeviceConfigCap, Ht as sleep, I as enumerateItemArrayFields, L as enumerateSchemaFields, M as deviceStateCapability, N as deviceStatusCapability, Q as normalizeUnit, St as DeviceRole, T as buildStreamParamsConfigSchema, Ut as EventCategory, _t as BaseAddon, a as CAP_NAMES_WITH_STATUS, et as parseStreamParamsFormPatch, gt as errMsg, j as deviceManagerCapability, p as STREAM_PROFILE_META, rt as runtimeStatePolicyFor, t as ALL_CAPABILITY_DEFINITIONS, u as DeviceStatusSchema, xt as DeviceFeature } from "../../dist-DXDvrBi0.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
@@ -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-CYqL8oZq.js");
6
+ const require_dist = require("../../dist-BH2tZ9ZN.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 { At as createEvent, Ct as DeviceType, P as doorbellCapability, U as isSameAddonId, Ut as EventCategory, _t as BaseAddon, gt as errMsg, w as bareAddonId } from "../../dist-DMhxx5Kr.mjs";
1
+ import { At as createEvent, Ct as DeviceType, P as doorbellCapability, U as isSameAddonId, Ut as EventCategory, _t as BaseAddon, gt as errMsg, w as bareAddonId } from "../../dist-DXDvrBi0.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-CYqL8oZq.js");
6
+ const require_dist = require("../../dist-BH2tZ9ZN.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, _t as BaseAddon } from "../../dist-DMhxx5Kr.mjs";
1
+ import { J as logDestinationCapability, _t as BaseAddon } from "../../dist-DXDvrBi0.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-CYqL8oZq.js");
3
+ const require_dist = require("../../dist-BH2tZ9ZN.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 { At as createEvent, Ut as EventCategory, _t as BaseAddon, gt as errMsg } from "../../dist-DMhxx5Kr.mjs";
1
+ import { At as createEvent, Ut as EventCategory, _t as BaseAddon, gt as errMsg } from "../../dist-DXDvrBi0.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";
@@ -1,7 +1,42 @@
1
1
  import { SettingsStoreClient } from '@camstack/types';
2
+ /**
3
+ * @durable class=config owner=local-auth
4
+ * write="an operator creates a user, or `ensureAdminExists` seeds/re-syncs the
5
+ * configured admin at boot; updated on scope, isAdmin and password edits"
6
+ * retention="none — a row goes only when an operator deletes the user. A
7
+ * `declareCollection` that adds a PK/UNIQUE/NOT-NULL-without-default column
8
+ * REBUILDS the table: every account is wiped and only the configured admin
9
+ * comes back on the next boot."
10
+ */
2
11
  export declare const USERS_COLLECTION = "users";
12
+ /**
13
+ * @durable class=config owner=local-auth
14
+ * write="an operator mints an API key; `validateToken` rewrites `lastUsedAt` on
15
+ * every request the key authorises"
16
+ * retention="none — an explicit `revoke` is the only delete. API keys have no
17
+ * expiry column and no sweep, so a key lives until someone removes it (or the
18
+ * destructive `declareCollection` rebuild takes the table)."
19
+ */
3
20
  export declare const API_KEYS_COLLECTION = "api_keys";
21
+ /**
22
+ * @durable class=config owner=local-auth
23
+ * write="an operator or the CLI mints a scoped token; `validate` fires
24
+ * `updateLastUsed` on every accepted call"
25
+ * retention="`revoke` deletes one. The daily sweep `reapExpired` drops rows past
26
+ * `expiresAt` — a NULL expiry is a deliberate never-expires token and BETWEEN
27
+ * never matches it. `cleanupOrphans` drops tokens owned by the legacy 'system'
28
+ * id or by a user that no longer exists."
29
+ */
4
30
  export declare const SCOPED_TOKENS_COLLECTION = "scoped_tokens";
31
+ /**
32
+ * @durable class=ledger owner=local-auth
33
+ * write="one row per redeemed OAuth code — every completed account link through
34
+ * `oauthExchangeCode`; `touch` rewrites `lastUsedAt` on each refresh"
35
+ * retention="ONLY revoked rows are pruned: the daily sweep deletes sessions
36
+ * revoked more than OAUTH_REVOKED_RETENTION_MS (30 d) ago. An ACTIVE session
37
+ * has `revokedAt` NULL, never expires and is never swept — the table grows one
38
+ * row per login until an operator revokes the link."
39
+ */
5
40
  export declare const OAUTH_SESSIONS_COLLECTION = "oauth_sessions";
6
41
  /**
7
42
  * TOTP enrollment table — split off from `users` to avoid the
@@ -15,6 +50,13 @@ export declare const OAUTH_SESSIONS_COLLECTION = "oauth_sessions";
15
50
  *
16
51
  * "Enabled" means `confirmedAt != null`. A half-enrolled row (`confirmedAt
17
52
  * = null`) is INACTIVE — `verifyTotp` returns false until confirmation.
53
+ *
54
+ * @durable class=config owner=local-auth
55
+ * write="`setup` inserts a fresh secret for the user (deleting any pending row
56
+ * first); `confirm` flips `confirmedAt` once a valid code proves possession"
57
+ * retention="`disable` deletes the row, and `setup` deletes before re-inserting.
58
+ * Nothing else prunes — deleting a USER does not cascade here, so their
59
+ * enrollment row outlives the account."
18
60
  */
19
61
  export declare const USER_TOTP_COLLECTION = "user_totp";
20
62
  /**