@camstack/system 1.2.443 → 1.2.445

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 (160) hide show
  1. package/assets/record-entries/icon.svg +8 -0
  2. package/dist/addon-runner.js +81 -42
  3. package/dist/addon-runner.mjs +82 -43
  4. package/dist/{agent-https-DlpTMDqA.js → agent-https-CiOW0-sj.js} +810 -12
  5. package/dist/{agent-https-U0d4uvn2.mjs → agent-https-D4Q8iFNd.mjs} +697 -13
  6. package/dist/auth/admin-token-manager.d.ts +9 -3
  7. package/dist/auth/api-key-manager.d.ts +9 -3
  8. package/dist/auth/auth-audit-log.d.ts +9 -3
  9. package/dist/auth/device-credential-manager.d.ts +5 -1
  10. package/dist/auth/external-identity-manager.d.ts +8 -21
  11. package/dist/auth/login-session-manager.d.ts +11 -3
  12. package/dist/auth/scoped-token-manager.d.ts +8 -52
  13. package/dist/auth/totp-manager.d.ts +11 -3
  14. package/dist/auth/user-manager.d.ts +10 -17
  15. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  16. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  17. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  18. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  19. package/dist/builtins/alerts/alert-store.d.ts +32 -0
  20. package/dist/builtins/alerts/alerts.addon.d.ts +19 -4
  21. package/dist/builtins/alerts/alerts.addon.js +331 -159
  22. package/dist/builtins/alerts/alerts.addon.mjs +330 -159
  23. package/dist/builtins/alerts/record-kinds.d.ts +15 -0
  24. package/dist/builtins/autotrack/index.js +1 -1
  25. package/dist/builtins/autotrack/index.mjs +1 -1
  26. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.d.ts +1 -0
  27. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +134 -193
  28. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +134 -193
  29. package/dist/builtins/backup-orchestrator/destination-policy.d.ts +4 -2
  30. package/dist/builtins/backup-orchestrator/encryption-key-store.d.ts +4 -2
  31. package/dist/builtins/backup-orchestrator/record-kinds.d.ts +58 -0
  32. package/dist/builtins/backup-orchestrator/schedule-store.d.ts +4 -2
  33. package/dist/builtins/backup-orchestrator/seed-marker-store.d.ts +4 -2
  34. package/dist/builtins/calendars/calendar-prefs-store.d.ts +13 -5
  35. package/dist/builtins/calendars/calendars.addon.d.ts +2 -2
  36. package/dist/builtins/calendars/calendars.addon.js +67 -64
  37. package/dist/builtins/calendars/calendars.addon.mjs +59 -64
  38. package/dist/builtins/calendars/record-kinds.d.ts +18 -0
  39. package/dist/builtins/camera-grid/index.js +1 -1
  40. package/dist/builtins/camera-grid/index.mjs +1 -1
  41. package/dist/builtins/cluster-agent/record-kinds.d.ts +36 -0
  42. package/dist/builtins/composer/composer.addon.js +2 -2
  43. package/dist/builtins/composer/composer.addon.mjs +2 -2
  44. package/dist/builtins/console-logging/index.js +1 -1
  45. package/dist/builtins/console-logging/index.mjs +1 -1
  46. package/dist/builtins/core-blocks/block-settings-store.d.ts +8 -4
  47. package/dist/builtins/core-blocks/core-block-store.d.ts +8 -16
  48. package/dist/builtins/core-blocks/core-blocks.addon.js +85 -112
  49. package/dist/builtins/core-blocks/core-blocks.addon.mjs +86 -113
  50. package/dist/builtins/core-blocks/index.mjs +1 -1
  51. package/dist/builtins/core-blocks/record-kinds.d.ts +26 -0
  52. package/dist/builtins/device-manager/device-manager.addon.js +111 -187
  53. package/dist/builtins/device-manager/device-manager.addon.mjs +110 -187
  54. package/dist/builtins/device-manager/device-tombstone-store.d.ts +9 -48
  55. package/dist/builtins/device-manager/record-kinds.d.ts +14 -0
  56. package/dist/builtins/hub-forwarder/index.js +1 -1
  57. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  58. package/dist/builtins/keychain/index.js +152 -22
  59. package/dist/builtins/keychain/index.mjs +152 -22
  60. package/dist/builtins/keychain/keychain-service.d.ts +3 -2
  61. package/dist/builtins/keychain/keychain-store.d.ts +13 -2
  62. package/dist/builtins/keychain/record-kinds.d.ts +16 -0
  63. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  64. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  65. package/dist/builtins/local-auth/auth-schema.d.ts +3 -1
  66. package/dist/builtins/local-auth/local-auth.addon.js +2903 -2123
  67. package/dist/builtins/local-auth/local-auth.addon.mjs +2904 -2124
  68. package/dist/builtins/local-auth/oauth-session-manager.d.ts +8 -48
  69. package/dist/builtins/local-auth/record-kinds.d.ts +141 -0
  70. package/dist/builtins/local-network/local-network.addon.js +2 -2
  71. package/dist/builtins/local-network/local-network.addon.mjs +2 -2
  72. package/dist/builtins/loki-logging/index.js +1 -1
  73. package/dist/builtins/loki-logging/index.mjs +1 -1
  74. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  75. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  76. package/dist/builtins/platform-probe/index.js +1 -1
  77. package/dist/builtins/platform-probe/index.mjs +1 -1
  78. package/dist/builtins/record-entries/entries-service.d.ts +42 -0
  79. package/dist/builtins/record-entries/index.d.ts +1 -0
  80. package/dist/builtins/record-entries/index.js +142 -0
  81. package/dist/builtins/record-entries/index.mjs +136 -0
  82. package/dist/builtins/record-entries/record-entries.addon.d.ts +13 -0
  83. package/dist/builtins/records/legacy-table-row-count.d.ts +5 -0
  84. package/dist/builtins/records/phase6-declare-gate.d.ts +28 -0
  85. package/dist/builtins/records-isolated.d.ts +10 -0
  86. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  87. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  88. package/dist/builtins/snapshot/index.js +1 -1
  89. package/dist/builtins/snapshot/index.mjs +1 -1
  90. package/dist/builtins/sqlite-storage/file-host-client.d.ts +2 -0
  91. package/dist/builtins/sqlite-storage/file-host-handlers.d.ts +4 -0
  92. package/dist/builtins/sqlite-storage/file-host-protocol.d.ts +7 -1
  93. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  94. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  95. package/dist/builtins/sqlite-storage/index.js +1 -1
  96. package/dist/builtins/sqlite-storage/index.mjs +1 -1
  97. package/dist/builtins/sqlite-storage/integration-record-kinds.d.ts +30 -0
  98. package/dist/builtins/sqlite-storage/integration-registry.d.ts +10 -16
  99. package/dist/builtins/sqlite-storage/owned-sqlite-file.d.ts +7 -0
  100. package/dist/builtins/sqlite-storage/placement-router.d.ts +5 -0
  101. package/dist/builtins/sqlite-storage/records/count-settings-array-legacy.d.ts +5 -0
  102. package/dist/builtins/sqlite-storage/records/count-settings-brokers-legacy.d.ts +6 -0
  103. package/dist/builtins/sqlite-storage/records/records-catalog.d.ts +29 -0
  104. package/dist/builtins/sqlite-storage/records/records-engine-types.d.ts +51 -0
  105. package/dist/builtins/sqlite-storage/records/records-engine.d.ts +94 -0
  106. package/dist/builtins/sqlite-storage/records/records-explain.d.ts +18 -0
  107. package/dist/builtins/sqlite-storage/records/records-plan.d.ts +90 -0
  108. package/dist/builtins/sqlite-storage/records/records-probe.d.ts +33 -0
  109. package/dist/builtins/sqlite-storage/records/records-provider.d.ts +63 -0
  110. package/dist/builtins/sqlite-storage/records/records-rows.d.ts +46 -0
  111. package/dist/builtins/sqlite-storage/records/records-sql.d.ts +41 -0
  112. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +17 -10
  113. package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.ts +8 -0
  114. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +446 -5
  115. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +446 -5
  116. package/dist/builtins/sqlite-storage/storage-admin-provider.d.ts +15 -1
  117. package/dist/builtins/storage-orchestrator/drain-state-store.d.ts +5 -2
  118. package/dist/builtins/storage-orchestrator/location-store.d.ts +7 -2
  119. package/dist/builtins/storage-orchestrator/record-kinds.d.ts +38 -0
  120. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +2 -0
  121. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +196 -5
  122. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +195 -6
  123. package/dist/builtins/system-config/record-kinds.d.ts +32 -0
  124. package/dist/builtins/system-config/system-config.addon.js +53 -618
  125. package/dist/builtins/system-config/system-config.addon.mjs +29 -577
  126. package/dist/builtins/winston-logging/index.js +1 -1
  127. package/dist/builtins/winston-logging/index.mjs +1 -1
  128. package/dist/{composition-sources-C8tUG2uL.mjs → composition-sources-BMt5cFYb.mjs} +1 -1
  129. package/dist/{composition-sources-BUXX1e8j.js → composition-sources-D3fi9Xns.js} +1 -1
  130. package/dist/{constants-CGwVaAV3.mjs → constants-BMHCxRzb.mjs} +1 -1
  131. package/dist/{constants-DWmm7_of.js → constants-Br879Axi.js} +1 -1
  132. package/dist/{device-ca-DGayRmC0.js → device-ca-CnJ_va7i.js} +1 -1
  133. package/dist/{device-ca-DBfiJ_SB.mjs → device-ca-T8osSMos.mjs} +1 -1
  134. package/dist/{dist-Bxt_S-9a.mjs → dist-BNunRyXT.mjs} +1133 -93
  135. package/dist/{dist-D9qT3jHr.js → dist-DgOG8flk.js} +1354 -92
  136. package/dist/file-host-worker.js +11 -1
  137. package/dist/file-host-worker.mjs +11 -1
  138. package/dist/{hub-mdns-ciao-B-wk9nzx.js → hub-mdns-ciao-BIkTHq-n.js} +2 -2
  139. package/dist/{hub-mdns-ciao-DpPcQDg-.mjs → hub-mdns-ciao-D16oRQ9K.mjs} +2 -2
  140. package/dist/index.d.ts +7 -0
  141. package/dist/index.js +3111 -2806
  142. package/dist/index.mjs +3083 -2795
  143. package/dist/kernel/index.d.ts +2 -0
  144. package/dist/kernel/moleculer/event-bus.d.ts +3 -1
  145. package/dist/kernel/moleculer/mesh-mtls/mesh-certificate-registry.d.ts +5 -1
  146. package/dist/kernel/moleculer/records-api-transport.d.ts +9 -0
  147. package/dist/kernel/moleculer/remote-logger.d.ts +2 -2
  148. package/dist/kernel/settings-view-types.d.ts +0 -7
  149. package/dist/kernel/transport/cap-route-resolver.d.ts +9 -38
  150. package/dist/phase6-declare-gate-Ci1-8uUn.js +98 -0
  151. package/dist/phase6-declare-gate-tkKOEitw.mjs +93 -0
  152. package/dist/read-all-rows-CVoO1ONM.js +29 -0
  153. package/dist/read-all-rows-TaPqdWNF.mjs +25 -0
  154. package/dist/records/read-all-rows.d.ts +6 -0
  155. package/dist/{restore-archive-9CGgeBzD.mjs → restore-archive-DR9MtO7J.mjs} +339 -9
  156. package/dist/{restore-archive-BMNXwWWD.js → restore-archive-DjJnCgap.js} +353 -5
  157. package/dist/tls/agent-https-names.d.ts +9 -13
  158. package/dist/{wal-maintenance-cRHR4rUY.js → wal-maintenance-CODGqhKY.js} +0 -0
  159. package/dist/{wal-maintenance-sIB32ONp.mjs → wal-maintenance-CfvgtQ7C.mjs} +0 -0
  160. package/package.json +22 -1
@@ -1,4 +1,6 @@
1
- import { Ar as errMsg, Ei as EventCategory, Jr as createEvent, i as AlertSchema, jr as BaseAddon, st as alertsCapability } from "../../dist-Bxt_S-9a.mjs";
1
+ import { Ci as createEvent, Di as defineRecordKinds, Lr as errMsg, Rr as BaseAddon, ft as alertsCapability, i as AlertSchema, la as EventCategory } from "../../dist-BNunRyXT.mjs";
2
+ import { t as require_read_all_rows } from "../../read-all-rows-TaPqdWNF.mjs";
3
+ import { t as resolvePhase6UseRecordsPathForHub } from "../../phase6-declare-gate-tkKOEitw.mjs";
2
4
  //#region src/builtins/alerts/critical-push.ts
3
5
  /** Alert categories that are critical system findings. */
4
6
  var CRITICAL_CATEGORIES = new Set([
@@ -277,6 +279,210 @@ function runnerPushLine(record) {
277
279
  return record.state === "refused" ? `${where}: refused to start. ${record.remedy ?? "See the Addons page."}` : `${where}: stopped after repeated crashes. Restart it from the Addons page.`;
278
280
  }
279
281
  //#endregion
282
+ //#region src/builtins/alerts/record-kinds.ts
283
+ var import_read_all_rows = require_read_all_rows();
284
+ /**
285
+ * Record kinds owned by alert-center (records plan phase 4).
286
+ */
287
+ /** Legacy collection / adoptsFrom target (canonical KV before phase 4). */
288
+ var ALERTS_COLLECTION = "alerts";
289
+ var _alertCenterRecordKinds = defineRecordKinds({ alerts: {
290
+ schema: AlertSchema.omit({ id: true }),
291
+ class: "audit",
292
+ writeTrigger: "EventBus categories the alert center tracks, operator mark read/dismiss, and model-download progress merges",
293
+ device: "none",
294
+ at: "none",
295
+ indexes: [
296
+ {
297
+ name: "read",
298
+ fields: ["read"]
299
+ },
300
+ {
301
+ name: "created_at",
302
+ fields: ["createdAt"]
303
+ },
304
+ {
305
+ name: "updated_at",
306
+ fields: ["updatedAt"]
307
+ }
308
+ ],
309
+ retention: {
310
+ owner: "alert-center",
311
+ reason: "maxAlerts cap and retentionDays enforced on write (enforceMaxAlerts / enforceRetention); read alerts preferred when trimming overflow"
312
+ },
313
+ adoptsFrom: ALERTS_COLLECTION
314
+ } });
315
+ var alertCenterRecordKinds = _alertCenterRecordKinds;
316
+ /** Narrow kind for `records.kind()` — `IsolatedRecordKinds` string index is `| undefined`. */
317
+ var alertsRecordKind = _alertCenterRecordKinds.alerts;
318
+ //#endregion
319
+ //#region src/builtins/alerts/alert-store.ts
320
+ function toRecord(row) {
321
+ return {
322
+ id: row.id,
323
+ data: row.data
324
+ };
325
+ }
326
+ function toRecordFromLegacy(row) {
327
+ return {
328
+ id: row.id,
329
+ data: row.data
330
+ };
331
+ }
332
+ function isRecord(value) {
333
+ return typeof value === "object" && value !== null && !Array.isArray(value);
334
+ }
335
+ function legacyGetData(value) {
336
+ if (!isRecord(value)) return null;
337
+ if ("data" in value) {
338
+ const data = value.data;
339
+ return isRecord(data) ? data : null;
340
+ }
341
+ return value;
342
+ }
343
+ var AlertStore = class AlertStore {
344
+ kind;
345
+ legacyStore;
346
+ constructor(kind, legacyStore) {
347
+ this.kind = kind;
348
+ this.legacyStore = legacyStore;
349
+ }
350
+ static of(records) {
351
+ return new AlertStore(records.kind(alertsRecordKind), null);
352
+ }
353
+ static legacy(store) {
354
+ return new AlertStore(null, store);
355
+ }
356
+ async legacyRows() {
357
+ const store = this.legacyStore;
358
+ if (store === null) return [];
359
+ return (await store.query.query({
360
+ collection: ALERTS_COLLECTION,
361
+ filter: {}
362
+ })).map((row) => toRecordFromLegacy({
363
+ id: row.id,
364
+ data: row.data
365
+ }));
366
+ }
367
+ async list(filter) {
368
+ let out;
369
+ const kind = this.kind;
370
+ if (kind !== null) out = (await (0, import_read_all_rows.readAllRows)(kind, { order: "id" })).map(toRecord);
371
+ else out = [...await this.legacyRows()];
372
+ if (filter?.unreadOnly === true) out = out.filter((r) => r.data["read"] === false);
373
+ out.sort((a, b) => Number(b.data["updatedAt"] ?? 0) - Number(a.data["updatedAt"] ?? 0));
374
+ const limit = filter?.limit ?? 100;
375
+ return out.slice(0, limit);
376
+ }
377
+ async countUnread() {
378
+ return (await this.list({
379
+ unreadOnly: true,
380
+ limit: 1e4
381
+ })).length;
382
+ }
383
+ async countAll() {
384
+ const kind = this.kind;
385
+ if (kind !== null) return (await (0, import_read_all_rows.readAllRows)(kind)).length;
386
+ return (await this.legacyRows()).length;
387
+ }
388
+ async getData(id) {
389
+ const kind = this.kind;
390
+ if (kind !== null) {
391
+ const row = await kind.get(id);
392
+ if (row === null) return null;
393
+ return row.data;
394
+ }
395
+ const store = this.legacyStore;
396
+ if (store === null) return null;
397
+ return legacyGetData(await store.get.query({
398
+ collection: ALERTS_COLLECTION,
399
+ key: id
400
+ }));
401
+ }
402
+ async mergeUpdate(id, patch) {
403
+ const existing = await this.getData(id);
404
+ if (existing === null) return;
405
+ await this.putRaw(id, {
406
+ ...existing,
407
+ ...patch
408
+ });
409
+ }
410
+ async persist(alert) {
411
+ const { id, ...body } = alert;
412
+ const data = alertsRecordKind.schema.parse(body);
413
+ const kind = this.kind;
414
+ if (kind !== null) {
415
+ await kind.put({
416
+ id,
417
+ data
418
+ });
419
+ return;
420
+ }
421
+ await this.putRaw(id, {
422
+ ...body,
423
+ id
424
+ });
425
+ }
426
+ async putRaw(id, data) {
427
+ const { id: _drop, ...body } = AlertSchema.parse({
428
+ id,
429
+ ...data
430
+ });
431
+ const kind = this.kind;
432
+ if (kind !== null) {
433
+ await kind.put({
434
+ id,
435
+ data: body
436
+ });
437
+ return;
438
+ }
439
+ const store = this.legacyStore;
440
+ if (store === null) return;
441
+ await store.set.mutate({
442
+ collection: ALERTS_COLLECTION,
443
+ key: id,
444
+ value: {
445
+ id,
446
+ ...body
447
+ }
448
+ });
449
+ }
450
+ async delete(id) {
451
+ const kind = this.kind;
452
+ if (kind !== null) {
453
+ await kind.delete(id);
454
+ return;
455
+ }
456
+ const store = this.legacyStore;
457
+ if (store === null) return;
458
+ await store.delete.mutate({
459
+ collection: ALERTS_COLLECTION,
460
+ key: id
461
+ });
462
+ }
463
+ async listAllByCreatedAtAsc() {
464
+ const kind = this.kind;
465
+ let out;
466
+ if (kind !== null) out = (await (0, import_read_all_rows.readAllRows)(kind)).map(toRecord);
467
+ else out = [...await this.legacyRows()];
468
+ out.sort((a, b) => Number(a.data["createdAt"] ?? 0) - Number(b.data["createdAt"] ?? 0));
469
+ return out;
470
+ }
471
+ async listRecent(limit) {
472
+ const kind = this.kind;
473
+ let out;
474
+ if (kind !== null) out = (await (0, import_read_all_rows.readAllRows)(kind, { order: "id" })).map(toRecord);
475
+ else out = [...await this.legacyRows()];
476
+ out.sort((a, b) => Number(b.data["updatedAt"] ?? 0) - Number(a.data["updatedAt"] ?? 0));
477
+ return out.slice(0, limit);
478
+ }
479
+ async findWhereDataField(field, value) {
480
+ const kind = this.kind;
481
+ if (kind !== null) return (await (0, import_read_all_rows.readAllRows)(kind)).filter((r) => r.data[field] === value).map(toRecord);
482
+ return (await this.legacyRows()).filter((r) => r.data[field] === value);
483
+ }
484
+ };
485
+ //#endregion
280
486
  //#region src/builtins/alerts/alerts.addon.ts
281
487
  var CATEGORY_MAPPINGS = {
282
488
  "addon.crashed": {
@@ -384,6 +590,21 @@ var SILENT_TRACKED_CATEGORIES = [
384
590
  "system.liveness-recovered",
385
591
  "stream.online"
386
592
  ];
593
+ /** The `stream.offline` reasons that are an outage (D883) — mirrors the broker's `isStreamOutageReason`. */
594
+ var STREAM_OUTAGE_REASONS = [
595
+ "stale-timeout",
596
+ "wake-timeout",
597
+ "connection-error",
598
+ "battery-unreachable"
599
+ ];
600
+ /** The streams an offline event covers: its list, else the one stream it names. */
601
+ function streamIdsOf(listed, fallback) {
602
+ if (Array.isArray(listed)) {
603
+ const ids = listed.filter((id) => typeof id === "string");
604
+ if (ids.length > 0) return ids;
605
+ }
606
+ return fallback !== void 0 ? [fallback] : [];
607
+ }
387
608
  var TRACKED_CATEGORIES = [...Object.keys(CATEGORY_MAPPINGS), ...SILENT_TRACKED_CATEGORIES];
388
609
  /**
389
610
  * `[ALERT-CENTER-EVENTS]` — rate limiting. Events that fire thousands of
@@ -434,13 +655,18 @@ var DEFAULT_CONFIG = {
434
655
  maxAlerts: 500,
435
656
  retentionDays: 30,
436
657
  criticalPushEnabled: true,
437
- criticalPushTargetIds: ""
658
+ criticalPushTargetIds: []
438
659
  };
660
+ /** Accept the live `string[]` shape and the pre-multiselect CSV string. */
661
+ function normalizeCriticalPushTargetIds(raw) {
662
+ if (Array.isArray(raw)) return raw.filter((id) => typeof id === "string" && id.trim().length > 0).map((id) => id.trim());
663
+ if (typeof raw === "string" && raw.trim().length > 0) return raw.split(",").map((id) => id.trim()).filter((id) => id.length > 0);
664
+ return [];
665
+ }
439
666
  /** One burst of critical findings (a whole boot of refused runners) is ONE digest push. */
440
667
  var PUSH_COALESCE_MS = 2e3;
441
668
  /** Addons whose (re)start is a reason to retry pending critical pushes. */
442
669
  var PUSH_RECONCILE_ADDONS = new Set(["notifiers", "notification-center"]);
443
- var ALERTS_COLLECTION = "alerts";
444
670
  var EVENT_SOURCE = {
445
671
  type: "addon",
446
672
  id: "alert-center"
@@ -461,6 +687,7 @@ function toPrunable(record) {
461
687
  };
462
688
  }
463
689
  var AlertCenterAddon = class extends BaseAddon {
690
+ alertStore = null;
464
691
  unsubscribers = [];
465
692
  /** How long a burst of critical findings is gathered into one digest push. */
466
693
  pushCoalesceMs;
@@ -475,6 +702,11 @@ var AlertCenterAddon = class extends BaseAddon {
475
702
  */
476
703
  rateLimitCounters = /* @__PURE__ */ new Map();
477
704
  async onInitialize() {
705
+ const records = this.ctx.records;
706
+ const store = this.ctx.api?.settingsStore;
707
+ const useRecords = records !== void 0 && store !== void 0 && await resolvePhase6UseRecordsPathForHub(this.ctx, "alert-center", alertCenterRecordKinds, ["alerts"]);
708
+ this.alertStore = useRecords && records !== void 0 ? AlertStore.of(records) : store === void 0 ? null : AlertStore.legacy(store);
709
+ if (this.alertStore === null) throw new Error("alert-center needs ctx.records or settings-store on the hub");
478
710
  const alertsProvider = {
479
711
  emit: (alert) => this.emitAlert(alert),
480
712
  update: (input) => this.updateAlert(input.alertId, input.patch),
@@ -493,6 +725,8 @@ var AlertCenterAddon = class extends BaseAddon {
493
725
  }
494
726
  this.unsubscribers = unsubs;
495
727
  this.reconcilePendingPushes();
728
+ this.dismissLegacyStreamCards(void 0);
729
+ this.migrateCriticalPushTargetIds();
496
730
  this.ctx.logger.info("Initialized", { meta: { trackedCategoriesCount: TRACKED_CATEGORIES.length } });
497
731
  return [{
498
732
  capability: alertsCapability,
@@ -540,43 +774,28 @@ var AlertCenterAddon = class extends BaseAddon {
540
774
  type: "boolean",
541
775
  key: "criticalPushEnabled",
542
776
  label: "Push critical system alerts",
543
- description: "A runner that failed or refused to start, or a node whose runners are degraded, is pushed straight to the notifier targets - even when notification-center is down.",
777
+ description: "A runner that failed or refused to start, or a node whose runners are degraded, is pushed straight to the notifier targets — even when Notification Center is down. This path does not use notification rules.",
544
778
  default: true
545
779
  }),
546
780
  this.field({
547
- type: "text",
781
+ type: "notification-target-multiselect",
548
782
  key: "criticalPushTargetIds",
549
783
  label: "Critical push targets",
550
- description: "Comma-separated notifier target ids. Empty = every enabled shared (not personal) target.",
551
- default: ""
784
+ description: "Notifier targets that receive critical system alerts. Empty = every enabled shared (not personal) target.",
785
+ default: []
552
786
  })
553
787
  ]
554
788
  }] });
555
789
  }
556
790
  async listAlerts(filter) {
557
- const backend = this.ctx.api.settingsStore;
558
- if (!backend) return [];
559
- const whereClause = {};
560
- if (filter?.unreadOnly) whereClause.read = false;
561
- return (await backend.query.query({
562
- collection: ALERTS_COLLECTION,
563
- filter: {
564
- where: Object.keys(whereClause).length > 0 ? whereClause : void 0,
565
- orderBy: {
566
- field: "updatedAt",
567
- direction: "desc"
568
- },
569
- limit: filter?.limit ?? 100
570
- }
571
- })).map(parseAlert);
791
+ const store = this.alertStore;
792
+ if (store === null) return [];
793
+ return (await store.list(filter)).map(parseAlert);
572
794
  }
573
795
  async getUnreadCount() {
574
- const backend = this.ctx.api.settingsStore;
575
- if (!backend) return 0;
576
- return backend.count.query({
577
- collection: ALERTS_COLLECTION,
578
- filter: { where: { read: false } }
579
- });
796
+ const store = this.alertStore;
797
+ if (store === null) return 0;
798
+ return store.countUnread();
580
799
  }
581
800
  async markRead(alertId) {
582
801
  await this.mergeUpdate(alertId, {
@@ -585,11 +804,11 @@ var AlertCenterAddon = class extends BaseAddon {
585
804
  });
586
805
  }
587
806
  async markAllRead() {
588
- const backend = this.ctx.api.settingsStore;
589
- if (!backend) return;
590
- const unread = await backend.query.query({
591
- collection: ALERTS_COLLECTION,
592
- filter: { where: { read: false } }
807
+ const store = this.alertStore;
808
+ if (store === null) return;
809
+ const unread = await store.list({
810
+ unreadOnly: true,
811
+ limit: 1e4
593
812
  });
594
813
  const now = Date.now();
595
814
  for (const record of unread) await this.mergeUpdate(record.id, {
@@ -686,14 +905,14 @@ var AlertCenterAddon = class extends BaseAddon {
686
905
  }
687
906
  }
688
907
  async readRunnerRecord(alertId) {
689
- const backend = this.ctx.api.settingsStore;
690
- if (!backend) return null;
691
- const raw = await backend.get.query({
692
- collection: ALERTS_COLLECTION,
693
- key: alertId
908
+ const store = this.alertStore;
909
+ if (store === null) return null;
910
+ const raw = await store.getData(alertId);
911
+ if (raw === null) return null;
912
+ const alert = AlertSchema.safeParse({
913
+ id: alertId,
914
+ ...raw
694
915
  });
695
- if (raw === null || raw === void 0 || typeof raw !== "object") return null;
696
- const alert = AlertSchema.safeParse(raw);
697
916
  if (!alert.success || alert.data.status === "dismissed") return null;
698
917
  return parseRunnerRecord(alert.data.metadata?.["runner"]);
699
918
  }
@@ -821,13 +1040,10 @@ var AlertCenterAddon = class extends BaseAddon {
821
1040
  });
822
1041
  }
823
1042
  async readAlertRaw(alertId) {
824
- const backend = this.ctx.api.settingsStore;
825
- if (!backend) return null;
826
- const raw = await backend.get.query({
827
- collection: ALERTS_COLLECTION,
828
- key: alertId
829
- });
830
- if (raw === null || raw === void 0 || typeof raw !== "object") return null;
1043
+ const store = this.alertStore;
1044
+ if (store === null) return null;
1045
+ const raw = await store.getData(alertId);
1046
+ if (raw === null) return null;
831
1047
  const rec = Object.fromEntries(Object.entries(raw));
832
1048
  const meta = rec["metadata"];
833
1049
  const push = typeof meta === "object" && meta !== null && !Array.isArray(meta) ? meta["push"] : void 0;
@@ -845,9 +1061,19 @@ var AlertCenterAddon = class extends BaseAddon {
845
1061
  pushPolicy() {
846
1062
  return {
847
1063
  enabled: this.config.criticalPushEnabled,
848
- targetIds: this.config.criticalPushTargetIds.split(",").map((id) => id.trim()).filter((id) => id.length > 0)
1064
+ targetIds: normalizeCriticalPushTargetIds(this.config.criticalPushTargetIds)
849
1065
  };
850
1066
  }
1067
+ /** Rewrite a legacy CSV `criticalPushTargetIds` string into `string[]`. */
1068
+ async migrateCriticalPushTargetIds() {
1069
+ const raw = this.config.criticalPushTargetIds;
1070
+ if (typeof raw !== "string") return;
1071
+ try {
1072
+ await this.updateGlobalSettings({ criticalPushTargetIds: normalizeCriticalPushTargetIds(raw) });
1073
+ } catch (err) {
1074
+ this.ctx.logger.warn("criticalPushTargetIds CSV migration failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
1075
+ }
1076
+ }
851
1077
  pushPorts() {
852
1078
  const api = this.ctx.api;
853
1079
  return {
@@ -924,27 +1150,16 @@ var AlertCenterAddon = class extends BaseAddon {
924
1150
  return this.pushChain;
925
1151
  }
926
1152
  async storePushState(alertId, push) {
927
- const backend = this.ctx.api.settingsStore;
928
- if (!backend) return;
929
- const raw = await backend.get.query({
930
- collection: ALERTS_COLLECTION,
931
- key: alertId
932
- });
933
- if (raw === null || raw === void 0 || typeof raw !== "object") return;
934
- const existing = Object.fromEntries(Object.entries(raw));
935
- const meta = existing["metadata"];
1153
+ const store = this.alertStore;
1154
+ if (store === null) return;
1155
+ const raw = await store.getData(alertId);
1156
+ if (raw === null) return;
1157
+ const meta = raw["metadata"];
936
1158
  const metadata = typeof meta === "object" && meta !== null && !Array.isArray(meta) ? meta : {};
937
- await backend.set.mutate({
938
- collection: ALERTS_COLLECTION,
939
- key: alertId,
940
- value: {
941
- ...existing,
942
- metadata: {
943
- ...metadata,
944
- push
945
- }
946
- }
947
- });
1159
+ await store.mergeUpdate(alertId, { metadata: {
1160
+ ...metadata,
1161
+ push
1162
+ } });
948
1163
  }
949
1164
  /**
950
1165
  * Retry every critical alert whose push is still pending - at boot and when
@@ -952,19 +1167,10 @@ var AlertCenterAddon = class extends BaseAddon {
952
1167
  * delivered is skipped by its key, so reconciling never sends twice.
953
1168
  */
954
1169
  async reconcilePendingPushes() {
955
- const backend = this.ctx.api.settingsStore;
956
- if (!backend) return;
1170
+ const store = this.alertStore;
1171
+ if (store === null) return;
957
1172
  try {
958
- const rows = await backend.query.query({
959
- collection: ALERTS_COLLECTION,
960
- filter: {
961
- orderBy: {
962
- field: "updatedAt",
963
- direction: "desc"
964
- },
965
- limit: 500
966
- }
967
- });
1173
+ const rows = await store.listRecent(500);
968
1174
  for (const row of rows) {
969
1175
  const parsed = AlertSchema.safeParse({
970
1176
  id: row.id,
@@ -991,12 +1197,9 @@ var AlertCenterAddon = class extends BaseAddon {
991
1197
  const modelId = data.modelId ?? "unknown";
992
1198
  const progress = data.progress ?? 0;
993
1199
  const stableId = `model-download:${modelId}`;
994
- const backend = this.ctx.api.settingsStore;
995
- if (!backend) return;
996
- if ((await backend.query.query({
997
- collection: ALERTS_COLLECTION,
998
- filter: { where: { id: stableId } }
999
- })).length > 0) {
1200
+ const store = this.alertStore;
1201
+ if (store === null) return;
1202
+ if (await store.getData(stableId) !== null) {
1000
1203
  const now = Date.now();
1001
1204
  if (progress >= 100) {
1002
1205
  await this.mergeUpdate(stableId, {
@@ -1197,23 +1400,26 @@ var AlertCenterAddon = class extends BaseAddon {
1197
1400
  }
1198
1401
  }
1199
1402
  /**
1200
- * Stream offline — ONLY `stale-timeout` is an outage. Suspend / destroy /
1201
- * idle dial are deliberate and must not ring the bell (same gate NC uses).
1403
+ * Stream offline — ONLY an outage reason counts: `stale-timeout`, or one of
1404
+ * the battery failures (`wake-timeout`, `connection-error`,
1405
+ * `battery-unreachable`). Suspend / destroy / idle dial are deliberate and
1406
+ * must not ring the bell (same gate NC uses). One card per DEVICE (D883).
1202
1407
  */
1203
1408
  async handleStreamOffline(event) {
1204
1409
  const reason = event.data["reason"];
1205
- if (reason !== "stale-timeout") return;
1410
+ if (typeof reason !== "string" || !STREAM_OUTAGE_REASONS.includes(reason)) return;
1206
1411
  const deviceId = event.data["deviceId"];
1207
1412
  const camStreamId = event.data["camStreamId"];
1208
- if (typeof deviceId !== "number" || typeof camStreamId !== "string") return;
1413
+ if (typeof deviceId !== "number") return;
1414
+ const camStreamIds = streamIdsOf(event.data["camStreamIds"], typeof camStreamId === "string" ? camStreamId : void 0);
1209
1415
  const name = await this.resolveDeviceName(deviceId);
1210
1416
  const label = name ?? `Camera ${String(deviceId)}`;
1211
1417
  const alert = {
1212
- id: `stream-offline:${String(deviceId)}:${camStreamId}`,
1418
+ id: `stream-offline:${String(deviceId)}`,
1213
1419
  category: EventCategory.StreamOffline,
1214
1420
  severity: "warning",
1215
1421
  title: "Stream offline",
1216
- message: `${label} — ${camStreamId} stopped sending video (stale timeout).`,
1422
+ message: `${label} — ${camStreamIds.length > 0 ? camStreamIds.join(", ") : "its stream"} stopped sending video (${reason}).`,
1217
1423
  status: "active",
1218
1424
  read: false,
1219
1425
  createdAt: Date.now(),
@@ -1225,6 +1431,7 @@ var AlertCenterAddon = class extends BaseAddon {
1225
1431
  metadata: {
1226
1432
  deviceId,
1227
1433
  camStreamId,
1434
+ camStreamIds,
1228
1435
  reason,
1229
1436
  ...name !== void 0 ? { name } : {}
1230
1437
  }
@@ -1234,9 +1441,9 @@ var AlertCenterAddon = class extends BaseAddon {
1234
1441
  /** Stream online after an outage — drop the matching offline card. */
1235
1442
  async handleStreamOnline(event) {
1236
1443
  const deviceId = event.data["deviceId"];
1237
- const camStreamId = event.data["camStreamId"];
1238
- if (typeof deviceId !== "number" || typeof camStreamId !== "string") return;
1239
- const stableId = `stream-offline:${String(deviceId)}:${camStreamId}`;
1444
+ if (typeof deviceId !== "number") return;
1445
+ const stableId = `stream-offline:${String(deviceId)}`;
1446
+ await this.dismissLegacyStreamCards(deviceId);
1240
1447
  try {
1241
1448
  await this.dismiss(stableId);
1242
1449
  } catch (err) {
@@ -1246,6 +1453,19 @@ var AlertCenterAddon = class extends BaseAddon {
1246
1453
  } });
1247
1454
  }
1248
1455
  }
1456
+ /**
1457
+ * Dismiss the pre-D883 per-cam-stream cards (`stream-offline:<deviceId>:<stream>`)
1458
+ * of one device, or of every device when none is named. They can never meet
1459
+ * their `stream.online` under the per-device id.
1460
+ */
1461
+ async dismissLegacyStreamCards(deviceId) {
1462
+ try {
1463
+ const pattern = deviceId === void 0 ? /^stream-offline:\d+:.+/ : new RegExp(`^stream-offline:${String(deviceId)}:.+`);
1464
+ for (const alert of await this.listAlerts({ limit: 1e3 })) if (alert.status === "active" && pattern.test(alert.id)) await this.dismiss(alert.id);
1465
+ } catch (err) {
1466
+ this.ctx.logger?.debug("legacy stream cards: sweep failed", { meta: { error: errMsg(err) } });
1467
+ }
1468
+ }
1249
1469
  async resolveDeviceName(deviceId) {
1250
1470
  try {
1251
1471
  const device = await this.ctx.api.deviceManager.getDevice.query({ deviceId });
@@ -1320,45 +1540,14 @@ var AlertCenterAddon = class extends BaseAddon {
1320
1540
  * so we must read the existing record, merge the patch, and write back.
1321
1541
  */
1322
1542
  async mergeUpdate(id, patch) {
1323
- const backend = this.ctx.api.settingsStore;
1324
- if (!backend) return;
1325
- const raw = await backend.get.query({
1326
- collection: ALERTS_COLLECTION,
1327
- key: id
1328
- });
1329
- if (raw === null || raw === void 0 || typeof raw !== "object") return;
1330
- const existing = Object.fromEntries(Object.entries(raw));
1331
- await backend.set.mutate({
1332
- collection: ALERTS_COLLECTION,
1333
- key: id,
1334
- value: {
1335
- ...existing,
1336
- ...patch
1337
- }
1338
- });
1543
+ const store = this.alertStore;
1544
+ if (store === null) return;
1545
+ await store.mergeUpdate(id, patch);
1339
1546
  }
1340
1547
  async persistAlert(alert) {
1341
- const backend = this.ctx.api.settingsStore;
1342
- if (!backend) return;
1343
- const data = {
1344
- id: alert.id,
1345
- category: alert.category,
1346
- severity: alert.severity,
1347
- title: alert.title,
1348
- message: alert.message,
1349
- status: alert.status,
1350
- read: alert.read,
1351
- createdAt: alert.createdAt,
1352
- updatedAt: alert.updatedAt,
1353
- ...alert.progress !== void 0 ? { progress: alert.progress } : {},
1354
- ...alert.source ? { source: alert.source } : {},
1355
- ...alert.metadata ? { metadata: alert.metadata } : {}
1356
- };
1357
- await backend.set.mutate({
1358
- collection: ALERTS_COLLECTION,
1359
- key: alert.id,
1360
- value: data
1361
- });
1548
+ const store = this.alertStore;
1549
+ if (store === null) return;
1550
+ await store.persist(alert);
1362
1551
  }
1363
1552
  emitAlertCreatedEvent(alert) {
1364
1553
  this.ctx.eventBus?.emit(createEvent("alert.created", EVENT_SOURCE, {
@@ -1378,36 +1567,18 @@ var AlertCenterAddon = class extends BaseAddon {
1378
1567
  }));
1379
1568
  }
1380
1569
  async enforceMaxAlerts() {
1381
- const backend = this.ctx.api.settingsStore;
1382
- if (!backend) return;
1383
- if (await backend.count.query({ collection: ALERTS_COLLECTION }) <= this.config.maxAlerts) return;
1384
- const all = await backend.query.query({
1385
- collection: ALERTS_COLLECTION,
1386
- filter: { orderBy: {
1387
- field: "createdAt",
1388
- direction: "asc"
1389
- } }
1390
- });
1391
- for (const id of selectOverflow(all.map(toPrunable), this.config.maxAlerts)) await backend.delete.mutate({
1392
- collection: ALERTS_COLLECTION,
1393
- key: id
1394
- });
1570
+ const store = this.alertStore;
1571
+ if (store === null) return;
1572
+ if (await store.countAll() <= this.config.maxAlerts) return;
1573
+ const all = await store.listAllByCreatedAtAsc();
1574
+ for (const id of selectOverflow(all.map(toPrunable), this.config.maxAlerts)) await store.delete(id);
1395
1575
  }
1396
1576
  async enforceRetention() {
1397
- const backend = this.ctx.api.settingsStore;
1398
- if (!backend) return;
1577
+ const store = this.alertStore;
1578
+ if (store === null) return;
1399
1579
  const cutoff = Date.now() - this.config.retentionDays * 24 * 60 * 60 * 1e3;
1400
- const all = await backend.query.query({
1401
- collection: ALERTS_COLLECTION,
1402
- filter: { orderBy: {
1403
- field: "createdAt",
1404
- direction: "asc"
1405
- } }
1406
- });
1407
- for (const id of selectExpired(all.map(toPrunable), cutoff)) await backend.delete.mutate({
1408
- collection: ALERTS_COLLECTION,
1409
- key: id
1410
- });
1580
+ const all = await store.listAllByCreatedAtAsc();
1581
+ for (const id of selectExpired(all.map(toPrunable), cutoff)) await store.delete(id);
1411
1582
  }
1412
1583
  };
1413
1584
  //#endregion
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Record kinds owned by alert-center (records plan phase 4).
3
+ */
4
+ import { type Alert, type DefinedRecordKind } from "@camstack/types";
5
+ import { z } from "zod";
6
+ /** Legacy collection / adoptsFrom target (canonical KV before phase 4). */
7
+ export declare const ALERTS_COLLECTION = "alerts";
8
+ export type AlertBody = Omit<Alert, "id">;
9
+ export declare const AlertBodySchema: z.ZodType<AlertBody>;
10
+ export type AlertCenterRecordKindsMap = {
11
+ readonly alerts: DefinedRecordKind<z.ZodType<AlertBody>>;
12
+ };
13
+ export declare const alertCenterRecordKinds: AlertCenterRecordKindsMap;
14
+ /** Narrow kind for `records.kind()` — `IsolatedRecordKinds` string index is `| undefined`. */
15
+ export declare const alertsRecordKind: DefinedRecordKind<z.ZodType<AlertBody>>;
@@ -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-D9qT3jHr.js");
6
+ const require_dist = require("../../dist-DgOG8flk.js");
7
7
  //#region src/builtins/autotrack/autotrack-config.ts
8
8
  var DEFAULT_AUTOTRACK_LOOP_CONFIG = {
9
9
  deadZoneFraction: .5,