@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
@@ -3,7 +3,9 @@ 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
+ const require_read_all_rows$1 = require("../../read-all-rows-CVoO1ONM.js");
8
+ const require_phase6_declare_gate = require("../../phase6-declare-gate-Ci1-8uUn.js");
7
9
  //#region src/builtins/alerts/critical-push.ts
8
10
  /** Alert categories that are critical system findings. */
9
11
  var CRITICAL_CATEGORIES = new Set([
@@ -282,6 +284,211 @@ function runnerPushLine(record) {
282
284
  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.`;
283
285
  }
284
286
  //#endregion
287
+ //#region src/builtins/alerts/record-kinds.ts
288
+ var import_read_all_rows = require_read_all_rows$1.require_read_all_rows();
289
+ /**
290
+ * Record kinds owned by alert-center (records plan phase 4).
291
+ */
292
+ /** Legacy collection / adoptsFrom target (canonical KV before phase 4). */
293
+ var ALERTS_COLLECTION = "alerts";
294
+ var AlertBodySchema = require_dist.AlertSchema.omit({ id: true });
295
+ var _alertCenterRecordKinds = require_dist.defineRecordKinds({ alerts: {
296
+ schema: AlertBodySchema,
297
+ class: "audit",
298
+ writeTrigger: "EventBus categories the alert center tracks, operator mark read/dismiss, and model-download progress merges",
299
+ device: "none",
300
+ at: "none",
301
+ indexes: [
302
+ {
303
+ name: "read",
304
+ fields: ["read"]
305
+ },
306
+ {
307
+ name: "created_at",
308
+ fields: ["createdAt"]
309
+ },
310
+ {
311
+ name: "updated_at",
312
+ fields: ["updatedAt"]
313
+ }
314
+ ],
315
+ retention: {
316
+ owner: "alert-center",
317
+ reason: "maxAlerts cap and retentionDays enforced on write (enforceMaxAlerts / enforceRetention); read alerts preferred when trimming overflow"
318
+ },
319
+ adoptsFrom: ALERTS_COLLECTION
320
+ } });
321
+ var alertCenterRecordKinds = _alertCenterRecordKinds;
322
+ /** Narrow kind for `records.kind()` — `IsolatedRecordKinds` string index is `| undefined`. */
323
+ var alertsRecordKind = _alertCenterRecordKinds.alerts;
324
+ //#endregion
325
+ //#region src/builtins/alerts/alert-store.ts
326
+ function toRecord(row) {
327
+ return {
328
+ id: row.id,
329
+ data: row.data
330
+ };
331
+ }
332
+ function toRecordFromLegacy(row) {
333
+ return {
334
+ id: row.id,
335
+ data: row.data
336
+ };
337
+ }
338
+ function isRecord(value) {
339
+ return typeof value === "object" && value !== null && !Array.isArray(value);
340
+ }
341
+ function legacyGetData(value) {
342
+ if (!isRecord(value)) return null;
343
+ if ("data" in value) {
344
+ const data = value.data;
345
+ return isRecord(data) ? data : null;
346
+ }
347
+ return value;
348
+ }
349
+ var AlertStore = class AlertStore {
350
+ kind;
351
+ legacyStore;
352
+ constructor(kind, legacyStore) {
353
+ this.kind = kind;
354
+ this.legacyStore = legacyStore;
355
+ }
356
+ static of(records) {
357
+ return new AlertStore(records.kind(alertsRecordKind), null);
358
+ }
359
+ static legacy(store) {
360
+ return new AlertStore(null, store);
361
+ }
362
+ async legacyRows() {
363
+ const store = this.legacyStore;
364
+ if (store === null) return [];
365
+ return (await store.query.query({
366
+ collection: ALERTS_COLLECTION,
367
+ filter: {}
368
+ })).map((row) => toRecordFromLegacy({
369
+ id: row.id,
370
+ data: row.data
371
+ }));
372
+ }
373
+ async list(filter) {
374
+ let out;
375
+ const kind = this.kind;
376
+ if (kind !== null) out = (await (0, import_read_all_rows.readAllRows)(kind, { order: "id" })).map(toRecord);
377
+ else out = [...await this.legacyRows()];
378
+ if (filter?.unreadOnly === true) out = out.filter((r) => r.data["read"] === false);
379
+ out.sort((a, b) => Number(b.data["updatedAt"] ?? 0) - Number(a.data["updatedAt"] ?? 0));
380
+ const limit = filter?.limit ?? 100;
381
+ return out.slice(0, limit);
382
+ }
383
+ async countUnread() {
384
+ return (await this.list({
385
+ unreadOnly: true,
386
+ limit: 1e4
387
+ })).length;
388
+ }
389
+ async countAll() {
390
+ const kind = this.kind;
391
+ if (kind !== null) return (await (0, import_read_all_rows.readAllRows)(kind)).length;
392
+ return (await this.legacyRows()).length;
393
+ }
394
+ async getData(id) {
395
+ const kind = this.kind;
396
+ if (kind !== null) {
397
+ const row = await kind.get(id);
398
+ if (row === null) return null;
399
+ return row.data;
400
+ }
401
+ const store = this.legacyStore;
402
+ if (store === null) return null;
403
+ return legacyGetData(await store.get.query({
404
+ collection: ALERTS_COLLECTION,
405
+ key: id
406
+ }));
407
+ }
408
+ async mergeUpdate(id, patch) {
409
+ const existing = await this.getData(id);
410
+ if (existing === null) return;
411
+ await this.putRaw(id, {
412
+ ...existing,
413
+ ...patch
414
+ });
415
+ }
416
+ async persist(alert) {
417
+ const { id, ...body } = alert;
418
+ const data = alertsRecordKind.schema.parse(body);
419
+ const kind = this.kind;
420
+ if (kind !== null) {
421
+ await kind.put({
422
+ id,
423
+ data
424
+ });
425
+ return;
426
+ }
427
+ await this.putRaw(id, {
428
+ ...body,
429
+ id
430
+ });
431
+ }
432
+ async putRaw(id, data) {
433
+ const { id: _drop, ...body } = require_dist.AlertSchema.parse({
434
+ id,
435
+ ...data
436
+ });
437
+ const kind = this.kind;
438
+ if (kind !== null) {
439
+ await kind.put({
440
+ id,
441
+ data: body
442
+ });
443
+ return;
444
+ }
445
+ const store = this.legacyStore;
446
+ if (store === null) return;
447
+ await store.set.mutate({
448
+ collection: ALERTS_COLLECTION,
449
+ key: id,
450
+ value: {
451
+ id,
452
+ ...body
453
+ }
454
+ });
455
+ }
456
+ async delete(id) {
457
+ const kind = this.kind;
458
+ if (kind !== null) {
459
+ await kind.delete(id);
460
+ return;
461
+ }
462
+ const store = this.legacyStore;
463
+ if (store === null) return;
464
+ await store.delete.mutate({
465
+ collection: ALERTS_COLLECTION,
466
+ key: id
467
+ });
468
+ }
469
+ async listAllByCreatedAtAsc() {
470
+ const kind = this.kind;
471
+ let out;
472
+ if (kind !== null) out = (await (0, import_read_all_rows.readAllRows)(kind)).map(toRecord);
473
+ else out = [...await this.legacyRows()];
474
+ out.sort((a, b) => Number(a.data["createdAt"] ?? 0) - Number(b.data["createdAt"] ?? 0));
475
+ return out;
476
+ }
477
+ async listRecent(limit) {
478
+ const kind = this.kind;
479
+ let out;
480
+ if (kind !== null) out = (await (0, import_read_all_rows.readAllRows)(kind, { order: "id" })).map(toRecord);
481
+ else out = [...await this.legacyRows()];
482
+ out.sort((a, b) => Number(b.data["updatedAt"] ?? 0) - Number(a.data["updatedAt"] ?? 0));
483
+ return out.slice(0, limit);
484
+ }
485
+ async findWhereDataField(field, value) {
486
+ const kind = this.kind;
487
+ if (kind !== null) return (await (0, import_read_all_rows.readAllRows)(kind)).filter((r) => r.data[field] === value).map(toRecord);
488
+ return (await this.legacyRows()).filter((r) => r.data[field] === value);
489
+ }
490
+ };
491
+ //#endregion
285
492
  //#region src/builtins/alerts/alerts.addon.ts
286
493
  var CATEGORY_MAPPINGS = {
287
494
  "addon.crashed": {
@@ -389,6 +596,21 @@ var SILENT_TRACKED_CATEGORIES = [
389
596
  "system.liveness-recovered",
390
597
  "stream.online"
391
598
  ];
599
+ /** The `stream.offline` reasons that are an outage (D883) — mirrors the broker's `isStreamOutageReason`. */
600
+ var STREAM_OUTAGE_REASONS = [
601
+ "stale-timeout",
602
+ "wake-timeout",
603
+ "connection-error",
604
+ "battery-unreachable"
605
+ ];
606
+ /** The streams an offline event covers: its list, else the one stream it names. */
607
+ function streamIdsOf(listed, fallback) {
608
+ if (Array.isArray(listed)) {
609
+ const ids = listed.filter((id) => typeof id === "string");
610
+ if (ids.length > 0) return ids;
611
+ }
612
+ return fallback !== void 0 ? [fallback] : [];
613
+ }
392
614
  var TRACKED_CATEGORIES = [...Object.keys(CATEGORY_MAPPINGS), ...SILENT_TRACKED_CATEGORIES];
393
615
  /**
394
616
  * `[ALERT-CENTER-EVENTS]` — rate limiting. Events that fire thousands of
@@ -439,13 +661,18 @@ var DEFAULT_CONFIG = {
439
661
  maxAlerts: 500,
440
662
  retentionDays: 30,
441
663
  criticalPushEnabled: true,
442
- criticalPushTargetIds: ""
664
+ criticalPushTargetIds: []
443
665
  };
666
+ /** Accept the live `string[]` shape and the pre-multiselect CSV string. */
667
+ function normalizeCriticalPushTargetIds(raw) {
668
+ if (Array.isArray(raw)) return raw.filter((id) => typeof id === "string" && id.trim().length > 0).map((id) => id.trim());
669
+ if (typeof raw === "string" && raw.trim().length > 0) return raw.split(",").map((id) => id.trim()).filter((id) => id.length > 0);
670
+ return [];
671
+ }
444
672
  /** One burst of critical findings (a whole boot of refused runners) is ONE digest push. */
445
673
  var PUSH_COALESCE_MS = 2e3;
446
674
  /** Addons whose (re)start is a reason to retry pending critical pushes. */
447
675
  var PUSH_RECONCILE_ADDONS = new Set(["notifiers", "notification-center"]);
448
- var ALERTS_COLLECTION = "alerts";
449
676
  var EVENT_SOURCE = {
450
677
  type: "addon",
451
678
  id: "alert-center"
@@ -466,6 +693,7 @@ function toPrunable(record) {
466
693
  };
467
694
  }
468
695
  var AlertCenterAddon = class extends require_dist.BaseAddon {
696
+ alertStore = null;
469
697
  unsubscribers = [];
470
698
  /** How long a burst of critical findings is gathered into one digest push. */
471
699
  pushCoalesceMs;
@@ -480,6 +708,11 @@ var AlertCenterAddon = class extends require_dist.BaseAddon {
480
708
  */
481
709
  rateLimitCounters = /* @__PURE__ */ new Map();
482
710
  async onInitialize() {
711
+ const records = this.ctx.records;
712
+ const store = this.ctx.api?.settingsStore;
713
+ const useRecords = records !== void 0 && store !== void 0 && await require_phase6_declare_gate.resolvePhase6UseRecordsPathForHub(this.ctx, "alert-center", alertCenterRecordKinds, ["alerts"]);
714
+ this.alertStore = useRecords && records !== void 0 ? AlertStore.of(records) : store === void 0 ? null : AlertStore.legacy(store);
715
+ if (this.alertStore === null) throw new Error("alert-center needs ctx.records or settings-store on the hub");
483
716
  const alertsProvider = {
484
717
  emit: (alert) => this.emitAlert(alert),
485
718
  update: (input) => this.updateAlert(input.alertId, input.patch),
@@ -498,6 +731,8 @@ var AlertCenterAddon = class extends require_dist.BaseAddon {
498
731
  }
499
732
  this.unsubscribers = unsubs;
500
733
  this.reconcilePendingPushes();
734
+ this.dismissLegacyStreamCards(void 0);
735
+ this.migrateCriticalPushTargetIds();
501
736
  this.ctx.logger.info("Initialized", { meta: { trackedCategoriesCount: TRACKED_CATEGORIES.length } });
502
737
  return [{
503
738
  capability: require_dist.alertsCapability,
@@ -545,43 +780,28 @@ var AlertCenterAddon = class extends require_dist.BaseAddon {
545
780
  type: "boolean",
546
781
  key: "criticalPushEnabled",
547
782
  label: "Push critical system alerts",
548
- 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.",
783
+ 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.",
549
784
  default: true
550
785
  }),
551
786
  this.field({
552
- type: "text",
787
+ type: "notification-target-multiselect",
553
788
  key: "criticalPushTargetIds",
554
789
  label: "Critical push targets",
555
- description: "Comma-separated notifier target ids. Empty = every enabled shared (not personal) target.",
556
- default: ""
790
+ description: "Notifier targets that receive critical system alerts. Empty = every enabled shared (not personal) target.",
791
+ default: []
557
792
  })
558
793
  ]
559
794
  }] });
560
795
  }
561
796
  async listAlerts(filter) {
562
- const backend = this.ctx.api.settingsStore;
563
- if (!backend) return [];
564
- const whereClause = {};
565
- if (filter?.unreadOnly) whereClause.read = false;
566
- return (await backend.query.query({
567
- collection: ALERTS_COLLECTION,
568
- filter: {
569
- where: Object.keys(whereClause).length > 0 ? whereClause : void 0,
570
- orderBy: {
571
- field: "updatedAt",
572
- direction: "desc"
573
- },
574
- limit: filter?.limit ?? 100
575
- }
576
- })).map(parseAlert);
797
+ const store = this.alertStore;
798
+ if (store === null) return [];
799
+ return (await store.list(filter)).map(parseAlert);
577
800
  }
578
801
  async getUnreadCount() {
579
- const backend = this.ctx.api.settingsStore;
580
- if (!backend) return 0;
581
- return backend.count.query({
582
- collection: ALERTS_COLLECTION,
583
- filter: { where: { read: false } }
584
- });
802
+ const store = this.alertStore;
803
+ if (store === null) return 0;
804
+ return store.countUnread();
585
805
  }
586
806
  async markRead(alertId) {
587
807
  await this.mergeUpdate(alertId, {
@@ -590,11 +810,11 @@ var AlertCenterAddon = class extends require_dist.BaseAddon {
590
810
  });
591
811
  }
592
812
  async markAllRead() {
593
- const backend = this.ctx.api.settingsStore;
594
- if (!backend) return;
595
- const unread = await backend.query.query({
596
- collection: ALERTS_COLLECTION,
597
- filter: { where: { read: false } }
813
+ const store = this.alertStore;
814
+ if (store === null) return;
815
+ const unread = await store.list({
816
+ unreadOnly: true,
817
+ limit: 1e4
598
818
  });
599
819
  const now = Date.now();
600
820
  for (const record of unread) await this.mergeUpdate(record.id, {
@@ -691,14 +911,14 @@ var AlertCenterAddon = class extends require_dist.BaseAddon {
691
911
  }
692
912
  }
693
913
  async readRunnerRecord(alertId) {
694
- const backend = this.ctx.api.settingsStore;
695
- if (!backend) return null;
696
- const raw = await backend.get.query({
697
- collection: ALERTS_COLLECTION,
698
- key: alertId
914
+ const store = this.alertStore;
915
+ if (store === null) return null;
916
+ const raw = await store.getData(alertId);
917
+ if (raw === null) return null;
918
+ const alert = require_dist.AlertSchema.safeParse({
919
+ id: alertId,
920
+ ...raw
699
921
  });
700
- if (raw === null || raw === void 0 || typeof raw !== "object") return null;
701
- const alert = require_dist.AlertSchema.safeParse(raw);
702
922
  if (!alert.success || alert.data.status === "dismissed") return null;
703
923
  return parseRunnerRecord(alert.data.metadata?.["runner"]);
704
924
  }
@@ -826,13 +1046,10 @@ var AlertCenterAddon = class extends require_dist.BaseAddon {
826
1046
  });
827
1047
  }
828
1048
  async readAlertRaw(alertId) {
829
- const backend = this.ctx.api.settingsStore;
830
- if (!backend) return null;
831
- const raw = await backend.get.query({
832
- collection: ALERTS_COLLECTION,
833
- key: alertId
834
- });
835
- if (raw === null || raw === void 0 || typeof raw !== "object") return null;
1049
+ const store = this.alertStore;
1050
+ if (store === null) return null;
1051
+ const raw = await store.getData(alertId);
1052
+ if (raw === null) return null;
836
1053
  const rec = Object.fromEntries(Object.entries(raw));
837
1054
  const meta = rec["metadata"];
838
1055
  const push = typeof meta === "object" && meta !== null && !Array.isArray(meta) ? meta["push"] : void 0;
@@ -850,9 +1067,19 @@ var AlertCenterAddon = class extends require_dist.BaseAddon {
850
1067
  pushPolicy() {
851
1068
  return {
852
1069
  enabled: this.config.criticalPushEnabled,
853
- targetIds: this.config.criticalPushTargetIds.split(",").map((id) => id.trim()).filter((id) => id.length > 0)
1070
+ targetIds: normalizeCriticalPushTargetIds(this.config.criticalPushTargetIds)
854
1071
  };
855
1072
  }
1073
+ /** Rewrite a legacy CSV `criticalPushTargetIds` string into `string[]`. */
1074
+ async migrateCriticalPushTargetIds() {
1075
+ const raw = this.config.criticalPushTargetIds;
1076
+ if (typeof raw !== "string") return;
1077
+ try {
1078
+ await this.updateGlobalSettings({ criticalPushTargetIds: normalizeCriticalPushTargetIds(raw) });
1079
+ } catch (err) {
1080
+ this.ctx.logger.warn("criticalPushTargetIds CSV migration failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
1081
+ }
1082
+ }
856
1083
  pushPorts() {
857
1084
  const api = this.ctx.api;
858
1085
  return {
@@ -929,27 +1156,16 @@ var AlertCenterAddon = class extends require_dist.BaseAddon {
929
1156
  return this.pushChain;
930
1157
  }
931
1158
  async storePushState(alertId, push) {
932
- const backend = this.ctx.api.settingsStore;
933
- if (!backend) return;
934
- const raw = await backend.get.query({
935
- collection: ALERTS_COLLECTION,
936
- key: alertId
937
- });
938
- if (raw === null || raw === void 0 || typeof raw !== "object") return;
939
- const existing = Object.fromEntries(Object.entries(raw));
940
- const meta = existing["metadata"];
1159
+ const store = this.alertStore;
1160
+ if (store === null) return;
1161
+ const raw = await store.getData(alertId);
1162
+ if (raw === null) return;
1163
+ const meta = raw["metadata"];
941
1164
  const metadata = typeof meta === "object" && meta !== null && !Array.isArray(meta) ? meta : {};
942
- await backend.set.mutate({
943
- collection: ALERTS_COLLECTION,
944
- key: alertId,
945
- value: {
946
- ...existing,
947
- metadata: {
948
- ...metadata,
949
- push
950
- }
951
- }
952
- });
1165
+ await store.mergeUpdate(alertId, { metadata: {
1166
+ ...metadata,
1167
+ push
1168
+ } });
953
1169
  }
954
1170
  /**
955
1171
  * Retry every critical alert whose push is still pending - at boot and when
@@ -957,19 +1173,10 @@ var AlertCenterAddon = class extends require_dist.BaseAddon {
957
1173
  * delivered is skipped by its key, so reconciling never sends twice.
958
1174
  */
959
1175
  async reconcilePendingPushes() {
960
- const backend = this.ctx.api.settingsStore;
961
- if (!backend) return;
1176
+ const store = this.alertStore;
1177
+ if (store === null) return;
962
1178
  try {
963
- const rows = await backend.query.query({
964
- collection: ALERTS_COLLECTION,
965
- filter: {
966
- orderBy: {
967
- field: "updatedAt",
968
- direction: "desc"
969
- },
970
- limit: 500
971
- }
972
- });
1179
+ const rows = await store.listRecent(500);
973
1180
  for (const row of rows) {
974
1181
  const parsed = require_dist.AlertSchema.safeParse({
975
1182
  id: row.id,
@@ -996,12 +1203,9 @@ var AlertCenterAddon = class extends require_dist.BaseAddon {
996
1203
  const modelId = data.modelId ?? "unknown";
997
1204
  const progress = data.progress ?? 0;
998
1205
  const stableId = `model-download:${modelId}`;
999
- const backend = this.ctx.api.settingsStore;
1000
- if (!backend) return;
1001
- if ((await backend.query.query({
1002
- collection: ALERTS_COLLECTION,
1003
- filter: { where: { id: stableId } }
1004
- })).length > 0) {
1206
+ const store = this.alertStore;
1207
+ if (store === null) return;
1208
+ if (await store.getData(stableId) !== null) {
1005
1209
  const now = Date.now();
1006
1210
  if (progress >= 100) {
1007
1211
  await this.mergeUpdate(stableId, {
@@ -1202,23 +1406,26 @@ var AlertCenterAddon = class extends require_dist.BaseAddon {
1202
1406
  }
1203
1407
  }
1204
1408
  /**
1205
- * Stream offline — ONLY `stale-timeout` is an outage. Suspend / destroy /
1206
- * idle dial are deliberate and must not ring the bell (same gate NC uses).
1409
+ * Stream offline — ONLY an outage reason counts: `stale-timeout`, or one of
1410
+ * the battery failures (`wake-timeout`, `connection-error`,
1411
+ * `battery-unreachable`). Suspend / destroy / idle dial are deliberate and
1412
+ * must not ring the bell (same gate NC uses). One card per DEVICE (D883).
1207
1413
  */
1208
1414
  async handleStreamOffline(event) {
1209
1415
  const reason = event.data["reason"];
1210
- if (reason !== "stale-timeout") return;
1416
+ if (typeof reason !== "string" || !STREAM_OUTAGE_REASONS.includes(reason)) return;
1211
1417
  const deviceId = event.data["deviceId"];
1212
1418
  const camStreamId = event.data["camStreamId"];
1213
- if (typeof deviceId !== "number" || typeof camStreamId !== "string") return;
1419
+ if (typeof deviceId !== "number") return;
1420
+ const camStreamIds = streamIdsOf(event.data["camStreamIds"], typeof camStreamId === "string" ? camStreamId : void 0);
1214
1421
  const name = await this.resolveDeviceName(deviceId);
1215
1422
  const label = name ?? `Camera ${String(deviceId)}`;
1216
1423
  const alert = {
1217
- id: `stream-offline:${String(deviceId)}:${camStreamId}`,
1424
+ id: `stream-offline:${String(deviceId)}`,
1218
1425
  category: require_dist.EventCategory.StreamOffline,
1219
1426
  severity: "warning",
1220
1427
  title: "Stream offline",
1221
- message: `${label} — ${camStreamId} stopped sending video (stale timeout).`,
1428
+ message: `${label} — ${camStreamIds.length > 0 ? camStreamIds.join(", ") : "its stream"} stopped sending video (${reason}).`,
1222
1429
  status: "active",
1223
1430
  read: false,
1224
1431
  createdAt: Date.now(),
@@ -1230,6 +1437,7 @@ var AlertCenterAddon = class extends require_dist.BaseAddon {
1230
1437
  metadata: {
1231
1438
  deviceId,
1232
1439
  camStreamId,
1440
+ camStreamIds,
1233
1441
  reason,
1234
1442
  ...name !== void 0 ? { name } : {}
1235
1443
  }
@@ -1239,9 +1447,9 @@ var AlertCenterAddon = class extends require_dist.BaseAddon {
1239
1447
  /** Stream online after an outage — drop the matching offline card. */
1240
1448
  async handleStreamOnline(event) {
1241
1449
  const deviceId = event.data["deviceId"];
1242
- const camStreamId = event.data["camStreamId"];
1243
- if (typeof deviceId !== "number" || typeof camStreamId !== "string") return;
1244
- const stableId = `stream-offline:${String(deviceId)}:${camStreamId}`;
1450
+ if (typeof deviceId !== "number") return;
1451
+ const stableId = `stream-offline:${String(deviceId)}`;
1452
+ await this.dismissLegacyStreamCards(deviceId);
1245
1453
  try {
1246
1454
  await this.dismiss(stableId);
1247
1455
  } catch (err) {
@@ -1251,6 +1459,19 @@ var AlertCenterAddon = class extends require_dist.BaseAddon {
1251
1459
  } });
1252
1460
  }
1253
1461
  }
1462
+ /**
1463
+ * Dismiss the pre-D883 per-cam-stream cards (`stream-offline:<deviceId>:<stream>`)
1464
+ * of one device, or of every device when none is named. They can never meet
1465
+ * their `stream.online` under the per-device id.
1466
+ */
1467
+ async dismissLegacyStreamCards(deviceId) {
1468
+ try {
1469
+ const pattern = deviceId === void 0 ? /^stream-offline:\d+:.+/ : new RegExp(`^stream-offline:${String(deviceId)}:.+`);
1470
+ for (const alert of await this.listAlerts({ limit: 1e3 })) if (alert.status === "active" && pattern.test(alert.id)) await this.dismiss(alert.id);
1471
+ } catch (err) {
1472
+ this.ctx.logger?.debug("legacy stream cards: sweep failed", { meta: { error: require_dist.errMsg(err) } });
1473
+ }
1474
+ }
1254
1475
  async resolveDeviceName(deviceId) {
1255
1476
  try {
1256
1477
  const device = await this.ctx.api.deviceManager.getDevice.query({ deviceId });
@@ -1325,45 +1546,14 @@ var AlertCenterAddon = class extends require_dist.BaseAddon {
1325
1546
  * so we must read the existing record, merge the patch, and write back.
1326
1547
  */
1327
1548
  async mergeUpdate(id, patch) {
1328
- const backend = this.ctx.api.settingsStore;
1329
- if (!backend) return;
1330
- const raw = await backend.get.query({
1331
- collection: ALERTS_COLLECTION,
1332
- key: id
1333
- });
1334
- if (raw === null || raw === void 0 || typeof raw !== "object") return;
1335
- const existing = Object.fromEntries(Object.entries(raw));
1336
- await backend.set.mutate({
1337
- collection: ALERTS_COLLECTION,
1338
- key: id,
1339
- value: {
1340
- ...existing,
1341
- ...patch
1342
- }
1343
- });
1549
+ const store = this.alertStore;
1550
+ if (store === null) return;
1551
+ await store.mergeUpdate(id, patch);
1344
1552
  }
1345
1553
  async persistAlert(alert) {
1346
- const backend = this.ctx.api.settingsStore;
1347
- if (!backend) return;
1348
- const data = {
1349
- id: alert.id,
1350
- category: alert.category,
1351
- severity: alert.severity,
1352
- title: alert.title,
1353
- message: alert.message,
1354
- status: alert.status,
1355
- read: alert.read,
1356
- createdAt: alert.createdAt,
1357
- updatedAt: alert.updatedAt,
1358
- ...alert.progress !== void 0 ? { progress: alert.progress } : {},
1359
- ...alert.source ? { source: alert.source } : {},
1360
- ...alert.metadata ? { metadata: alert.metadata } : {}
1361
- };
1362
- await backend.set.mutate({
1363
- collection: ALERTS_COLLECTION,
1364
- key: alert.id,
1365
- value: data
1366
- });
1554
+ const store = this.alertStore;
1555
+ if (store === null) return;
1556
+ await store.persist(alert);
1367
1557
  }
1368
1558
  emitAlertCreatedEvent(alert) {
1369
1559
  this.ctx.eventBus?.emit(require_dist.createEvent("alert.created", EVENT_SOURCE, {
@@ -1383,36 +1573,18 @@ var AlertCenterAddon = class extends require_dist.BaseAddon {
1383
1573
  }));
1384
1574
  }
1385
1575
  async enforceMaxAlerts() {
1386
- const backend = this.ctx.api.settingsStore;
1387
- if (!backend) return;
1388
- if (await backend.count.query({ collection: ALERTS_COLLECTION }) <= this.config.maxAlerts) return;
1389
- const all = await backend.query.query({
1390
- collection: ALERTS_COLLECTION,
1391
- filter: { orderBy: {
1392
- field: "createdAt",
1393
- direction: "asc"
1394
- } }
1395
- });
1396
- for (const id of selectOverflow(all.map(toPrunable), this.config.maxAlerts)) await backend.delete.mutate({
1397
- collection: ALERTS_COLLECTION,
1398
- key: id
1399
- });
1576
+ const store = this.alertStore;
1577
+ if (store === null) return;
1578
+ if (await store.countAll() <= this.config.maxAlerts) return;
1579
+ const all = await store.listAllByCreatedAtAsc();
1580
+ for (const id of selectOverflow(all.map(toPrunable), this.config.maxAlerts)) await store.delete(id);
1400
1581
  }
1401
1582
  async enforceRetention() {
1402
- const backend = this.ctx.api.settingsStore;
1403
- if (!backend) return;
1583
+ const store = this.alertStore;
1584
+ if (store === null) return;
1404
1585
  const cutoff = Date.now() - this.config.retentionDays * 24 * 60 * 60 * 1e3;
1405
- const all = await backend.query.query({
1406
- collection: ALERTS_COLLECTION,
1407
- filter: { orderBy: {
1408
- field: "createdAt",
1409
- direction: "asc"
1410
- } }
1411
- });
1412
- for (const id of selectExpired(all.map(toPrunable), cutoff)) await backend.delete.mutate({
1413
- collection: ALERTS_COLLECTION,
1414
- key: id
1415
- });
1586
+ const all = await store.listAllByCreatedAtAsc();
1587
+ for (const id of selectExpired(all.map(toPrunable), cutoff)) await store.delete(id);
1416
1588
  }
1417
1589
  };
1418
1590
  //#endregion