@camstack/system 1.2.471 → 1.2.472
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.
- package/dist/addon-runner.js +202 -8
- package/dist/addon-runner.mjs +203 -9
- package/dist/addon-utils.mjs +1 -1
- package/dist/{agent-https-DHBvFZJS.mjs → agent-https-JhvY1axF.mjs} +1 -1
- package/dist/{agent-https-bH6bKDeK.js → agent-https-bnyFRK7Y.js} +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +4 -4
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +5 -5
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/autotrack/index.js +1 -1
- package/dist/builtins/autotrack/index.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +5 -5
- package/dist/builtins/calendars/calendars.addon.js +1 -1
- package/dist/builtins/calendars/calendars.addon.mjs +1 -1
- package/dist/builtins/camera-grid/index.js +1 -1
- package/dist/builtins/camera-grid/index.mjs +1 -1
- package/dist/builtins/composer/composer.addon.js +2 -2
- package/dist/builtins/composer/composer.addon.mjs +2 -2
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
- package/dist/builtins/device-history/device-history.addon.js +1 -1
- package/dist/builtins/device-history/device-history.addon.mjs +1 -1
- package/dist/builtins/device-manager/device-manager.addon.js +1 -1
- package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/keychain/index.js +3 -3
- package/dist/builtins/keychain/index.mjs +3 -3
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.d.ts +9 -25
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +240 -39
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +237 -38
- package/dist/builtins/liveness-monitor/liveness-reporter.d.ts +28 -0
- package/dist/builtins/liveness-monitor/storage-root-access.d.ts +53 -0
- package/dist/builtins/local-auth/local-auth.addon.js +2 -2
- package/dist/builtins/local-auth/local-auth.addon.mjs +5 -5
- package/dist/builtins/local-network/local-network.addon.js +2 -2
- package/dist/builtins/local-network/local-network.addon.mjs +2 -2
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +10 -10
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +8 -8
- package/dist/builtins/record-entries/index.js +1 -1
- package/dist/builtins/record-entries/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +20 -20
- package/dist/builtins/sqlite-storage/index.js +1 -1
- package/dist/builtins/sqlite-storage/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +2 -2
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +2 -2
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-config/system-config.addon.js +3 -3
- package/dist/builtins/system-config/system-config.addon.mjs +115 -115
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{composition-sources-DRw_vBKH.mjs → composition-sources-BY6rFuAE.mjs} +1 -1
- package/dist/{composition-sources-B5uJZXZ1.js → composition-sources-D3h-tNoJ.js} +1 -1
- package/dist/{constants-CToE4OFp.js → constants-CqbO6e9i.js} +1 -1
- package/dist/{constants-5lA8Z9d8.mjs → constants-zBiEodEM.mjs} +1 -1
- package/dist/{coral-accelerators-BW3OcrLD.mjs → coral-accelerators-DBlgWB8k.mjs} +6 -6
- package/dist/{custom-action-registry-BGYDjTqY.mjs → custom-action-registry-DZSCLBuT.mjs} +37 -37
- package/dist/{device-ca-URrs1lxK.mjs → device-ca-BWTWWBcc.mjs} +1 -1
- package/dist/{device-ca-nOCYVFS4.js → device-ca-CfiIg3a9.js} +1 -1
- package/dist/{dist-5XUG7iYJ.mjs → dist-CdFkkmXW.mjs} +100 -254
- package/dist/{dist-BjcWEfRy.js → dist-DX5Fu3mV.js} +100 -254
- package/dist/file-host-worker.js +1 -1
- package/dist/file-host-worker.mjs +1 -1
- package/dist/{hub-mdns-ciao-vEWhJ_SD.js → hub-mdns-ciao-C174UDzO.js} +2 -2
- package/dist/{hub-mdns-ciao-zD3JojB_.mjs → hub-mdns-ciao-CYp1Gipm.mjs} +2 -2
- package/dist/index.js +234 -16
- package/dist/index.mjs +453 -235
- package/dist/kernel/hwaccel/hwaccel-resolver.d.ts +24 -4
- package/dist/kernel/hwaccel/vaapi-init-probe.d.ts +29 -0
- package/dist/kernel/moleculer/runner-process-group.d.ts +4 -0
- package/dist/{restore-archive-B64m2nAj.js → restore-archive-B2jIFfu_.js} +1 -1
- package/dist/{restore-archive-CN1pJTRn.mjs → restore-archive-BX0gaKtG.mjs} +94 -94
- package/dist/{wal-maintenance-BNXVX-ot.js → wal-maintenance-Dxqd5igw.js} +0 -0
- package/dist/{wal-maintenance-Bl2I-ZcR.mjs → wal-maintenance-KHzJXlF2.mjs} +0 -0
- package/package.json +1 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Da as EventCategory, Er as storageOccupancyCapability, Sa as toNodeId, Tt as backupCapability, Vn as mayWriteToLocation, ca as nodePin, ei as BaseAddon } from "../../dist-
|
|
1
|
+
import { Da as EventCategory, Er as storageOccupancyCapability, Sa as toNodeId, Tt as backupCapability, Vn as mayWriteToLocation, ca as nodePin, ei as BaseAddon } from "../../dist-CdFkkmXW.mjs";
|
|
2
2
|
import { t as require_read_all_rows } from "../../read-all-rows-TaPqdWNF.mjs";
|
|
3
|
-
import { _ as backupRecordStoresOf, a as BackupSnapshotRefusedError, d as ENCRYPTED_BACKUP_SUFFIX, f as generateBackupKeyPair, g as backupOrchestratorRecordKinds, i as BackupCancelledError, l as BackupKeyFormatError, m as parseBackupSecretKey, n as precheckRestoreKey, o as DEFAULT_BACKUP_DATABASES, p as parseBackupPublicKey, r as prepareRestoreArchive, s as SystemBackupService } from "../../restore-archive-
|
|
3
|
+
import { _ as backupRecordStoresOf, a as BackupSnapshotRefusedError, d as ENCRYPTED_BACKUP_SUFFIX, f as generateBackupKeyPair, g as backupOrchestratorRecordKinds, i as BackupCancelledError, l as BackupKeyFormatError, m as parseBackupSecretKey, n as precheckRestoreKey, o as DEFAULT_BACKUP_DATABASES, p as parseBackupPublicKey, r as prepareRestoreArchive, s as SystemBackupService } from "../../restore-archive-BX0gaKtG.mjs";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import * as fsp from "node:fs/promises";
|
|
6
6
|
import * as path$1 from "node:path";
|
|
7
|
-
import * as fs from "node:fs";
|
|
7
|
+
import * as fs$1 from "node:fs";
|
|
8
8
|
//#region src/builtins/backup-orchestrator/backup-databases.ts
|
|
9
9
|
/** Stable id of the seeded weekly schedule. */
|
|
10
10
|
var WEEKLY_SCHEDULE_ID = "weekly-events-history";
|
|
@@ -1018,7 +1018,7 @@ async function downloadArchiveToCache(input) {
|
|
|
1018
1018
|
location: input.locationId,
|
|
1019
1019
|
relativePath: input.filename
|
|
1020
1020
|
});
|
|
1021
|
-
const writeStream = fs.createWriteStream(input.cachePath);
|
|
1021
|
+
const writeStream = fs$1.createWriteStream(input.cachePath);
|
|
1022
1022
|
let offset = 0;
|
|
1023
1023
|
try {
|
|
1024
1024
|
while (offset < sizeBytes) {
|
|
@@ -2502,7 +2502,7 @@ var BackupOrchestratorAddon = class extends BaseAddon {
|
|
|
2502
2502
|
});
|
|
2503
2503
|
try {
|
|
2504
2504
|
let offset = 0;
|
|
2505
|
-
const stream = fs.createReadStream(input.cachePath, { highWaterMark: UPLOAD_CHUNK_BYTES });
|
|
2505
|
+
const stream = fs$1.createReadStream(input.cachePath, { highWaterMark: UPLOAD_CHUNK_BYTES });
|
|
2506
2506
|
for await (const rawChunk of stream) {
|
|
2507
2507
|
if (input.signal?.aborted) {
|
|
2508
2508
|
stream.destroy();
|
|
@@ -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-
|
|
6
|
+
const require_dist = require("../../dist-DX5Fu3mV.js");
|
|
7
7
|
const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
|
|
8
8
|
let zod = require("zod");
|
|
9
9
|
//#region src/builtins/calendars/calendar-color.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ki as defineRecordKinds, Mt as calendarsCapability, ei as BaseAddon, g as CalendarPreferencesSchema } from "../../dist-
|
|
1
|
+
import { Ki as defineRecordKinds, Mt as calendarsCapability, ei as BaseAddon, g as CalendarPreferencesSchema } from "../../dist-CdFkkmXW.mjs";
|
|
2
2
|
import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
//#region src/builtins/calendars/calendar-color.ts
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-DX5Fu3mV.js");
|
|
7
7
|
let zod = require("zod");
|
|
8
8
|
let _camstack_types_node = require("@camstack/types/node");
|
|
9
9
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $r as errMsg, Da as EventCategory, Gn as motionCapability, Hn as memoizePinnedFfmpeg, N as DeclaredDevices, Nt as cameraGridLayoutCapability, Or as streamCatalogCapability, Qi as hydrateSchema, Qr as DeviceType, T as DERIVED_CAMERA_SILENCED_CAP_NAMES, Ui as createEvent, X as MOTION_CLOSE_AFTER_MS, Xr as DeviceFeature, a as BaseDevice, ei as BaseAddon, qt as declarationOwnerNodeId, xr as snapshotCapability, zr as buildFfmpegArgs } from "../../dist-
|
|
1
|
+
import { $r as errMsg, Da as EventCategory, Gn as motionCapability, Hn as memoizePinnedFfmpeg, N as DeclaredDevices, Nt as cameraGridLayoutCapability, Or as streamCatalogCapability, Qi as hydrateSchema, Qr as DeviceType, T as DERIVED_CAMERA_SILENCED_CAP_NAMES, Ui as createEvent, X as MOTION_CLOSE_AFTER_MS, Xr as DeviceFeature, a as BaseDevice, ei as BaseAddon, qt as declarationOwnerNodeId, xr as snapshotCapability, zr as buildFfmpegArgs } from "../../dist-CdFkkmXW.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { FfmpegProcess } from "@camstack/types/node";
|
|
4
4
|
import { join } from "node:path";
|
|
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
7
|
-
const require_composition_sources = require("../../composition-sources-
|
|
6
|
+
const require_dist = require("../../dist-DX5Fu3mV.js");
|
|
7
|
+
const require_composition_sources = require("../../composition-sources-D3h-tNoJ.js");
|
|
8
8
|
let zod = require("zod");
|
|
9
9
|
//#region src/builtins/shared/device-state-changed.ts
|
|
10
10
|
/** Structural narrowing helper for untrusted bus payloads. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $r as errMsg, Br as cloneStatefulExpressionMemory, Da as EventCategory, Gr as compositionSliceKey, Hr as composedDeviceStableId, It as composerCapability, Jr as disarmRoseMemory, Kr as compositionSourceKey, Lt as compositionFieldKey, N as DeclaredDevices, Ot as buildCapabilityLookup, Qn as planComposition, Qr as DeviceType, Rr as COMPOSER_ADDON_ID, Ui as createEvent, Ur as compositionFeatureEnabled, Vr as composedDeviceRef, Wr as compositionItemFieldPath, Yr as toExpressionValue, a as BaseDevice, cn as evaluateCompositionField, d as COMPOSED_ITEM_FIXED_FIELDS, dn as expressionContainsBuiltinCall, ei as BaseAddon, qr as createStatefulExpressionMemory, r as ALL_CAPABILITY_DEFINITIONS } from "../../dist-
|
|
2
|
-
import { n as normalizedNativeOwner } from "../../composition-sources-
|
|
1
|
+
import { $r as errMsg, Br as cloneStatefulExpressionMemory, Da as EventCategory, Gr as compositionSliceKey, Hr as composedDeviceStableId, It as composerCapability, Jr as disarmRoseMemory, Kr as compositionSourceKey, Lt as compositionFieldKey, N as DeclaredDevices, Ot as buildCapabilityLookup, Qn as planComposition, Qr as DeviceType, Rr as COMPOSER_ADDON_ID, Ui as createEvent, Ur as compositionFeatureEnabled, Vr as composedDeviceRef, Wr as compositionItemFieldPath, Yr as toExpressionValue, a as BaseDevice, cn as evaluateCompositionField, d as COMPOSED_ITEM_FIXED_FIELDS, dn as expressionContainsBuiltinCall, ei as BaseAddon, qr as createStatefulExpressionMemory, r as ALL_CAPABILITY_DEFINITIONS } from "../../dist-CdFkkmXW.mjs";
|
|
2
|
+
import { n as normalizedNativeOwner } from "../../composition-sources-BY6rFuAE.mjs";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
//#region src/builtins/shared/device-state-changed.ts
|
|
5
5
|
/** Structural narrowing helper for untrusted bus payloads. */
|
|
@@ -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-
|
|
6
|
+
const require_dist = require("../../dist-DX5Fu3mV.js");
|
|
7
7
|
const require_formatter = require("../../formatter-DqAKDlvN.js");
|
|
8
8
|
//#region src/builtins/console-logging/console-destination.ts
|
|
9
9
|
var LEVEL_RANK = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ln as logDestinationCapability, ei as BaseAddon } from "../../dist-
|
|
1
|
+
import { Ln as logDestinationCapability, ei as BaseAddon } from "../../dist-CdFkkmXW.mjs";
|
|
2
2
|
import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
|
|
3
3
|
//#region src/builtins/console-logging/console-destination.ts
|
|
4
4
|
var LEVEL_RANK = {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
3
|
-
const require_dist = require("../../dist-
|
|
3
|
+
const require_dist = require("../../dist-DX5Fu3mV.js");
|
|
4
4
|
const require_read_all_rows$1 = require("../../read-all-rows-CVoO1ONM.js");
|
|
5
|
-
const require_composition_sources = require("../../composition-sources-
|
|
5
|
+
const require_composition_sources = require("../../composition-sources-D3h-tNoJ.js");
|
|
6
6
|
const require_prefix_range = require("../../prefix-range-D_KueXbQ.js");
|
|
7
7
|
const require_prefix_walk = require("../../prefix-walk-Dv6MLZlM.js");
|
|
8
8
|
let zod = require("zod");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { $r as errMsg, Bt as coreBlockComposition, Ht as coreBlockSourceKind, Ki as defineRecordKinds, Kn as normalizeCoreBlockWrite, Kr as compositionSourceKey, Mr as validateComposition, N as DeclaredDevices, Ot as buildCapabilityLookup, Qi as hydrateSchema, Qn as planComposition, Rt as compositionSourceRefs, Ut as coreBlocksCapability, Vi as collectSecretMarkerRefs, Vr as composedDeviceRef, Vt as coreBlockIdFromAddonId, Zt as describeComposableCaps, _a as resolveSecretMarkers, aa as isSecretRefOwnedBy, ar as replacedFieldKeys, ba as secretTreeFromSchema, cr as resolveCoreBlockEntry, ei as BaseAddon, f as COMPOSER_RUNNER_ID, lr as resolveCoreBlockFiles, m as CORE_BLOCK_ADDON_PREFIX, p as CORE_BLOCKS_ADDON_ID, r as ALL_CAPABILITY_DEFINITIONS, ra as isSecretMarker, v as CoreBlockSchema, wa as transformSecretWrite, zt as coreBlockAddonId } from "../../dist-
|
|
1
|
+
import { $r as errMsg, Bt as coreBlockComposition, Ht as coreBlockSourceKind, Ki as defineRecordKinds, Kn as normalizeCoreBlockWrite, Kr as compositionSourceKey, Mr as validateComposition, N as DeclaredDevices, Ot as buildCapabilityLookup, Qi as hydrateSchema, Qn as planComposition, Rt as compositionSourceRefs, Ut as coreBlocksCapability, Vi as collectSecretMarkerRefs, Vr as composedDeviceRef, Vt as coreBlockIdFromAddonId, Zt as describeComposableCaps, _a as resolveSecretMarkers, aa as isSecretRefOwnedBy, ar as replacedFieldKeys, ba as secretTreeFromSchema, cr as resolveCoreBlockEntry, ei as BaseAddon, f as COMPOSER_RUNNER_ID, lr as resolveCoreBlockFiles, m as CORE_BLOCK_ADDON_PREFIX, p as CORE_BLOCKS_ADDON_ID, r as ALL_CAPABILITY_DEFINITIONS, ra as isSecretMarker, v as CoreBlockSchema, wa as transformSecretWrite, zt as coreBlockAddonId } from "../../dist-CdFkkmXW.mjs";
|
|
2
2
|
import { t as require_read_all_rows } from "../../read-all-rows-TaPqdWNF.mjs";
|
|
3
|
-
import { t as describeCompositionSources } from "../../composition-sources-
|
|
3
|
+
import { t as describeCompositionSources } from "../../composition-sources-BY6rFuAE.mjs";
|
|
4
4
|
import { t as prefixRange } from "../../prefix-range-Dl0znowN.mjs";
|
|
5
5
|
import { n as unwrapStoredValue, t as loadPrefixed } from "../../prefix-walk-DAOBSyH1.mjs";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
@@ -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-
|
|
6
|
+
const require_dist = require("../../dist-DX5Fu3mV.js");
|
|
7
7
|
//#region src/builtins/device-history/time/bucket-time.ts
|
|
8
8
|
/**
|
|
9
9
|
* Bucket arithmetic for the three rollup tiers (D850, plan §4/§5.5).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as HISTORY_ITEM_CATALOG, Ct as armRemovalReconcile, D as DEVICE_HISTORY_ENERGY_KEY, Da as EventCategory, Ji as emitReadiness, Qr as DeviceType, Zi as historyItemFieldPath, ei as BaseAddon, en as deviceHistoryCapability, li as HISTORY_DEFAULT_MIN_INTERVAL_MS, nn as deviceRemovalHookCapability, z as HISTORY_CATALOG } from "../../dist-
|
|
1
|
+
import { B as HISTORY_ITEM_CATALOG, Ct as armRemovalReconcile, D as DEVICE_HISTORY_ENERGY_KEY, Da as EventCategory, Ji as emitReadiness, Qr as DeviceType, Zi as historyItemFieldPath, ei as BaseAddon, en as deviceHistoryCapability, li as HISTORY_DEFAULT_MIN_INTERVAL_MS, nn as deviceRemovalHookCapability, z as HISTORY_CATALOG } from "../../dist-CdFkkmXW.mjs";
|
|
2
2
|
//#region src/builtins/device-history/time/bucket-time.ts
|
|
3
3
|
/**
|
|
4
4
|
* Bucket arithmetic for the three rollup tiers (D850, plan §4/§5.5).
|
|
@@ -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-
|
|
6
|
+
const require_dist = require("../../dist-DX5Fu3mV.js");
|
|
7
7
|
const require_read_all_rows$1 = require("../../read-all-rows-CVoO1ONM.js");
|
|
8
8
|
const require_prefix_range = require("../../prefix-range-D_KueXbQ.js");
|
|
9
9
|
const require_prefix_walk = require("../../prefix-walk-Dv6MLZlM.js");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $i as isDeviceConfigCap, $r as errMsg, $t as deviceExtensionCapability, Ca as toSecretFieldView, Da as EventCategory, Di as SECRET_CLEAR, F as DeviceTombstoneSchema, Hi as collectZodSecretPaths, In as locationIconKey, Ki as defineRecordKinds, L as FieldClaimModeSchema, Mi as WELL_KNOWN_TAB_MAP, Mn as legacyAutoLinkIds, P as DeviceStatusSchema, Pt as canonicalIconKey, Qr as DeviceType, R as FieldClaimSchema, Ta as userError, Wt as createDeviceExtensionProvider, Xn as parseStreamParamsFormPatch, Xr as DeviceFeature, Y as MINUTES_PER_DAY, Yt as defineExtensionSlotProvider, Zr as DeviceRole, _a as resolveSecretMarkers, br as siteZoneKey, ei as BaseAddon, gn as formatMinutes, ia as isSecretMarkerLike, in as deviceStatusCapability, jt as buildStreamParamsConfigSchema, l as CAP_NAMES_WITH_STATUS, mr as runtimeStatePolicyFor, na as isSecretClear, on as enumerateItemArrayFields, ot as STREAM_PROFILE_META, qn as normalizeUnit, r as ALL_CAPABILITY_DEFINITIONS, ra as isSecretMarker, rn as deviceStateCapability, s as CAMERA_SWITCH_ORDER, sn as enumerateSchemaFields, ta as isKeychainStoredField, tn as deviceManagerCapability, un as expandContainer, w as DAY_NAMES, wa as transformSecretWrite, xa as sleep$1, ya as secretTreeFromPaths } from "../../dist-
|
|
1
|
+
import { $i as isDeviceConfigCap, $r as errMsg, $t as deviceExtensionCapability, Ca as toSecretFieldView, Da as EventCategory, Di as SECRET_CLEAR, F as DeviceTombstoneSchema, Hi as collectZodSecretPaths, In as locationIconKey, Ki as defineRecordKinds, L as FieldClaimModeSchema, Mi as WELL_KNOWN_TAB_MAP, Mn as legacyAutoLinkIds, P as DeviceStatusSchema, Pt as canonicalIconKey, Qr as DeviceType, R as FieldClaimSchema, Ta as userError, Wt as createDeviceExtensionProvider, Xn as parseStreamParamsFormPatch, Xr as DeviceFeature, Y as MINUTES_PER_DAY, Yt as defineExtensionSlotProvider, Zr as DeviceRole, _a as resolveSecretMarkers, br as siteZoneKey, ei as BaseAddon, gn as formatMinutes, ia as isSecretMarkerLike, in as deviceStatusCapability, jt as buildStreamParamsConfigSchema, l as CAP_NAMES_WITH_STATUS, mr as runtimeStatePolicyFor, na as isSecretClear, on as enumerateItemArrayFields, ot as STREAM_PROFILE_META, qn as normalizeUnit, r as ALL_CAPABILITY_DEFINITIONS, ra as isSecretMarker, rn as deviceStateCapability, s as CAMERA_SWITCH_ORDER, sn as enumerateSchemaFields, ta as isKeychainStoredField, tn as deviceManagerCapability, un as expandContainer, w as DAY_NAMES, wa as transformSecretWrite, xa as sleep$1, ya as secretTreeFromPaths } from "../../dist-CdFkkmXW.mjs";
|
|
2
2
|
import { t as require_read_all_rows } from "../../read-all-rows-TaPqdWNF.mjs";
|
|
3
3
|
import { t as prefixRange } from "../../prefix-range-Dl0znowN.mjs";
|
|
4
4
|
import { t as loadPrefixed } from "../../prefix-walk-DAOBSyH1.mjs";
|
|
@@ -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-
|
|
6
|
+
const require_dist = require("../../dist-DX5Fu3mV.js");
|
|
7
7
|
const require_formatter = require("../../formatter-DqAKDlvN.js");
|
|
8
8
|
//#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
|
|
9
9
|
var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ln as logDestinationCapability, ei as BaseAddon } from "../../dist-
|
|
1
|
+
import { Ln as logDestinationCapability, ei as BaseAddon } from "../../dist-CdFkkmXW.mjs";
|
|
2
2
|
import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
|
|
3
3
|
//#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
|
|
4
4
|
var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
|
|
@@ -3,10 +3,10 @@ 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-
|
|
7
|
-
const require_device_ca = require("../../device-ca-
|
|
6
|
+
const require_dist = require("../../dist-DX5Fu3mV.js");
|
|
7
|
+
const require_device_ca = require("../../device-ca-CfiIg3a9.js");
|
|
8
8
|
const require_read_all_rows$1 = require("../../read-all-rows-CVoO1ONM.js");
|
|
9
|
-
const require_constants = require("../../constants-
|
|
9
|
+
const require_constants = require("../../constants-CqbO6e9i.js");
|
|
10
10
|
const require_platform_key_source = require("../../platform-key-source-Di7HrlGR.js");
|
|
11
11
|
let node_crypto = require("node:crypto");
|
|
12
12
|
let _camstack_types_node = require("@camstack/types/node");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { An as keychainAdminCapability, Da as EventCategory, G as KeychainResetRecordSchema, Ki as defineRecordKinds, U as KEYCHAIN_RESET_TRIGGER_ACTOR, W as KeychainAuditEntrySchema, Xi as formatSecretScope, ei as BaseAddon, jn as keychainCapability, pa as parseSecretRef, ua as normalizeSecretOwner } from "../../dist-
|
|
2
|
-
import { d as JWT_SIGNING_KEY_REF, n as DEVICE_CA_KEY_REF, p as SYSTEM_SECRET_OWNER } from "../../device-ca-
|
|
1
|
+
import { An as keychainAdminCapability, Da as EventCategory, G as KeychainResetRecordSchema, Ki as defineRecordKinds, U as KEYCHAIN_RESET_TRIGGER_ACTOR, W as KeychainAuditEntrySchema, Xi as formatSecretScope, ei as BaseAddon, jn as keychainCapability, pa as parseSecretRef, ua as normalizeSecretOwner } from "../../dist-CdFkkmXW.mjs";
|
|
2
|
+
import { d as JWT_SIGNING_KEY_REF, n as DEVICE_CA_KEY_REF, p as SYSTEM_SECRET_OWNER } from "../../device-ca-BWTWWBcc.mjs";
|
|
3
3
|
import { t as require_read_all_rows } from "../../read-all-rows-TaPqdWNF.mjs";
|
|
4
|
-
import { a as MESH_CA_KEY_REF } from "../../constants-
|
|
4
|
+
import { a as MESH_CA_KEY_REF } from "../../constants-zBiEodEM.mjs";
|
|
5
5
|
import { n as getPlatformKeychainKey } from "../../platform-key-source-BWlq0jjn.mjs";
|
|
6
6
|
import { createCipheriv, createDecipheriv, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
7
7
|
import { restrictDirToOwnerSync, restrictFileToOwnerSync, writeOwnerOnlyFileSync } from "@camstack/types/node";
|
|
@@ -1,24 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* liveness-monitor — the node states, out loud, when it has stopped working.
|
|
3
|
-
*
|
|
4
|
-
* On 2026-08-03 the hub ran for four hours with zero devices, zero published
|
|
5
|
-
* streams and zero recordings. Three separate defects caused it, each silent at
|
|
6
|
-
* the point of failure, and the operator found out by opening the viewer. Every
|
|
7
|
-
* one of those defects has since been fixed and given a log line — but a log
|
|
8
|
-
* line per known bug does not cover the next one. This addon covers the class:
|
|
9
|
-
* it asserts what must be true of a working node and reports when it is not.
|
|
10
|
-
*
|
|
11
|
-
* It emits `SystemLivenessFailed` / `SystemLivenessRecovered` on the event bus
|
|
12
|
-
* with a STABLE finding id, exactly like the kernel's addon-health monitor, so
|
|
13
|
-
* AlertCenter raises one persistent alert per fault and dismisses it on
|
|
14
|
-
* recovery instead of producing a stream of them. The same lines go to the log
|
|
15
|
-
* at ERROR, which is what makes them alertable in Loki.
|
|
16
|
-
*
|
|
17
|
-
* Deliberately hub-only and deliberately read-only: it observes through
|
|
18
|
-
* `ctx.api` and changes nothing. A monitor that can act is a monitor that can
|
|
19
|
-
* cause the outage it was meant to report — this repo has already stopped a
|
|
20
|
-
* hub that way once (the remedial-action watcher, 2026-07-30).
|
|
21
|
-
*/
|
|
22
1
|
import { BaseAddon, type ProviderRegistration } from "@camstack/types";
|
|
23
2
|
import { type LivenessFinding } from "./liveness-checks.js";
|
|
24
3
|
interface LivenessMonitorConfig extends Record<string, unknown> {
|
|
@@ -27,8 +6,9 @@ interface LivenessMonitorConfig extends Record<string, unknown> {
|
|
|
27
6
|
export declare class LivenessMonitorAddon extends BaseAddon<LivenessMonitorConfig> {
|
|
28
7
|
private tickTimer;
|
|
29
8
|
private startedAtMs;
|
|
30
|
-
|
|
31
|
-
|
|
9
|
+
private reporter;
|
|
10
|
+
/** Roots whose writability could not be read, already said once. */
|
|
11
|
+
private readonly unknownRootsSaid;
|
|
32
12
|
constructor();
|
|
33
13
|
protected onInitialize(): Promise<ProviderRegistration[]>;
|
|
34
14
|
protected onShutdown(): Promise<void>;
|
|
@@ -56,7 +36,11 @@ export declare class LivenessMonitorAddon extends BaseAddon<LivenessMonitorConfi
|
|
|
56
36
|
* names it, per camera (D393, and CLAUDE.md's logging rule).
|
|
57
37
|
*/
|
|
58
38
|
private unreadable;
|
|
59
|
-
/**
|
|
60
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Every storage root this node writes to must be writable BY THIS PROCESS.
|
|
41
|
+
* Reported through the same finding path (one ERROR, one alert, a recovery
|
|
42
|
+
* when it is fixed). Never throws: an unreadable list is said and skipped.
|
|
43
|
+
*/
|
|
44
|
+
checkStorageRoots(): Promise<readonly LivenessFinding[]>;
|
|
61
45
|
}
|
|
62
46
|
export {};
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
require("../../chunk-Cek0wNdY.js");
|
|
3
|
-
const require_dist = require("../../dist-
|
|
2
|
+
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
3
|
+
const require_dist = require("../../dist-DX5Fu3mV.js");
|
|
4
|
+
let node_fs_promises = require("node:fs/promises");
|
|
5
|
+
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
6
|
+
let node_path = require("node:path");
|
|
7
|
+
node_path = require_chunk.__toESM(node_path);
|
|
8
|
+
let node_fs = require("node:fs");
|
|
4
9
|
//#region src/builtins/liveness-monitor/liveness-checks.ts
|
|
5
10
|
var NO_DEVICES = "liveness:no-devices";
|
|
6
11
|
var ALL_OFFLINE = "liveness:all-devices-offline";
|
|
@@ -52,6 +57,198 @@ function isLivenessFindingId(id) {
|
|
|
52
57
|
return id === NO_DEVICES || id === ALL_OFFLINE || id.startsWith(NO_FOOTAGE_PREFIX);
|
|
53
58
|
}
|
|
54
59
|
//#endregion
|
|
60
|
+
//#region src/builtins/liveness-monitor/liveness-reporter.ts
|
|
61
|
+
/**
|
|
62
|
+
* Announce new liveness faults and their recoveries — never an unchanged fault.
|
|
63
|
+
*
|
|
64
|
+
* One ERROR line and one `SystemLivenessFailed` event per NEW finding id (the
|
|
65
|
+
* alert centre turns it into one persistent alert), one INFO line and one
|
|
66
|
+
* `SystemLivenessRecovered` when it is gone. Several families of checks run on
|
|
67
|
+
* different cadences (the device checks wait out the boot grace, the storage
|
|
68
|
+
* check does not), so a recovery is judged only within the family that was
|
|
69
|
+
* just evaluated: a storage check that found nothing must not "recover" a
|
|
70
|
+
* device finding it never looked at.
|
|
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
|
+
var LivenessReporter = class {
|
|
78
|
+
deps;
|
|
79
|
+
/** Finding ids currently reported, so recovery can be detected and announced. */
|
|
80
|
+
active = /* @__PURE__ */ new Set();
|
|
81
|
+
constructor(deps) {
|
|
82
|
+
this.deps = deps;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* @param findings what the family found this evaluation
|
|
86
|
+
* @param inFamily which ids the family owns — only those can recover here
|
|
87
|
+
*/
|
|
88
|
+
report(findings, inFamily) {
|
|
89
|
+
const current = new Set(findings.map((f) => f.id));
|
|
90
|
+
for (const finding of findings) {
|
|
91
|
+
if (this.active.has(finding.id)) continue;
|
|
92
|
+
this.active.add(finding.id);
|
|
93
|
+
this.deps.logger.error(`liveness: ${finding.title} — ${finding.message}`, {
|
|
94
|
+
...finding.deviceId !== void 0 ? { tags: { deviceId: finding.deviceId } } : {},
|
|
95
|
+
meta: {
|
|
96
|
+
findingId: finding.id,
|
|
97
|
+
severity: finding.severity
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
this.deps.emit(require_dist.createEvent(require_dist.EventCategory.SystemLivenessFailed, LIVENESS_SOURCE, {
|
|
101
|
+
findingId: finding.id,
|
|
102
|
+
severity: finding.severity,
|
|
103
|
+
title: finding.title,
|
|
104
|
+
message: finding.message,
|
|
105
|
+
...finding.deviceId !== void 0 ? { deviceId: finding.deviceId } : {},
|
|
106
|
+
...finding.deviceName !== void 0 ? { name: finding.deviceName } : {}
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
for (const id of [...this.active]) {
|
|
110
|
+
if (current.has(id) || !inFamily(id)) continue;
|
|
111
|
+
this.active.delete(id);
|
|
112
|
+
this.deps.logger.info(`liveness: recovered — ${id}`, { meta: { findingId: id } });
|
|
113
|
+
this.deps.emit(require_dist.createEvent(require_dist.EventCategory.SystemLivenessRecovered, LIVENESS_SOURCE, { findingId: id }));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/builtins/liveness-monitor/storage-root-access.ts
|
|
119
|
+
/**
|
|
120
|
+
* Can this node WRITE where its storage locations say media goes?
|
|
121
|
+
*
|
|
122
|
+
* 2026-10-23, a fresh install of the dev hub: the bind mounted at
|
|
123
|
+
* `/recordings` was `root:root 0755` while the hub runs as uid 1001. Nothing
|
|
124
|
+
* said so at boot; the first sign was an `EACCES` on `/recordings/artifacts`,
|
|
125
|
+
* and recordings would have failed one write at a time. A root the process
|
|
126
|
+
* cannot write is a fact known the moment the node starts, so it is reported
|
|
127
|
+
* then — once, naming the path, its owner, its mode and the uid — instead of
|
|
128
|
+
* as a stream of per-write errors.
|
|
129
|
+
*
|
|
130
|
+
* Only a location this node may WRITE (`mayWriteToLocation`), served by the
|
|
131
|
+
* local filesystem provider, on this node, with an absolute `basePath`, is
|
|
132
|
+
* judged. A root that does not exist yet is judged by its nearest existing
|
|
133
|
+
* ancestor: that is the directory the node would have to create it in.
|
|
134
|
+
*
|
|
135
|
+
* A probe that cannot answer (a stat that fails for a reason other than
|
|
136
|
+
* permission) is `unknown` and never a finding (D393): an unreadable fact is
|
|
137
|
+
* not an unwritable disk.
|
|
138
|
+
*/
|
|
139
|
+
/** The local filesystem storage provider's id (`FilesystemStorageProvider.providerId`). */
|
|
140
|
+
var FILESYSTEM_PROVIDER_ID = "filesystem-storage";
|
|
141
|
+
var UNWRITABLE_PREFIX = "liveness:storage-root-unwritable:";
|
|
142
|
+
/** The hub-local, writable filesystem roots among `locations`, deduplicated by path. */
|
|
143
|
+
function storageRootTargets(locations, localNodeId) {
|
|
144
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
145
|
+
for (const location of locations) {
|
|
146
|
+
if (location.providerId !== FILESYSTEM_PROVIDER_ID) continue;
|
|
147
|
+
if (location.nodeId !== void 0 && location.nodeId !== localNodeId) continue;
|
|
148
|
+
if (!require_dist.mayWriteToLocation(location)) continue;
|
|
149
|
+
const basePath = location.config["basePath"];
|
|
150
|
+
if (typeof basePath !== "string" || !node_path.default.isAbsolute(basePath)) continue;
|
|
151
|
+
const root = node_path.default.resolve(basePath);
|
|
152
|
+
byPath.set(root, [...byPath.get(root) ?? [], location.id]);
|
|
153
|
+
}
|
|
154
|
+
return [...byPath.entries()].map(([root, locationIds]) => ({
|
|
155
|
+
path: root,
|
|
156
|
+
locationIds: locationIds.toSorted()
|
|
157
|
+
})).toSorted((a, b) => a.path.localeCompare(b.path));
|
|
158
|
+
}
|
|
159
|
+
function isMissing(err) {
|
|
160
|
+
return err instanceof Error && "code" in err && (err.code === "ENOENT" || err.code === "ENOTDIR");
|
|
161
|
+
}
|
|
162
|
+
/** Probe one root: the root itself, or the nearest ancestor that exists. */
|
|
163
|
+
async function probeStorageRoot(target, probe) {
|
|
164
|
+
let dir = target.path;
|
|
165
|
+
let stat;
|
|
166
|
+
for (;;) try {
|
|
167
|
+
stat = await probe.stat(dir);
|
|
168
|
+
break;
|
|
169
|
+
} catch (err) {
|
|
170
|
+
const parent = node_path.default.dirname(dir);
|
|
171
|
+
if (!isMissing(err) || parent === dir) return {
|
|
172
|
+
kind: "unknown",
|
|
173
|
+
target,
|
|
174
|
+
reason: `stat ${dir} failed: ${require_dist.errMsg(err)}`
|
|
175
|
+
};
|
|
176
|
+
dir = parent;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
await probe.canWrite(dir);
|
|
180
|
+
return {
|
|
181
|
+
kind: "writable",
|
|
182
|
+
target
|
|
183
|
+
};
|
|
184
|
+
} catch (err) {
|
|
185
|
+
return {
|
|
186
|
+
kind: "unwritable",
|
|
187
|
+
target,
|
|
188
|
+
blockedAt: dir,
|
|
189
|
+
stat,
|
|
190
|
+
reason: require_dist.errMsg(err)
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function octalMode(mode) {
|
|
195
|
+
return (mode & 4095).toString(8).padStart(4, "0");
|
|
196
|
+
}
|
|
197
|
+
function processIdentity(probe) {
|
|
198
|
+
if (probe.processUid === null) return "this process";
|
|
199
|
+
return `uid ${String(probe.processUid)}` + (probe.processGid === null ? "" : ` (gid ${String(probe.processGid)})`);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* One finding per DIRECTORY that refused — several roots under one unwritable
|
|
203
|
+
* mount (`/recordings` holding recordings, event media and artifacts) are one
|
|
204
|
+
* cause and one alert. Stable id = that directory.
|
|
205
|
+
*/
|
|
206
|
+
function storageRootFindings(results, probe) {
|
|
207
|
+
const byBlocked = /* @__PURE__ */ new Map();
|
|
208
|
+
for (const result of results) {
|
|
209
|
+
if (result.kind !== "unwritable") continue;
|
|
210
|
+
const entry = byBlocked.get(result.blockedAt) ?? {
|
|
211
|
+
stat: result.stat,
|
|
212
|
+
roots: [],
|
|
213
|
+
locationIds: []
|
|
214
|
+
};
|
|
215
|
+
entry.roots.push(result.target.path);
|
|
216
|
+
entry.locationIds.push(...result.target.locationIds);
|
|
217
|
+
byBlocked.set(result.blockedAt, entry);
|
|
218
|
+
}
|
|
219
|
+
const uidFix = probe.processUid === null ? "<uid>" : String(probe.processUid);
|
|
220
|
+
const gidFix = probe.processGid === null ? "<gid>" : String(probe.processGid);
|
|
221
|
+
return [...byBlocked.entries()].toSorted(([a], [b]) => a.localeCompare(b)).map(([blockedAt, { stat, roots, locationIds }]) => {
|
|
222
|
+
const missing = roots.filter((r) => r !== blockedAt);
|
|
223
|
+
const below = missing.length > 0 ? ` (${missing.join(", ")} must be created inside it)` : "";
|
|
224
|
+
return {
|
|
225
|
+
id: `${UNWRITABLE_PREFIX}${blockedAt}`,
|
|
226
|
+
severity: "error",
|
|
227
|
+
title: "Storage location not writable",
|
|
228
|
+
message: `${blockedAt} is owned by ${String(stat.uid)}:${String(stat.gid)} with mode ${octalMode(stat.mode)}, and CamStack runs as ${processIdentity(probe)} — nothing can be written there${below}. Locations affected: ${locationIds.toSorted().join(", ")}. Fix the ownership on the host, e.g. chown -R ${uidFix}:${gidFix} <the host directory bound at ${blockedAt}>.`
|
|
229
|
+
};
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
function isStorageRootFindingId(id) {
|
|
233
|
+
return id.startsWith(UNWRITABLE_PREFIX);
|
|
234
|
+
}
|
|
235
|
+
/** The real probe: `fs.access(W_OK | X_OK)` as THIS process, and `fs.stat`. */
|
|
236
|
+
function nodeStorageRootProbe(fs) {
|
|
237
|
+
return {
|
|
238
|
+
canWrite: (dir) => fs.access(dir, node_fs.constants.W_OK | node_fs.constants.X_OK),
|
|
239
|
+
stat: async (dir) => {
|
|
240
|
+
const st = await fs.stat(dir);
|
|
241
|
+
return {
|
|
242
|
+
uid: st.uid,
|
|
243
|
+
gid: st.gid,
|
|
244
|
+
mode: st.mode
|
|
245
|
+
};
|
|
246
|
+
},
|
|
247
|
+
processUid: typeof process.getuid === "function" ? process.getuid() : null,
|
|
248
|
+
processGid: typeof process.getgid === "function" ? process.getgid() : null
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
//#endregion
|
|
55
252
|
//#region src/builtins/liveness-monitor/liveness-monitor.addon.ts
|
|
56
253
|
/**
|
|
57
254
|
* liveness-monitor — the node states, out loud, when it has stopped working.
|
|
@@ -74,11 +271,8 @@ function isLivenessFindingId(id) {
|
|
|
74
271
|
* cause the outage it was meant to report — this repo has already stopped a
|
|
75
272
|
* hub that way once (the remedial-action watcher, 2026-07-30).
|
|
76
273
|
*/
|
|
77
|
-
/**
|
|
78
|
-
var
|
|
79
|
-
type: "addon",
|
|
80
|
-
id: "liveness-monitor"
|
|
81
|
-
};
|
|
274
|
+
/** The node this monitor runs on (it is hub-only). */
|
|
275
|
+
var HUB_NODE_ID = "hub";
|
|
82
276
|
/** How often the invariants are checked. */
|
|
83
277
|
var TICK_INTERVAL_MS = 5 * 6e4;
|
|
84
278
|
/**
|
|
@@ -101,13 +295,19 @@ var RECORDING_LOOKBACK_MS = 1440 * 6e4;
|
|
|
101
295
|
var LivenessMonitorAddon = class extends require_dist.BaseAddon {
|
|
102
296
|
tickTimer = null;
|
|
103
297
|
startedAtMs = 0;
|
|
104
|
-
|
|
105
|
-
|
|
298
|
+
reporter = null;
|
|
299
|
+
/** Roots whose writability could not be read, already said once. */
|
|
300
|
+
unknownRootsSaid = /* @__PURE__ */ new Set();
|
|
106
301
|
constructor() {
|
|
107
302
|
super({});
|
|
108
303
|
}
|
|
109
304
|
async onInitialize() {
|
|
110
305
|
this.startedAtMs = Date.now();
|
|
306
|
+
this.reporter = new LivenessReporter({
|
|
307
|
+
logger: this.ctx.logger,
|
|
308
|
+
emit: (event) => this.ctx.eventBus.emit(event)
|
|
309
|
+
});
|
|
310
|
+
this.checkStorageRoots();
|
|
111
311
|
this.tickTimer = setInterval(() => {
|
|
112
312
|
this.tick();
|
|
113
313
|
}, TICK_INTERVAL_MS);
|
|
@@ -127,11 +327,12 @@ var LivenessMonitorAddon = class extends require_dist.BaseAddon {
|
|
|
127
327
|
* without waiting out the interval.
|
|
128
328
|
*/
|
|
129
329
|
async tick() {
|
|
130
|
-
|
|
330
|
+
const storage = await this.checkStorageRoots();
|
|
331
|
+
if (Date.now() - this.startedAtMs < BOOT_GRACE_MS) return storage;
|
|
131
332
|
try {
|
|
132
333
|
const findings = evaluateLiveness(await this.gather());
|
|
133
|
-
this.report(findings);
|
|
134
|
-
return findings;
|
|
334
|
+
this.reporter?.report(findings, isLivenessFindingId);
|
|
335
|
+
return [...storage, ...findings];
|
|
135
336
|
} catch (err) {
|
|
136
337
|
this.ctx.logger.error("liveness-monitor: check failed — node health is UNKNOWN this tick", { meta: { error: require_dist.errMsg(err) } });
|
|
137
338
|
return [];
|
|
@@ -206,33 +407,33 @@ var LivenessMonitorAddon = class extends require_dist.BaseAddon {
|
|
|
206
407
|
this.ctx.logger.warn(`liveness: recording availability was unreadable (${window}) — this camera is not judged this tick`, { tags: { deviceId } });
|
|
207
408
|
return null;
|
|
208
409
|
}
|
|
209
|
-
/**
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
this.ctx.logger.
|
|
235
|
-
|
|
410
|
+
/**
|
|
411
|
+
* Every storage root this node writes to must be writable BY THIS PROCESS.
|
|
412
|
+
* Reported through the same finding path (one ERROR, one alert, a recovery
|
|
413
|
+
* when it is fixed). Never throws: an unreadable list is said and skipped.
|
|
414
|
+
*/
|
|
415
|
+
async checkStorageRoots() {
|
|
416
|
+
const reporter = this.reporter;
|
|
417
|
+
if (reporter === null) return [];
|
|
418
|
+
try {
|
|
419
|
+
const locations = await this.ctx.api.storage.listLocations.query({});
|
|
420
|
+
const probe = nodeStorageRootProbe(node_fs_promises);
|
|
421
|
+
const results = await Promise.all(storageRootTargets(locations, HUB_NODE_ID).map((t) => probeStorageRoot(t, probe)));
|
|
422
|
+
for (const result of results) {
|
|
423
|
+
if (result.kind !== "unknown" || this.unknownRootsSaid.has(result.target.path)) continue;
|
|
424
|
+
this.unknownRootsSaid.add(result.target.path);
|
|
425
|
+
this.ctx.logger.warn("liveness: storage root writability could not be read — not judged", { meta: {
|
|
426
|
+
path: result.target.path,
|
|
427
|
+
locationIds: result.target.locationIds,
|
|
428
|
+
reason: result.reason
|
|
429
|
+
} });
|
|
430
|
+
}
|
|
431
|
+
const findings = storageRootFindings(results, probe);
|
|
432
|
+
reporter.report(findings, isStorageRootFindingId);
|
|
433
|
+
return findings;
|
|
434
|
+
} catch (err) {
|
|
435
|
+
this.ctx.logger.warn("liveness: storage locations unreadable — storage roots not checked", { meta: { error: require_dist.errMsg(err) } });
|
|
436
|
+
return [];
|
|
236
437
|
}
|
|
237
438
|
}
|
|
238
439
|
};
|