@camstack/system 1.2.30 → 1.2.32

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 (60) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.d.ts +10 -0
  6. package/dist/builtins/alerts/alerts.addon.js +60 -1
  7. package/dist/builtins/alerts/alerts.addon.mjs +60 -1
  8. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +135 -92
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +135 -92
  10. package/dist/builtins/backup-orchestrator/destination-policy.d.ts +12 -14
  11. package/dist/builtins/backup-orchestrator/schedule-store.d.ts +6 -6
  12. package/dist/builtins/console-logging/index.js +1 -1
  13. package/dist/builtins/console-logging/index.mjs +1 -1
  14. package/dist/builtins/device-manager/device-manager.addon.js +1 -1
  15. package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
  16. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  17. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  18. package/dist/builtins/hub-forwarder/index.js +1 -1
  19. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  20. package/dist/builtins/liveness-monitor/index.d.ts +2 -0
  21. package/dist/builtins/liveness-monitor/index.js +6 -0
  22. package/dist/builtins/liveness-monitor/index.mjs +2 -0
  23. package/dist/builtins/liveness-monitor/liveness-checks.d.ts +67 -0
  24. package/dist/builtins/liveness-monitor/liveness-monitor.addon.d.ts +32 -0
  25. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +216 -0
  26. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +212 -0
  27. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  28. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  29. package/dist/builtins/local-network/local-network.addon.js +1 -1
  30. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  31. package/dist/builtins/loki-logging/index.js +1 -1
  32. package/dist/builtins/loki-logging/index.mjs +1 -1
  33. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  34. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  35. package/dist/builtins/platform-probe/index.js +1 -1
  36. package/dist/builtins/platform-probe/index.mjs +1 -1
  37. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  38. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  39. package/dist/builtins/snapshot/index.js +1 -1
  40. package/dist/builtins/snapshot/index.mjs +1 -1
  41. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  42. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  43. package/dist/builtins/sqlite-storage/integration-registry.d.ts +3 -3
  44. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +6 -8
  45. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +3 -25
  46. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +3 -25
  47. package/dist/builtins/storage-orchestrator/location-store.d.ts +17 -24
  48. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +0 -6
  49. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +130 -121
  50. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +130 -121
  51. package/dist/builtins/system-config/system-config.addon.js +1 -1
  52. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  53. package/dist/builtins/winston-logging/index.js +1 -1
  54. package/dist/builtins/winston-logging/index.mjs +1 -1
  55. package/dist/{dist-Dr_P0DOB.mjs → dist-BL_FHWPP.mjs} +33 -2
  56. package/dist/{dist-VIwdvws5.js → dist-CSs1A90r.js} +33 -2
  57. package/dist/index.d.ts +1 -0
  58. package/dist/index.js +365 -224
  59. package/dist/index.mjs +365 -225
  60. package/package.json +10 -1
@@ -0,0 +1,216 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("../../chunk-Cek0wNdY.js");
3
+ const require_dist = require("../../dist-CSs1A90r.js");
4
+ //#region src/builtins/liveness-monitor/liveness-checks.ts
5
+ var NO_DEVICES = "liveness:no-devices";
6
+ var ALL_OFFLINE = "liveness:all-devices-offline";
7
+ var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
8
+ function minutes(ms) {
9
+ return Math.round(ms / 6e4);
10
+ }
11
+ /**
12
+ * Evaluate every invariant against one tick's facts.
13
+ *
14
+ * Ordered most-fundamental first, and it STOPS at "no devices": when the node
15
+ * has nothing, every camera is also silent, and reporting twelve consequences
16
+ * of one cause is how an operator ends up reading none of them.
17
+ */
18
+ function evaluateLiveness(facts) {
19
+ if (facts.deviceCount === 0) return [{
20
+ id: NO_DEVICES,
21
+ severity: "error",
22
+ title: "No devices",
23
+ message: "This node has no devices. Nothing can publish a stream, record, or detect. The persisted device ledger is either unreachable or empty — check the boot log for \"SettingsStore not initialized\" and for \"[worker-restore] no persisted devices\"."
24
+ }];
25
+ const findings = [];
26
+ if (facts.cameras.length > 0 && facts.cameras.every((c) => !c.online)) findings.push({
27
+ id: ALL_OFFLINE,
28
+ severity: "error",
29
+ title: "Every camera offline",
30
+ message: `All ${facts.cameras.length} cameras report offline. This is a provider or network fault, not a per-camera one.`
31
+ });
32
+ for (const cam of facts.cameras) {
33
+ if (cam.lastSegmentMs === null) continue;
34
+ if (!cam.online) continue;
35
+ const silentFor = facts.nowMs - cam.lastSegmentMs;
36
+ if (silentFor <= facts.staleAfterMs) continue;
37
+ findings.push({
38
+ id: `${NO_FOOTAGE_PREFIX}${cam.deviceId}`,
39
+ severity: "error",
40
+ title: "Recording stopped",
41
+ message: `Camera ${cam.deviceId} is online and was recording, but has produced no segment for ${minutes(silentFor)} minutes.`,
42
+ deviceId: cam.deviceId
43
+ });
44
+ }
45
+ return findings;
46
+ }
47
+ /** Every finding id this module can produce, for the recovery sweep. */
48
+ function isLivenessFindingId(id) {
49
+ return id === NO_DEVICES || id === ALL_OFFLINE || id.startsWith(NO_FOOTAGE_PREFIX);
50
+ }
51
+ //#endregion
52
+ //#region src/builtins/liveness-monitor/liveness-monitor.addon.ts
53
+ /**
54
+ * liveness-monitor — the node states, out loud, when it has stopped working.
55
+ *
56
+ * On 2026-08-03 the hub ran for four hours with zero devices, zero published
57
+ * streams and zero recordings. Three separate defects caused it, each silent at
58
+ * the point of failure, and the operator found out by opening the viewer. Every
59
+ * one of those defects has since been fixed and given a log line — but a log
60
+ * line per known bug does not cover the next one. This addon covers the class:
61
+ * it asserts what must be true of a working node and reports when it is not.
62
+ *
63
+ * It emits `SystemLivenessFailed` / `SystemLivenessRecovered` on the event bus
64
+ * with a STABLE finding id, exactly like the kernel's addon-health monitor, so
65
+ * AlertCenter raises one persistent alert per fault and dismisses it on
66
+ * recovery instead of producing a stream of them. The same lines go to the log
67
+ * at ERROR, which is what makes them alertable in Loki.
68
+ *
69
+ * Deliberately hub-only and deliberately read-only: it observes through
70
+ * `ctx.api` and changes nothing. A monitor that can act is a monitor that can
71
+ * cause the outage it was meant to report — this repo has already stopped a
72
+ * hub that way once (the remedial-action watcher, 2026-07-30).
73
+ */
74
+ /** Event source stamped on every emission — the addon that observed it. */
75
+ var LIVENESS_SOURCE = {
76
+ type: "addon",
77
+ id: "liveness-monitor"
78
+ };
79
+ /** How often the invariants are checked. */
80
+ var TICK_INTERVAL_MS = 5 * 6e4;
81
+ /**
82
+ * Grace period after boot before anything is reported.
83
+ *
84
+ * Every check would be true during startup — devices restore, providers
85
+ * connect, the first segment takes a minute — and a monitor that cries at every
86
+ * restart is one nobody reads. The kernel's addon-health monitor takes the same
87
+ * position for the same reason.
88
+ */
89
+ var BOOT_GRACE_MS = 6 * 6e4;
90
+ /** How long a recording camera may stay silent before it is a fault. */
91
+ var DEFAULT_STALE_AFTER_MS = 15 * 6e4;
92
+ /**
93
+ * How far back to look for "does this camera record at all". A camera with no
94
+ * footage in this window is treated as a non-recording camera and never
95
+ * reported — which is what keeps the check free of any configuration coupling.
96
+ */
97
+ var RECORDING_LOOKBACK_MS = 1440 * 6e4;
98
+ var LivenessMonitorAddon = class extends require_dist.BaseAddon {
99
+ tickTimer = null;
100
+ startedAtMs = 0;
101
+ /** Finding ids currently reported, so recovery can be detected and announced. */
102
+ active = /* @__PURE__ */ new Set();
103
+ constructor() {
104
+ super({});
105
+ }
106
+ async onInitialize() {
107
+ this.startedAtMs = Date.now();
108
+ this.tickTimer = setInterval(() => {
109
+ this.tick();
110
+ }, TICK_INTERVAL_MS);
111
+ this.tickTimer.unref?.();
112
+ this.ctx.logger.info("liveness-monitor started", { meta: {
113
+ tickIntervalMs: TICK_INTERVAL_MS,
114
+ bootGraceMs: BOOT_GRACE_MS
115
+ } });
116
+ return [];
117
+ }
118
+ async onShutdown() {
119
+ if (this.tickTimer) clearInterval(this.tickTimer);
120
+ this.tickTimer = null;
121
+ }
122
+ /**
123
+ * One evaluation. Public so a test or an operator-facing action can force it
124
+ * without waiting out the interval.
125
+ */
126
+ async tick() {
127
+ if (Date.now() - this.startedAtMs < BOOT_GRACE_MS) return [];
128
+ try {
129
+ const findings = evaluateLiveness(await this.gather());
130
+ this.report(findings);
131
+ return findings;
132
+ } catch (err) {
133
+ this.ctx.logger.error("liveness-monitor: check failed — node health is UNKNOWN this tick", { meta: { error: require_dist.errMsg(err) } });
134
+ return [];
135
+ }
136
+ }
137
+ /** Read the facts through the cap surface. Changes nothing. */
138
+ async gather() {
139
+ const nowMs = Date.now();
140
+ const staleAfterMs = this.config.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
141
+ const devices = await this.ctx.api.deviceManager.listAll.query({});
142
+ if (devices.length === 0) return {
143
+ nowMs,
144
+ deviceCount: 0,
145
+ cameras: [],
146
+ staleAfterMs
147
+ };
148
+ const cameraDevices = devices.filter((d) => d.isCamera === true);
149
+ const cameras = [];
150
+ for (const device of cameraDevices) cameras.push({
151
+ deviceId: device.id,
152
+ online: device.online === true,
153
+ lastSegmentMs: await this.lastSegmentMs(device.id, nowMs, staleAfterMs)
154
+ });
155
+ return {
156
+ nowMs,
157
+ deviceCount: devices.length,
158
+ cameras,
159
+ staleAfterMs
160
+ };
161
+ }
162
+ /**
163
+ * Newest segment for a camera, or null when it has produced none in a day.
164
+ *
165
+ * Two steps on purpose: the healthy case asks only for the recent window,
166
+ * which is a handful of ranges, and only a camera that looks silent pays for
167
+ * the 24-hour lookback that decides whether it records at all.
168
+ */
169
+ async lastSegmentMs(deviceId, nowMs, staleAfterMs) {
170
+ const recent = await this.ctx.api.recording.getAvailability.query({
171
+ deviceId,
172
+ fromMs: nowMs - staleAfterMs,
173
+ toMs: nowMs
174
+ });
175
+ if (recent.ranges.length > 0) return Math.max(...recent.ranges.map((r) => r.endMs));
176
+ const day = await this.ctx.api.recording.getAvailability.query({
177
+ deviceId,
178
+ fromMs: nowMs - RECORDING_LOOKBACK_MS,
179
+ toMs: nowMs
180
+ });
181
+ if (day.ranges.length === 0) return null;
182
+ return Math.max(...day.ranges.map((r) => r.endMs));
183
+ }
184
+ /** Announce new faults and recoveries; say nothing about an unchanged fault. */
185
+ report(findings) {
186
+ const current = new Set(findings.map((f) => f.id));
187
+ for (const finding of findings) {
188
+ if (this.active.has(finding.id)) continue;
189
+ this.active.add(finding.id);
190
+ this.ctx.logger.error(`liveness: ${finding.title} — ${finding.message}`, {
191
+ ...finding.deviceId !== void 0 ? { tags: { deviceId: finding.deviceId } } : {},
192
+ meta: {
193
+ findingId: finding.id,
194
+ severity: finding.severity
195
+ }
196
+ });
197
+ this.ctx.eventBus.emit(require_dist.createEvent(require_dist.EventCategory.SystemLivenessFailed, LIVENESS_SOURCE, {
198
+ findingId: finding.id,
199
+ severity: finding.severity,
200
+ title: finding.title,
201
+ message: finding.message,
202
+ ...finding.deviceId !== void 0 ? { deviceId: finding.deviceId } : {}
203
+ }));
204
+ }
205
+ for (const id of [...this.active]) {
206
+ if (current.has(id)) continue;
207
+ this.active.delete(id);
208
+ this.ctx.logger.info(`liveness: recovered — ${id}`, { meta: { findingId: id } });
209
+ this.ctx.eventBus.emit(require_dist.createEvent(require_dist.EventCategory.SystemLivenessRecovered, LIVENESS_SOURCE, { findingId: id }));
210
+ }
211
+ }
212
+ };
213
+ //#endregion
214
+ exports.LivenessMonitorAddon = LivenessMonitorAddon;
215
+ exports.evaluateLiveness = evaluateLiveness;
216
+ exports.isLivenessFindingId = isLivenessFindingId;
@@ -0,0 +1,212 @@
1
+ import { Mt as EventCategory, at as errMsg, ot as BaseAddon, yt as createEvent } from "../../dist-BL_FHWPP.mjs";
2
+ //#region src/builtins/liveness-monitor/liveness-checks.ts
3
+ var NO_DEVICES = "liveness:no-devices";
4
+ var ALL_OFFLINE = "liveness:all-devices-offline";
5
+ var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
6
+ function minutes(ms) {
7
+ return Math.round(ms / 6e4);
8
+ }
9
+ /**
10
+ * Evaluate every invariant against one tick's facts.
11
+ *
12
+ * Ordered most-fundamental first, and it STOPS at "no devices": when the node
13
+ * has nothing, every camera is also silent, and reporting twelve consequences
14
+ * of one cause is how an operator ends up reading none of them.
15
+ */
16
+ function evaluateLiveness(facts) {
17
+ if (facts.deviceCount === 0) return [{
18
+ id: NO_DEVICES,
19
+ severity: "error",
20
+ title: "No devices",
21
+ message: "This node has no devices. Nothing can publish a stream, record, or detect. The persisted device ledger is either unreachable or empty — check the boot log for \"SettingsStore not initialized\" and for \"[worker-restore] no persisted devices\"."
22
+ }];
23
+ const findings = [];
24
+ if (facts.cameras.length > 0 && facts.cameras.every((c) => !c.online)) findings.push({
25
+ id: ALL_OFFLINE,
26
+ severity: "error",
27
+ title: "Every camera offline",
28
+ message: `All ${facts.cameras.length} cameras report offline. This is a provider or network fault, not a per-camera one.`
29
+ });
30
+ for (const cam of facts.cameras) {
31
+ if (cam.lastSegmentMs === null) continue;
32
+ if (!cam.online) continue;
33
+ const silentFor = facts.nowMs - cam.lastSegmentMs;
34
+ if (silentFor <= facts.staleAfterMs) continue;
35
+ findings.push({
36
+ id: `${NO_FOOTAGE_PREFIX}${cam.deviceId}`,
37
+ severity: "error",
38
+ title: "Recording stopped",
39
+ message: `Camera ${cam.deviceId} is online and was recording, but has produced no segment for ${minutes(silentFor)} minutes.`,
40
+ deviceId: cam.deviceId
41
+ });
42
+ }
43
+ return findings;
44
+ }
45
+ /** Every finding id this module can produce, for the recovery sweep. */
46
+ function isLivenessFindingId(id) {
47
+ return id === NO_DEVICES || id === ALL_OFFLINE || id.startsWith(NO_FOOTAGE_PREFIX);
48
+ }
49
+ //#endregion
50
+ //#region src/builtins/liveness-monitor/liveness-monitor.addon.ts
51
+ /**
52
+ * liveness-monitor — the node states, out loud, when it has stopped working.
53
+ *
54
+ * On 2026-08-03 the hub ran for four hours with zero devices, zero published
55
+ * streams and zero recordings. Three separate defects caused it, each silent at
56
+ * the point of failure, and the operator found out by opening the viewer. Every
57
+ * one of those defects has since been fixed and given a log line — but a log
58
+ * line per known bug does not cover the next one. This addon covers the class:
59
+ * it asserts what must be true of a working node and reports when it is not.
60
+ *
61
+ * It emits `SystemLivenessFailed` / `SystemLivenessRecovered` on the event bus
62
+ * with a STABLE finding id, exactly like the kernel's addon-health monitor, so
63
+ * AlertCenter raises one persistent alert per fault and dismisses it on
64
+ * recovery instead of producing a stream of them. The same lines go to the log
65
+ * at ERROR, which is what makes them alertable in Loki.
66
+ *
67
+ * Deliberately hub-only and deliberately read-only: it observes through
68
+ * `ctx.api` and changes nothing. A monitor that can act is a monitor that can
69
+ * cause the outage it was meant to report — this repo has already stopped a
70
+ * hub that way once (the remedial-action watcher, 2026-07-30).
71
+ */
72
+ /** Event source stamped on every emission — the addon that observed it. */
73
+ var LIVENESS_SOURCE = {
74
+ type: "addon",
75
+ id: "liveness-monitor"
76
+ };
77
+ /** How often the invariants are checked. */
78
+ var TICK_INTERVAL_MS = 5 * 6e4;
79
+ /**
80
+ * Grace period after boot before anything is reported.
81
+ *
82
+ * Every check would be true during startup — devices restore, providers
83
+ * connect, the first segment takes a minute — and a monitor that cries at every
84
+ * restart is one nobody reads. The kernel's addon-health monitor takes the same
85
+ * position for the same reason.
86
+ */
87
+ var BOOT_GRACE_MS = 6 * 6e4;
88
+ /** How long a recording camera may stay silent before it is a fault. */
89
+ var DEFAULT_STALE_AFTER_MS = 15 * 6e4;
90
+ /**
91
+ * How far back to look for "does this camera record at all". A camera with no
92
+ * footage in this window is treated as a non-recording camera and never
93
+ * reported — which is what keeps the check free of any configuration coupling.
94
+ */
95
+ var RECORDING_LOOKBACK_MS = 1440 * 6e4;
96
+ var LivenessMonitorAddon = class extends BaseAddon {
97
+ tickTimer = null;
98
+ startedAtMs = 0;
99
+ /** Finding ids currently reported, so recovery can be detected and announced. */
100
+ active = /* @__PURE__ */ new Set();
101
+ constructor() {
102
+ super({});
103
+ }
104
+ async onInitialize() {
105
+ this.startedAtMs = Date.now();
106
+ this.tickTimer = setInterval(() => {
107
+ this.tick();
108
+ }, TICK_INTERVAL_MS);
109
+ this.tickTimer.unref?.();
110
+ this.ctx.logger.info("liveness-monitor started", { meta: {
111
+ tickIntervalMs: TICK_INTERVAL_MS,
112
+ bootGraceMs: BOOT_GRACE_MS
113
+ } });
114
+ return [];
115
+ }
116
+ async onShutdown() {
117
+ if (this.tickTimer) clearInterval(this.tickTimer);
118
+ this.tickTimer = null;
119
+ }
120
+ /**
121
+ * One evaluation. Public so a test or an operator-facing action can force it
122
+ * without waiting out the interval.
123
+ */
124
+ async tick() {
125
+ if (Date.now() - this.startedAtMs < BOOT_GRACE_MS) return [];
126
+ try {
127
+ const findings = evaluateLiveness(await this.gather());
128
+ this.report(findings);
129
+ return findings;
130
+ } catch (err) {
131
+ this.ctx.logger.error("liveness-monitor: check failed — node health is UNKNOWN this tick", { meta: { error: errMsg(err) } });
132
+ return [];
133
+ }
134
+ }
135
+ /** Read the facts through the cap surface. Changes nothing. */
136
+ async gather() {
137
+ const nowMs = Date.now();
138
+ const staleAfterMs = this.config.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
139
+ const devices = await this.ctx.api.deviceManager.listAll.query({});
140
+ if (devices.length === 0) return {
141
+ nowMs,
142
+ deviceCount: 0,
143
+ cameras: [],
144
+ staleAfterMs
145
+ };
146
+ const cameraDevices = devices.filter((d) => d.isCamera === true);
147
+ const cameras = [];
148
+ for (const device of cameraDevices) cameras.push({
149
+ deviceId: device.id,
150
+ online: device.online === true,
151
+ lastSegmentMs: await this.lastSegmentMs(device.id, nowMs, staleAfterMs)
152
+ });
153
+ return {
154
+ nowMs,
155
+ deviceCount: devices.length,
156
+ cameras,
157
+ staleAfterMs
158
+ };
159
+ }
160
+ /**
161
+ * Newest segment for a camera, or null when it has produced none in a day.
162
+ *
163
+ * Two steps on purpose: the healthy case asks only for the recent window,
164
+ * which is a handful of ranges, and only a camera that looks silent pays for
165
+ * the 24-hour lookback that decides whether it records at all.
166
+ */
167
+ async lastSegmentMs(deviceId, nowMs, staleAfterMs) {
168
+ const recent = await this.ctx.api.recording.getAvailability.query({
169
+ deviceId,
170
+ fromMs: nowMs - staleAfterMs,
171
+ toMs: nowMs
172
+ });
173
+ if (recent.ranges.length > 0) return Math.max(...recent.ranges.map((r) => r.endMs));
174
+ const day = await this.ctx.api.recording.getAvailability.query({
175
+ deviceId,
176
+ fromMs: nowMs - RECORDING_LOOKBACK_MS,
177
+ toMs: nowMs
178
+ });
179
+ if (day.ranges.length === 0) return null;
180
+ return Math.max(...day.ranges.map((r) => r.endMs));
181
+ }
182
+ /** Announce new faults and recoveries; say nothing about an unchanged fault. */
183
+ report(findings) {
184
+ const current = new Set(findings.map((f) => f.id));
185
+ for (const finding of findings) {
186
+ if (this.active.has(finding.id)) continue;
187
+ this.active.add(finding.id);
188
+ this.ctx.logger.error(`liveness: ${finding.title} — ${finding.message}`, {
189
+ ...finding.deviceId !== void 0 ? { tags: { deviceId: finding.deviceId } } : {},
190
+ meta: {
191
+ findingId: finding.id,
192
+ severity: finding.severity
193
+ }
194
+ });
195
+ this.ctx.eventBus.emit(createEvent(EventCategory.SystemLivenessFailed, LIVENESS_SOURCE, {
196
+ findingId: finding.id,
197
+ severity: finding.severity,
198
+ title: finding.title,
199
+ message: finding.message,
200
+ ...finding.deviceId !== void 0 ? { deviceId: finding.deviceId } : {}
201
+ }));
202
+ }
203
+ for (const id of [...this.active]) {
204
+ if (current.has(id)) continue;
205
+ this.active.delete(id);
206
+ this.ctx.logger.info(`liveness: recovered — ${id}`, { meta: { findingId: id } });
207
+ this.ctx.eventBus.emit(createEvent(EventCategory.SystemLivenessRecovered, LIVENESS_SOURCE, { findingId: id }));
208
+ }
209
+ }
210
+ };
211
+ //#endregion
212
+ export { LivenessMonitorAddon, isLivenessFindingId as n, evaluateLiveness as t };
@@ -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-VIwdvws5.js");
6
+ const require_dist = require("../../dist-CSs1A90r.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  node_crypto = require_chunk.__toESM(node_crypto);
9
9
  let crypto$1 = require("crypto");
@@ -1,5 +1,5 @@
1
1
  import { i as __require, o as __toESM, t as __commonJSMin } from "../../chunk-CNf5ZN-e.mjs";
2
- import { f as UserRecordSchema, ot as BaseAddon, r as ApiKeyRecordSchema, rt as userManagementCapability, u as ScopedTokenSchema, v as authProviderCapability } from "../../dist-Dr_P0DOB.mjs";
2
+ import { f as UserRecordSchema, ot as BaseAddon, r as ApiKeyRecordSchema, rt as userManagementCapability, u as ScopedTokenSchema, v as authProviderCapability } from "../../dist-BL_FHWPP.mjs";
3
3
  import * as crypto$1 from "node:crypto";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import nodeCrypto from "crypto";
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-VIwdvws5.js");
3
+ const require_dist = require("../../dist-CSs1A90r.js");
4
4
  let node_os = require("node:os");
5
5
  node_os = require_chunk.__toESM(node_os);
6
6
  //#region src/builtins/local-network/local-network.addon.ts
@@ -1,4 +1,4 @@
1
- import { Mt as EventCategory, ot as BaseAddon, z as localNetworkCapability } from "../../dist-Dr_P0DOB.mjs";
1
+ import { Mt as EventCategory, ot as BaseAddon, z as localNetworkCapability } from "../../dist-BL_FHWPP.mjs";
2
2
  import * as os from "node:os";
3
3
  //#region src/builtins/local-network/local-network.addon.ts
4
4
  /**
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-VIwdvws5.js");
6
+ const require_dist = require("../../dist-CSs1A90r.js");
7
7
  //#region src/builtins/loki-logging/loki-payload.ts
8
8
  /**
9
9
  * Loki rejects a label name that is not a valid Prometheus label
@@ -1,4 +1,4 @@
1
- import { B as logDestinationCapability, ot as BaseAddon } from "../../dist-Dr_P0DOB.mjs";
1
+ import { B as logDestinationCapability, ot as BaseAddon } from "../../dist-BL_FHWPP.mjs";
2
2
  //#region src/builtins/loki-logging/loki-payload.ts
3
3
  /**
4
4
  * Loki rejects a label name that is not a valid Prometheus label
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-VIwdvws5.js");
6
+ const require_dist = require("../../dist-CSs1A90r.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_chunk.__toESM(node_fs);
9
9
  let node_child_process = require("node:child_process");
@@ -1,4 +1,4 @@
1
- import { Mt as EventCategory, U as metricsProviderCapability, ot as BaseAddon, yt as createEvent } from "../../dist-Dr_P0DOB.mjs";
1
+ import { Mt as EventCategory, U as metricsProviderCapability, ot as BaseAddon, yt as createEvent } from "../../dist-BL_FHWPP.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import { execFile, execFileSync } from "node:child_process";
4
4
  import { promisify } from "node:util";
@@ -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-VIwdvws5.js");
6
+ const require_dist = require("../../dist-CSs1A90r.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_chunk.__toESM(node_fs);
9
9
  let node_child_process = require("node:child_process");
@@ -1,5 +1,5 @@
1
1
  import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
2
- import { E as enumerateInferenceDevices, Mt as EventCategory, Y as scoreRuntimes, at as errMsg, ot as BaseAddon, q as platformProbeCapability, xt as emitReadiness } from "../../dist-Dr_P0DOB.mjs";
2
+ import { E as enumerateInferenceDevices, Mt as EventCategory, Y as scoreRuntimes, at as errMsg, ot as BaseAddon, q as platformProbeCapability, xt as emitReadiness } from "../../dist-BL_FHWPP.mjs";
3
3
  import * as fs from "node:fs";
4
4
  import { execFile } from "node:child_process";
5
5
  import { promisify } from "node:util";
@@ -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-VIwdvws5.js");
6
+ const require_dist = require("../../dist-CSs1A90r.js");
7
7
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
8
8
  /**
9
9
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -1,4 +1,4 @@
1
- import { Mt as EventCategory, ot as BaseAddon } from "../../dist-Dr_P0DOB.mjs";
1
+ import { Mt as EventCategory, ot as BaseAddon } from "../../dist-BL_FHWPP.mjs";
2
2
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
3
3
  /**
4
4
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-VIwdvws5.js");
6
+ const require_dist = require("../../dist-CSs1A90r.js");
7
7
  let node_child_process = require("node:child_process");
8
8
  //#region src/builtins/snapshot/snapshot-coalescing.ts
9
9
  /**
@@ -1,4 +1,4 @@
1
- import { Q as snapshotCapability, Tt as nodePin, at as errMsg, ft as DeviceType, i as BatteryStatusSchema, ot as BaseAddon, tt as streamQualityLabel, ut as DeviceFeature } from "../../dist-Dr_P0DOB.mjs";
1
+ import { Q as snapshotCapability, Tt as nodePin, at as errMsg, ft as DeviceType, i as BatteryStatusSchema, ot as BaseAddon, tt as streamQualityLabel, ut as DeviceFeature } from "../../dist-BL_FHWPP.mjs";
2
2
  import { execFile } from "node:child_process";
3
3
  //#region src/builtins/snapshot/snapshot-coalescing.ts
4
4
  /**
@@ -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-VIwdvws5.js");
6
+ const require_dist = require("../../dist-CSs1A90r.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_chunk.__toESM(node_fs);
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { et as storageProviderCapability, j as filesystemBrowseCapability, ot as BaseAddon } from "../../dist-Dr_P0DOB.mjs";
1
+ import { et as storageProviderCapability, j as filesystemBrowseCapability, ot as BaseAddon } from "../../dist-BL_FHWPP.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import * as path$1 from "node:path";
4
4
  import { basename, dirname, join, resolve, sep } from "node:path";
@@ -1,7 +1,7 @@
1
- import { Integration, PersistedDevice, CreateIntegrationInput, CreateDeviceInput, IIntegrationRegistry, ISettingsBackend } from '@camstack/types';
1
+ import { Integration, PersistedDevice, CreateIntegrationInput, CreateDeviceInput, IIntegrationRegistry, ISettingsStoreProvider } from '@camstack/types';
2
2
  export declare class IntegrationRegistry implements IIntegrationRegistry {
3
- private readonly backend;
4
- constructor(backend: ISettingsBackend);
3
+ private readonly store;
4
+ constructor(backend: ISettingsStoreProvider);
5
5
  initialize(): Promise<void>;
6
6
  createIntegration(input: CreateIntegrationInput): Promise<Integration>;
7
7
  getIntegration(id: string): Promise<Integration | null>;
@@ -1,5 +1,5 @@
1
1
  import { default as Database } from 'better-sqlite3';
2
- import { DataStoreEngineInfo, ISettingsBackend, SettingsRecord, TableSchema, TableQueryOptions, SettingsGetInput, SettingsSetInput, SettingsQueryInput, SettingsInsertInput, SettingsUpdateInput, SettingsDeleteInput, SettingsCountInput, SettingsIsEmptyInput, SettingsHistogramInput, HistogramBucket, CollectionColumn, CollectionIndex } from '@camstack/types';
2
+ import { DataStoreEngineInfo, ISettingsBackend, SettingsRecord, SettingsGetInput, SettingsSetInput, SettingsQueryInput, SettingsInsertInput, SettingsUpdateInput, SettingsDeleteInput, SettingsCountInput, SettingsIsEmptyInput, SettingsHistogramInput, HistogramBucket, CollectionColumn, CollectionIndex } from '@camstack/types';
3
3
  import { MutationFilterInput } from './filter-compiler.js';
4
4
  /** Input for {@link SqliteSettingsBackend.deleteWhere}. */
5
5
  interface SettingsDeleteWhereInput {
@@ -139,13 +139,11 @@ export declare class SqliteSettingsBackend implements ISettingsBackend {
139
139
  }): Promise<void>;
140
140
  /** Serialise per-column values for SQL binding: objects → JSON, booleans → 0/1. */
141
141
  private serializeColumnValue;
142
- ensureTable(table: string, schema: TableSchema): Promise<void>;
143
- tableInsert(table: string, row: Record<string, unknown>): Promise<void>;
144
- tableUpdate(table: string, filter: Record<string, unknown>, updates: Record<string, unknown>): Promise<number>;
145
- tableDelete(table: string, filter: Record<string, unknown>): Promise<number>;
146
- tableQuery(table: string, options?: TableQueryOptions): Promise<readonly Record<string, unknown>[]>;
147
- tableGet(table: string, filter: Record<string, unknown>): Promise<Record<string, unknown> | null>;
148
- tableCount(table: string, filter?: Record<string, unknown>): Promise<number>;
142
+ private ensureTable;
143
+ private tableInsert;
144
+ private tableUpdate;
145
+ private tableDelete;
146
+ private tableCount;
149
147
  private buildWhere;
150
148
  }
151
149
  export default SqliteSettingsBackend;
@@ -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-VIwdvws5.js");
6
+ const require_dist = require("../../dist-CSs1A90r.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let better_sqlite3 = require("better-sqlite3");
9
9
  better_sqlite3 = require_chunk.__toESM(better_sqlite3);
@@ -536,7 +536,8 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
536
536
  type: c.type === "JSON" ? "TEXT" : c.type === "BOOLEAN" ? "INTEGER" : c.type,
537
537
  ...c.primaryKey !== void 0 ? { primaryKey: c.primaryKey } : {},
538
538
  ...c.notNull !== void 0 ? { notNull: c.notNull } : {},
539
- ...c.unique !== void 0 ? { unique: c.unique } : {}
539
+ ...c.unique !== void 0 ? { unique: c.unique } : {},
540
+ ...c.defaultValue !== void 0 ? { defaultValue: c.defaultValue } : {}
540
541
  })),
541
542
  ...input.indexes ? { indexes: input.indexes.map((i) => ({
542
543
  name: i.name,
@@ -621,29 +622,6 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
621
622
  const { whereSql, whereValues } = this.buildWhere(filter);
622
623
  return this.getDb().prepare(`DELETE FROM "${table}"${whereSql}`).run(...whereValues).changes;
623
624
  }
624
- async tableQuery(table, options) {
625
- let sql = `SELECT * FROM "${table}"`;
626
- const values = [];
627
- if (options?.where) {
628
- const { whereSql, whereValues } = this.buildWhere(options.where);
629
- sql += whereSql;
630
- values.push(...whereValues);
631
- }
632
- if (options?.orderBy) sql += ` ORDER BY "${options.orderBy.field}" ${options.orderBy.direction === "desc" ? "DESC" : "ASC"}`;
633
- if (options?.limit !== void 0) {
634
- sql += ` LIMIT ?`;
635
- values.push(options.limit);
636
- }
637
- if (options?.offset !== void 0) {
638
- sql += ` OFFSET ?`;
639
- values.push(options.offset);
640
- }
641
- return this.getDb().prepare(sql).all(...values).flatMap((r) => require_dist.asJsonObject(r) ? [require_dist.asJsonObject(r)] : []);
642
- }
643
- async tableGet(table, filter) {
644
- const { whereSql, whereValues } = this.buildWhere(filter);
645
- return require_dist.asJsonObject(this.getDb().prepare(`SELECT * FROM "${table}"${whereSql} LIMIT 1`).get(...whereValues));
646
- }
647
625
  async tableCount(table, filter) {
648
626
  let sql = `SELECT COUNT(*) as count FROM "${table}"`;
649
627
  const values = [];