@camstack/system 1.2.439 → 1.2.441

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 (103) hide show
  1. package/dist/addon-runner.js +743 -79
  2. package/dist/addon-runner.mjs +743 -79
  3. package/dist/addon-utils.js +1 -1
  4. package/dist/addon-utils.mjs +1 -1
  5. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  8. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  9. package/dist/builtins/alerts/alerts.addon.js +1 -1
  10. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  11. package/dist/builtins/autotrack/index.js +1 -1
  12. package/dist/builtins/autotrack/index.mjs +1 -1
  13. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
  14. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
  15. package/dist/builtins/calendars/calendars.addon.js +1 -1
  16. package/dist/builtins/calendars/calendars.addon.mjs +1 -1
  17. package/dist/builtins/camera-grid/index.js +1 -1
  18. package/dist/builtins/camera-grid/index.mjs +1 -1
  19. package/dist/builtins/composer/composer.addon.js +2 -2
  20. package/dist/builtins/composer/composer.addon.mjs +2 -2
  21. package/dist/builtins/console-logging/index.js +1 -1
  22. package/dist/builtins/console-logging/index.mjs +1 -1
  23. package/dist/builtins/core-blocks/block-settings-store.d.ts +1 -1
  24. package/dist/builtins/core-blocks/core-blocks.addon.js +77 -11
  25. package/dist/builtins/core-blocks/core-blocks.addon.mjs +77 -12
  26. package/dist/builtins/core-blocks/index.mjs +1 -1
  27. package/dist/builtins/device-manager/device-manager.addon.js +181 -59
  28. package/dist/builtins/device-manager/device-manager.addon.mjs +180 -60
  29. package/dist/builtins/device-manager/device-removal-hooks.d.ts +0 -11
  30. package/dist/builtins/hub-forwarder/index.js +1 -1
  31. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  32. package/dist/builtins/keychain/index.js +29 -11
  33. package/dist/builtins/keychain/index.mjs +29 -11
  34. package/dist/builtins/keychain/keychain-service.d.ts +8 -0
  35. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  36. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  37. package/dist/builtins/local-auth/local-auth.addon.js +2 -2
  38. package/dist/builtins/local-auth/local-auth.addon.mjs +2 -2
  39. package/dist/builtins/local-network/local-network.addon.js +2 -2
  40. package/dist/builtins/local-network/local-network.addon.mjs +2 -2
  41. package/dist/builtins/loki-logging/index.js +1 -1
  42. package/dist/builtins/loki-logging/index.mjs +1 -1
  43. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  44. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  45. package/dist/builtins/platform-probe/index.js +1 -1
  46. package/dist/builtins/platform-probe/index.mjs +1 -1
  47. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  48. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  49. package/dist/builtins/snapshot/index.js +1 -1
  50. package/dist/builtins/snapshot/index.mjs +1 -1
  51. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  52. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  53. package/dist/builtins/sqlite-storage/index.js +1 -1
  54. package/dist/builtins/sqlite-storage/index.mjs +1 -1
  55. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +38 -7
  56. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +69 -3
  57. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +69 -3
  58. package/dist/builtins/sqlite-storage/storage-admin-provider.d.ts +31 -0
  59. package/dist/builtins/storage-orchestrator/hub-settings-forward.d.ts +3 -3
  60. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +4 -4
  61. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +4 -4
  62. package/dist/builtins/system-config/system-config.addon.js +2 -2
  63. package/dist/builtins/system-config/system-config.addon.mjs +2 -2
  64. package/dist/builtins/winston-logging/index.js +1 -1
  65. package/dist/builtins/winston-logging/index.mjs +1 -1
  66. package/dist/{composition-sources-B4jC2FC7.js → composition-sources-BG-W2U0d.js} +1 -1
  67. package/dist/{composition-sources-BGOSCGWa.mjs → composition-sources-ByNISOnW.mjs} +1 -1
  68. package/dist/{constants-B92BjObl.mjs → constants-DJILnUSb.mjs} +1 -1
  69. package/dist/{constants-BRsjQwi-.js → constants-UojhAJcm.js} +1 -1
  70. package/dist/{device-ca-DHG9OW2m.js → device-ca-196DAEZT.js} +1 -1
  71. package/dist/{device-ca-46BUsWD1.mjs → device-ca-D8Ri9TVz.mjs} +1 -1
  72. package/dist/{dist-yI6kF6r6.mjs → dist-BWvkuc31.mjs} +248 -27
  73. package/dist/{dist-BOWdLjoK.js → dist-BiTCcaf-.js} +265 -26
  74. package/dist/file-host-worker.js +1 -1
  75. package/dist/file-host-worker.mjs +1 -1
  76. package/dist/{hub-mdns-ciao-DGwVc2uh.mjs → hub-mdns-ciao-Cnd0bTB9.mjs} +1 -1
  77. package/dist/{hub-mdns-ciao-D7S5AJRU.js → hub-mdns-ciao-DOBTq8qr.js} +1 -1
  78. package/dist/index.js +336 -336
  79. package/dist/index.mjs +337 -337
  80. package/dist/kernel/addon-settings-store.d.ts +43 -0
  81. package/dist/kernel/config-manager.d.ts +0 -19
  82. package/dist/kernel/device-settings-store.d.ts +74 -0
  83. package/dist/kernel/legacy-blob-guard.d.ts +58 -0
  84. package/dist/kernel/moleculer/runner-addon-settings.d.ts +34 -0
  85. package/dist/kernel/moleculer/runner-device-settings.d.ts +39 -0
  86. package/dist/kernel/prefix-walk.d.ts +39 -0
  87. package/dist/kernel/sections-store.d.ts +20 -0
  88. package/dist/kernel/settings-door-view.d.ts +2 -2
  89. package/dist/kernel/settings-view-types.d.ts +5 -30
  90. package/dist/kernel/store-settings-view.d.ts +0 -3
  91. package/dist/prefix-range-D_KueXbQ.js +27 -0
  92. package/dist/prefix-range-Dl0znowN.mjs +22 -0
  93. package/dist/prefix-walk-DAOBSyH1.mjs +88 -0
  94. package/dist/prefix-walk-Dv6MLZlM.js +99 -0
  95. package/dist/{restore-archive-CpdUO-uS.mjs → restore-archive-4ziluTU3.mjs} +1 -1
  96. package/dist/{restore-archive-DiGiI6Jp.js → restore-archive-CfSSjKnA.js} +1 -1
  97. package/dist/{retired-settings-keys-UsVtLOT0.mjs → retired-settings-keys-Bhwe4ymb.mjs} +2 -2
  98. package/dist/{retired-settings-keys-CyQ7uvta.js → retired-settings-keys-D_VwZiDt.js} +10 -4
  99. package/dist/{wal-maintenance-xsvdT5Go.mjs → wal-maintenance-B57SAp4a.mjs} +0 -0
  100. package/dist/{wal-maintenance-DEL_jeG0.js → wal-maintenance-CgZTNWAz.js} +0 -0
  101. package/package.json +5 -2
  102. package/dist/{custom-action-registry-Bf_YnagS.js → custom-action-registry-BKmoaqG0.js} +1 -1
  103. package/dist/{custom-action-registry-BQnbN0DP.mjs → custom-action-registry-CyUuhATc.mjs} +1 -1
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { i as __require } from "./chunk-CNf5ZN-e.mjs";
2
- import { Ar as DEVICE_SETTINGS_CONTRIBUTION_METHODS, B as METHOD_ACCESS_MAP, Br as asString$1, Dr as errMsg$1, En as objectInputDeclaresRequiredDeviceId, Fr as ReadinessTimeoutError, Gn as scopesAllowDeviceCap, Kt as extractNestedAddonId, Nr as RUNTIME_DEFAULTS, Pr as ReadinessRegistry, Qt as interpolate, R as LOG_LEVEL_RANK, Rr as asJsonObject$1, Tn as objectInputDeclaresAddonId, Vn as resolveMethodAuth, Wn as scopesAllowAddon, Wr as createEvent$1, Yr as expandCapMethods, Zn as splitTranslationRef, _ as CatalogFileSchema, an as isVoidInput, d as CATALOG_LANGS, dn as lifecycleJobSchema, en as isArrayOutputSchema, fi as scopeKey, gn as looseSchema, hn as logLevelAtMost, in as isObjectInput, ir as supportedRuntimes, jn as procedureAuthKey, jr as DEVICE_STATUS_METHOD, kr as DATAPLANE_SECRET_HEADER$1, li as readinessKey, n as AGENT_MDNS_TXT_KEYS, oi as parseJsonObject, on as kebabToCamel$1, qr as emitDownForOwnedCaps, r as ALL_CAPABILITY_DEFINITIONS, rt as addonSettingsCapability, si as parseJsonUnknown, t as AGENT_MDNS_SERVICE_TYPE, tn as isCollectionArrayMethod, ui as resolveCapMount, vi as userError, yi as EventCategory$1, zr as asNumber } from "./dist-yI6kF6r6.mjs";
3
- import { d as JWT_SIGNING_KEY_REF, f as JwtSigningKeyUnavailableError, g as processJwtSigningKey, i as generateDeviceCa, l as verifyDeviceLeaf, m as getProcessJwtSigningKeyState, n as DEVICE_CA_KEY_REF, o as issueDeviceLeaf, p as SYSTEM_SECRET_OWNER, s as loadDeviceCa, u as verifyDeviceSignature, v as staticJwtSigningKey } from "./device-ca-46BUsWD1.mjs";
2
+ import { $n as splitTranslationRef, Ar as errMsg$1, B as METHOD_ACCESS_MAP, Dn as objectInputDeclaresAddonId, Hr as asNumber, Ir as RUNTIME_DEFAULTS, Jt as extractNestedAddonId, Kn as scopesAllowAddon, Lr as ReadinessRegistry, Mr as DATAPLANE_SECRET_HEADER$1, Nn as procedureAuthKey, Nr as DEVICE_SETTINGS_CONTRIBUTION_METHODS, On as objectInputDeclaresRequiredDeviceId, Pr as DEVICE_STATUS_METHOD, Qr as expandCapMethods, R as LOG_LEVEL_RANK, Rr as ReadinessTimeoutError, Si as EventCategory$1, Un as resolveMethodAuth, Ur as asString$1, Vr as asJsonObject$1, Xr as emitDownForOwnedCaps, _ as CatalogFileSchema, _n as logLevelAtMost, at as addonSettingsCapability, cn as kebabToCamel$1, d as CATALOG_LANGS, en as interpolate, fi as readinessKey, hi as scopeKey, li as parseJsonObject, n as AGENT_MDNS_TXT_KEYS, nn as isArrayOutputSchema, on as isObjectInput, pi as resolveCapMount, pn as lifecycleJobSchema, qn as scopesAllowDeviceCap, qr as createEvent$1, r as ALL_CAPABILITY_DEFINITIONS, rn as isCollectionArrayMethod, sn as isVoidInput, sr as supportedRuntimes, t as AGENT_MDNS_SERVICE_TYPE, ui as parseJsonUnknown, vn as looseSchema, xi as userError } from "./dist-BWvkuc31.mjs";
3
+ import { d as JWT_SIGNING_KEY_REF, f as JwtSigningKeyUnavailableError, g as processJwtSigningKey, i as generateDeviceCa, l as verifyDeviceLeaf, m as getProcessJwtSigningKeyState, n as DEVICE_CA_KEY_REF, o as issueDeviceLeaf, p as SYSTEM_SECRET_OWNER, s as loadDeviceCa, u as verifyDeviceSignature, v as staticJwtSigningKey } from "./device-ca-D8Ri9TVz.mjs";
4
4
  import { AlertCenterAddon } from "./builtins/alerts/alerts.addon.mjs";
5
5
  import "./builtins/alerts/index.mjs";
6
6
  import { CalendarsAddon } from "./builtins/calendars/calendars.addon.mjs";
@@ -9,9 +9,12 @@ import { ComposerAddon } from "./builtins/composer/composer.addon.mjs";
9
9
  import "./builtins/composer/index.mjs";
10
10
  import { t as formatLogLine } from "./formatter-B7qW8bPJ.mjs";
11
11
  import { ConsoleDestination, ConsoleLoggingAddon } from "./builtins/console-logging/index.mjs";
12
- import { CoreBlocksAddon } from "./builtins/core-blocks/core-blocks.addon.mjs";
12
+ import { t as prefixRange } from "./prefix-range-Dl0znowN.mjs";
13
+ import { t as loadPrefixed } from "./prefix-walk-DAOBSyH1.mjs";
14
+ import { CoreBlocksAddon, c as createAddonSettingsStore } from "./builtins/core-blocks/core-blocks.addon.mjs";
13
15
  import "./builtins/core-blocks/index.mjs";
14
- import { DeviceManagerAddon, t as setWrapperActive } from "./builtins/device-manager/device-manager.addon.mjs";
16
+ import { t as RETIRED_SETTINGS_KEYS } from "./retired-settings-keys-Bhwe4ymb.mjs";
17
+ import { DeviceManagerAddon, n as readDeviceOrThrow, r as setWrapperActive, t as createDeviceSettingsStore } from "./builtins/device-manager/device-manager.addon.mjs";
15
18
  import "./builtins/device-manager/index.mjs";
16
19
  import { HubForwarderAddon, HubForwarderDestination } from "./builtins/hub-forwarder/index.mjs";
17
20
  import { LivenessMonitorAddon } from "./builtins/liveness-monitor/liveness-monitor.addon.mjs";
@@ -21,21 +24,21 @@ import "./builtins/local-auth/index.mjs";
21
24
  import { LokiDestination, LokiLoggingAddon } from "./builtins/loki-logging/index.mjs";
22
25
  import NativeMetricsAddon, { a as NativeMetricsProvider, n as nodeProcReader, r as parseProcStat } from "./builtins/native-metrics/native-metrics.addon.mjs";
23
26
  import { FilesystemStorageAddon, t as FilesystemStorageProvider } from "./builtins/sqlite-storage/filesystem-storage.addon.mjs";
24
- import { c as SqliteSettingsBackend, l as prefixRange } from "./wal-maintenance-xsvdT5Go.mjs";
27
+ import { c as SqliteSettingsBackend } from "./wal-maintenance-B57SAp4a.mjs";
25
28
  import { SqliteSettingsAddon } from "./builtins/sqlite-storage/sqlite-settings.addon.mjs";
26
29
  import { ConfigStore, DeviceStore } from "./builtins/sqlite-storage/index.mjs";
27
30
  import { StorageOrchestratorAddon, a as stripForwardedCaller, i as createHubCapForwardService, n as HUB_CAP_FWD_ACTION, o as buildStorageLocationRegistry, r as HUB_CAP_FWD_SERVICE, t as StorageOrchestratorService } from "./builtins/storage-orchestrator/storage-orchestrator.addon.mjs";
28
31
  import "./builtins/storage-orchestrator/index.mjs";
29
32
  import { A as isConstrainedCa, C as MAX_LEAF_VALIDITY_DAYS, D as PERMITTED_DNS_SUFFIXES, F as AGENT_HTTPS_NAMES_COLLECTION, I as AGENT_HTTPS_NAMES_COLUMNS, L as AgentHttpsNames, M as permitsIdentity, N as permitsName, O as PERMITTED_IP_RANGES, P as readNameConstraints, R as getProcessAgentHttpsNames, S as LEAF_RENEWAL_WINDOW_DAYS, T as evaluateExistingCert, a as collectAgentNames, b as systemHostFacts, c as installAgentHttps, d as issueAgentHttpsLeaf, f as loadAgentHttps, h as CA_COMMON_NAME, i as certificateNames, j as isLocalOnlyHostname, k as buildNameConstraintSet, l as isContainerBridgeAddress, m as pinnedHttpsCaFingerprint, n as acceptAgentSans, o as createAgentHttpsRequest, p as pemDigest, r as agentHttpsPaths, s as describeHttpsLeaf, t as AgentHttpsInstallError, u as isPrivateAddress, w as SERVER_AUTH_OID, x as CA_VALIDITY_DAYS, y as collectCertIdentity, z as setProcessAgentHttpsNames } from "./agent-https-U0d4uvn2.mjs";
30
- import { a as MESH_CA_KEY_REF, i as MESH_CA_FIELD, n as DEFAULT_HUB_TLS_TCP_PORT, o as MESH_CA_SETTINGS_KEY } from "./constants-B92BjObl.mjs";
33
+ import { a as MESH_CA_KEY_REF, i as MESH_CA_FIELD, n as DEFAULT_HUB_TLS_TCP_PORT, o as MESH_CA_SETTINGS_KEY } from "./constants-DJILnUSb.mjs";
31
34
  import { $ as generateMeshKeyPair, A as setProcessMeshPeerTransportLookup, B as describeMeshLeaf, C as setProcessMeshTlsEnabler, D as getProcessPeerLeaf, E as getProcessNodeMeshTransport, F as setProcessMeshTlsListenerState, G as MeshCaKeeper, H as setProcessMeshCertificateRegistry, I as MESH_NODE_CERTIFICATES_COLLECTION, J as caFingerprint, K as getProcessMeshCaState, L as MESH_NODE_CERTIFICATES_COLUMNS, M as revokeMeshCertificate, N as getProcessMeshTlsListenerState, O as isProcessVerifiedMeshPeer, P as openMeshTlsWhenRevocationsLoaded, Q as generateMeshCa, R as MeshCertificateRegistry, S as ServiceBroker, SystemConfigAddon, T as emitProcessNodeRegistered, U as MeshCrl, V as getProcessMeshCertificateRegistry, W as processMeshCrl, X as createMeshCsrPem, Y as caFingerprintCompact, Z as exportMeshPrivateKeyPem, _ as signPairingProof, _t as readCidrSetting, a as hubHostFromMeshAddress, at as AGENT_UI_UNLOCK_TTL_MS, b as TlsTcpTransporter, c as PAIRING_MAX_SESSIONS, ct as AGENT_UI_UNLOCK_VERSION, d as PAIRING_PROTOCOL, dt as mintAgentUiUnlockTicket, et as isValidAgentNodeId, f as PAIRING_TTL_MS, ft as parseAgentDashboardOrigins, g as keyFingerprintShort, gt as parseCidrList, h as describeCsr, ht as normalizeIp, i as AgentPairing, it as AGENT_DASHBOARD_ORIGINS_FIELD, j as renewMeshCertificate, k as onProcessNodeRegistered, l as PAIRING_PATHS, lt as UnlockTicketJtiStore, m as computePairingSas, mt as createCidrMatcher, n as HubPairing, nt as issueMeshLeafFromCsr, o as PAIRING_CODE_DIGITS, ot as AGENT_UI_UNLOCK_TYP, p as computePairingCommitment, pt as verifyAgentUiUnlockTicket, q as requireProcessMeshCa, r as PairingHttpError, rt as loadMeshCa, s as PAIRING_MAX_CODE_ATTEMPTS, st as AGENT_UI_UNLOCK_USE, tt as issueMeshLeafForNode, u as PAIRING_PIN_DIGITS, ut as isAllowedAgentDashboardReturnTo, v as verifyPairingProof, w as dropProcessPeer, x as Errors, y as MESH_TLS_PORT_HELLO_FIELD, z as RENEWAL_ABANDONED } from "./builtins/system-config/system-config.addon.mjs";
32
35
  import "./builtins/system-config/index.mjs";
33
36
  import { WinstonDestination, WinstonLoggingAddon } from "./builtins/winston-logging/index.mjs";
34
- import { C as isModelDownloaded, T as revisionWeightUrl, _ as downloadFile, a as startEventLoopStallMonitor, b as fetchJson, c as createAuthenticatedFileServer, d as resolveFilePath, f as ModelDownloadService, g as deleteModelFromDisk, h as collectModelFiles, i as attributeStall, l as parseRangeHeader, m as adoptModelRevisions, n as STALL_REPORT_MS, o as createFileDataPlaneHandler, r as STALL_TICK_MS, s as contentTypeFor, t as CustomActionRegistry, u as parseTokenizedUrl, v as downloadModel, w as resolveFormatRevision, x as getModelFilePath, y as ensureModel } from "./custom-action-registry-BQnbN0DP.mjs";
35
- import { A as allFamiliesListenHost, C as caReplacementMarkerFile, D as replaceUnconstrainedLocalCa, E as reissueTlsLeaf, F as registerLanHttpHandler, M as bindPendingLanHttp, N as closeLanHttp, O as retirePreviousLocalCa, P as readLanHttpState, S as validateUploadedTls, T as loadTlsCert, _ as splitPemCertificates, a as storeUploadedCertificate, b as chainsToPublicRoot, c as leafFingerprintOf, d as servedHttpsLeafFingerprint, f as prepareBootTls, g as readTlsMode, h as readTlsAccessStatus, i as revertToGeneratedCertificate, j as applyLanHttp, k as DEFAULT_LAN_HTTP_PORT, l as registerHttpsListener, m as readExtraSans, n as applyCertificateToListener, o as useStoredUploadedCertificate, p as UPLOADED_CA_FILE, r as regenerateCertificate, s as clearHttpsListener, t as createCiaoPublisher, u as reloadHttpsListener, v as writeExtraSans, w as ensureTlsCert, x as checkUploadedCa, y as writeTlsMode } from "./hub-mdns-ciao-DGwVc2uh.mjs";
37
+ import { C as isModelDownloaded, T as revisionWeightUrl, _ as downloadFile, a as startEventLoopStallMonitor, b as fetchJson, c as createAuthenticatedFileServer, d as resolveFilePath, f as ModelDownloadService, g as deleteModelFromDisk, h as collectModelFiles, i as attributeStall, l as parseRangeHeader, m as adoptModelRevisions, n as STALL_REPORT_MS, o as createFileDataPlaneHandler, r as STALL_TICK_MS, s as contentTypeFor, t as CustomActionRegistry, u as parseTokenizedUrl, v as downloadModel, w as resolveFormatRevision, x as getModelFilePath, y as ensureModel } from "./custom-action-registry-CyUuhATc.mjs";
38
+ import { A as allFamiliesListenHost, C as caReplacementMarkerFile, D as replaceUnconstrainedLocalCa, E as reissueTlsLeaf, F as registerLanHttpHandler, M as bindPendingLanHttp, N as closeLanHttp, O as retirePreviousLocalCa, P as readLanHttpState, S as validateUploadedTls, T as loadTlsCert, _ as splitPemCertificates, a as storeUploadedCertificate, b as chainsToPublicRoot, c as leafFingerprintOf, d as servedHttpsLeafFingerprint, f as prepareBootTls, g as readTlsMode, h as readTlsAccessStatus, i as revertToGeneratedCertificate, j as applyLanHttp, k as DEFAULT_LAN_HTTP_PORT, l as registerHttpsListener, m as readExtraSans, n as applyCertificateToListener, o as useStoredUploadedCertificate, p as UPLOADED_CA_FILE, r as regenerateCertificate, s as clearHttpsListener, t as createCiaoPublisher, u as reloadHttpsListener, v as writeExtraSans, w as ensureTlsCert, x as checkUploadedCa, y as writeTlsMode } from "./hub-mdns-ciao-Cnd0bTB9.mjs";
36
39
  import { n as getPlatformKeychainKey, r as setPlatformKeychainKey, t as clearPlatformKeychainKey } from "./platform-key-source-BWlq0jjn.mjs";
37
40
  import { n as getSinglePidStats, t as getPidStats } from "./resource-monitor-Di9daRwI.mjs";
38
- import { c as BackupDecryptError, f as generateBackupKeyPair, h as readBackupEncryptionHeader, l as BackupKeyFormatError, r as prepareRestoreArchive, s as SystemBackupService, t as BackupKeyRequiredError, u as BackupKeyMismatchError } from "./restore-archive-CpdUO-uS.mjs";
41
+ import { c as BackupDecryptError, f as generateBackupKeyPair, h as readBackupEncryptionHeader, l as BackupKeyFormatError, r as prepareRestoreArchive, s as SystemBackupService, t as BackupKeyRequiredError, u as BackupKeyMismatchError } from "./restore-archive-4ziluTU3.mjs";
39
42
  import { z } from "zod";
40
43
  import * as crypto$1 from "node:crypto";
41
44
  import { createHash, randomBytes, randomUUID } from "node:crypto";
@@ -47,13 +50,13 @@ import { dirname, isAbsolute, join, resolve } from "node:path";
47
50
  import * as os$1 from "node:os";
48
51
  import { tmpdir } from "node:os";
49
52
  import { pathToFileURL } from "node:url";
53
+ import { BOOT_RECOVERY_BACKOFF_MS, DATAPLANE_SECRET_HEADER, DEVICE_CHILDREN_BATCH_MAX, DeviceType, DisposerChain, ErrorI18nPayloadSchema, EventCategory, PEER_BYTES_CHUNK_BYTES, PEER_BYTES_MAX_OUTSTANDING, PEER_BYTES_STALL_MS, PEER_BYTES_TICKET_TTL_MS, ReadinessRegistry as ReadinessRegistry$1, asJsonObject, asString, collectZodSecretPaths, createDeviceProxy, createEvent, createEventBusSliceSource, createSecretsAccess, deviceOpsCapability, emitReadiness, errMsg, expandCapMethods as expandCapMethods$1, maskUrlCredentials, readErrorI18n, readNodePin, scopeKey as scopeKey$1, scrubKnownSecrets, sleep, withCapInputDefaults } from "@camstack/types/addon";
50
54
  import * as fs from "node:fs";
51
55
  import { accessSync, constants, createReadStream, createWriteStream, existsSync, readFileSync, readdirSync } from "node:fs";
52
56
  import { execFile, spawn } from "node:child_process";
53
57
  import * as util from "node:util";
54
58
  import { promisify } from "node:util";
55
59
  import { once } from "node:events";
56
- import { BOOT_RECOVERY_BACKOFF_MS, DATAPLANE_SECRET_HEADER, DEVICE_CHILDREN_BATCH_MAX, DeviceType, DisposerChain, ErrorI18nPayloadSchema, EventCategory, PEER_BYTES_CHUNK_BYTES, PEER_BYTES_MAX_OUTSTANDING, PEER_BYTES_STALL_MS, PEER_BYTES_TICKET_TTL_MS, ReadinessRegistry as ReadinessRegistry$1, asJsonObject, asString, collectZodSecretPaths, createDeviceProxy, createEvent, createEventBusSliceSource, createSecretsAccess, deviceOpsCapability, emitReadiness, errMsg, expandCapMethods as expandCapMethods$1, maskUrlCredentials, readErrorI18n, readNodePin, scopeKey as scopeKey$1, scrubKnownSecrets, sleep, withCapInputDefaults } from "@camstack/types/addon";
57
60
  import { createServer as createServer$1, request } from "node:http";
58
61
  import * as v8 from "node:v8";
59
62
  import { Session } from "node:inspector/promises";
@@ -1171,23 +1174,23 @@ function formatSocketPlane(window) {
1171
1174
  }
1172
1175
  //#endregion
1173
1176
  //#region src/kernel/moleculer/mesh-queue-report.ts
1174
- function isRecord$6(value) {
1177
+ function isRecord$7(value) {
1175
1178
  return typeof value === "object" && value !== null;
1176
1179
  }
1177
1180
  /** The shape `TcpWriter` keeps its sockets in. Anything else is not a mesh. */
1178
1181
  function writerSocketsOf(broker) {
1179
- if (!isRecord$6(broker)) return void 0;
1182
+ if (!isRecord$7(broker)) return void 0;
1180
1183
  const transit = broker["transit"];
1181
- if (!isRecord$6(transit)) return void 0;
1184
+ if (!isRecord$7(transit)) return void 0;
1182
1185
  const transporter = transit["tx"];
1183
- if (!isRecord$6(transporter)) return void 0;
1186
+ if (!isRecord$7(transporter)) return void 0;
1184
1187
  const writer = transporter["writer"];
1185
- if (!isRecord$6(writer)) return void 0;
1188
+ if (!isRecord$7(writer)) return void 0;
1186
1189
  const sockets = writer["sockets"];
1187
1190
  return sockets instanceof Map ? sockets : void 0;
1188
1191
  }
1189
1192
  function readSocket(nodeId, socket) {
1190
- if (typeof nodeId !== "string" || !isRecord$6(socket)) return void 0;
1193
+ if (typeof nodeId !== "string" || !isRecord$7(socket)) return void 0;
1191
1194
  const queuedBytes = socket["writableLength"];
1192
1195
  const bytesWritten = socket["bytesWritten"];
1193
1196
  if (typeof queuedBytes !== "number" || typeof bytesWritten !== "number") return void 0;
@@ -6307,7 +6310,7 @@ async function resolveUncached(options) {
6307
6310
  }
6308
6311
  //#endregion
6309
6312
  //#region src/kernel/addon-class-resolver.ts
6310
- function isRecord$5(value) {
6313
+ function isRecord$6(value) {
6311
6314
  return typeof value === "object" && value !== null && !Array.isArray(value);
6312
6315
  }
6313
6316
  /**
@@ -6331,14 +6334,14 @@ function isRecord$5(value) {
6331
6334
  function isAddonConstructor(fn) {
6332
6335
  if (typeof fn !== "function") return false;
6333
6336
  const proto = fn.prototype;
6334
- if (!isRecord$5(proto)) return false;
6337
+ if (!isRecord$6(proto)) return false;
6335
6338
  return typeof proto["initialize"] === "function";
6336
6339
  }
6337
6340
  function resolveAddonClass(mod) {
6338
6341
  let candidate = mod["default"];
6339
- if (isRecord$5(candidate) && "default" in candidate) candidate = candidate["default"];
6342
+ if (isRecord$6(candidate) && "default" in candidate) candidate = candidate["default"];
6340
6343
  if (isAddonConstructor(candidate)) return candidate;
6341
- if (isRecord$5(candidate)) {
6344
+ if (isRecord$6(candidate)) {
6342
6345
  const fn = Object.values(candidate).find(isAddonConstructor);
6343
6346
  if (fn) return fn;
6344
6347
  }
@@ -6347,7 +6350,7 @@ function resolveAddonClass(mod) {
6347
6350
  }
6348
6351
  //#endregion
6349
6352
  //#region src/kernel/addon-loader.ts
6350
- function isRecord$4(value) {
6353
+ function isRecord$5(value) {
6351
6354
  return typeof value === "object" && value !== null && !Array.isArray(value);
6352
6355
  }
6353
6356
  /**
@@ -6445,9 +6448,9 @@ async function importAddonModuleFresh(entryPath) {
6445
6448
  * AddonPackageManifest + AddonDeclaration.
6446
6449
  */
6447
6450
  function toAddonPackageManifest(value) {
6448
- if (!isRecord$4(value)) return void 0;
6451
+ if (!isRecord$5(value)) return void 0;
6449
6452
  if (!Array.isArray(value.addons)) return void 0;
6450
- if (!value.addons.every((a) => isRecord$4(a) && typeof a.id === "string")) return void 0;
6453
+ if (!value.addons.every((a) => isRecord$5(a) && typeof a.id === "string")) return void 0;
6451
6454
  return value;
6452
6455
  }
6453
6456
  /** Minimal no-op logger for default parameter */
@@ -6597,7 +6600,7 @@ var AddonLoader = class {
6597
6600
  }
6598
6601
  if (!fs.existsSync(entryPath)) throw new Error(`Entry not found: ${entryPath}`);
6599
6602
  const modUnknown = await importAddonModuleFresh(entryPath);
6600
- const mod = isRecord$4(modUnknown) ? modUnknown : {};
6603
+ const mod = isRecord$5(modUnknown) ? modUnknown : {};
6601
6604
  const AddonClass = resolveAddonClass(mod);
6602
6605
  if (!AddonClass) throw new Error(`No addon class in ${entryPath}`);
6603
6606
  this.addons.set(declaration.id, {
@@ -6613,7 +6616,7 @@ var AddonLoader = class {
6613
6616
  /** Load addon from a direct path (for development/testing) */
6614
6617
  async loadFromPath(addonId, modulePath, packageName, declaration, packageVersion = "0.0.0") {
6615
6618
  const modUnknown = await importAddonModuleFresh(modulePath);
6616
- const mod = isRecord$4(modUnknown) ? modUnknown : {};
6619
+ const mod = isRecord$5(modUnknown) ? modUnknown : {};
6617
6620
  const AddonClass = resolveAddonClass(mod);
6618
6621
  if (!AddonClass) throw new Error(`Module ${modulePath} has no default export`);
6619
6622
  this.addons.set(addonId, {
@@ -11533,92 +11536,11 @@ async function runHubAddonBoot(steps) {
11533
11536
  }
11534
11537
  //#endregion
11535
11538
  //#region src/kernel/settings-door-view.ts
11536
- function isRecord$3(value) {
11539
+ function isRecord$4(value) {
11537
11540
  return typeof value === "object" && value !== null && !Array.isArray(value);
11538
11541
  }
11539
11542
  function asBlob(value) {
11540
- return isRecord$3(value) ? value : {};
11541
- }
11542
- function unwrapValue(parsed) {
11543
- if (!isRecord$3(parsed) || !("value" in parsed)) return parsed;
11544
- return parsed.value;
11545
- }
11546
- /**
11547
- * Rows one page of a prefix read asks for.
11548
- *
11549
- * Below `SETTINGS_QUERY_DEFAULT_ROW_CAP` (2 000, `query-bounds.ts`) on purpose:
11550
- * this read now names its own bound, so the engine's defensive cap is never the
11551
- * thing that decides how much of the answer the caller gets.
11552
- */
11553
- var PREFIX_PAGE_ROWS = 1e3;
11554
- /**
11555
- * Pages one prefix read may take before it refuses.
11556
- *
11557
- * 512 pages is 512 000 rows — three orders of magnitude past the largest live
11558
- * scope (`addon-device-settings` for one addon over a ~1 019-device fleet) and
11559
- * still a bound, because a walk with no bound is the defect this file was
11560
- * fixing, not one to reintroduce.
11561
- */
11562
- var PREFIX_PAGE_BUDGET = 512;
11563
- /**
11564
- * Every row under `prefix`, or a throw — never the first 2 000 of them.
11565
- *
11566
- * **The defect.** This issued ONE `query` with no `limit`, and
11567
- * `queryDeclared` resolves an absent limit to `SETTINGS_QUERY_DEFAULT_ROW_CAP`
11568
- * and cuts the result there. On the live hub that fired 138 times in two hours
11569
- * (`settings-store query truncated by a defensive row cap`, collection
11570
- * `addon-device-settings`, `rowCap: 2000`, `requestedLimit: null`): a
11571
- * ~1 019-device fleet with more than two stored keys per device puts one
11572
- * addon's `<addonId>:` range past the cap. The engine warned; the door did not
11573
- * notice, and handed its caller a short map that is **shape-identical to a
11574
- * complete one**. Every device whose rows sort past the cap reads as "has no
11575
- * stored settings", which is exactly what a device with none looks like.
11576
- *
11577
- * **The fix is a keyset walk, not a bigger limit.** The cursor is the last id
11578
- * of the previous page, INCLUSIVE — so the page boundary re-reads exactly one
11579
- * row (ids are unique, so a full page always advances) and no row can fall
11580
- * between two pages the way an `OFFSET` walk drops rows under a concurrent
11581
- * write (D295 rule 1). Re-reading is free here: the accumulator is keyed by
11582
- * suffix, so the duplicate overwrites itself with its own value.
11583
- *
11584
- * `ORDER BY id` is not decoration — the cursor is only meaningful if the
11585
- * engine returns the range in the order the cursor advances through. It is the
11586
- * primary key, so the ordering is the range scan's own.
11587
- *
11588
- * @throws when the prefix cannot be expressed as a range and the collection is
11589
- * larger than one page, or when the walk exceeds {@link PREFIX_PAGE_BUDGET}.
11590
- * A throw is the point: a caller that cannot be answered completely must not
11591
- * be handed something that looks like a complete answer.
11592
- */
11593
- async function loadPrefixed(door, collection, prefix, decode = unwrapValue) {
11594
- const range = prefixRange(prefix);
11595
- const result = {};
11596
- let cursor = range?.lo ?? null;
11597
- for (let page = 0;; page++) {
11598
- if (page >= PREFIX_PAGE_BUDGET) throw new Error(`settings door: reading "${prefix}" from "${collection}" exceeded ${PREFIX_PAGE_BUDGET} pages of ${PREFIX_PAGE_ROWS} rows. Refusing to return the part that was read — a partial scope read is indistinguishable from an empty one.`);
11599
- const rows = await door.query({
11600
- collection,
11601
- filter: {
11602
- ...range !== null && cursor !== null ? { whereBetween: { id: [cursor, range.hi] } } : {},
11603
- orderBy: {
11604
- field: "id",
11605
- direction: "asc"
11606
- },
11607
- limit: PREFIX_PAGE_ROWS
11608
- }
11609
- });
11610
- for (const row of rows) {
11611
- if (range !== null && (row.id < range.lo || row.id >= range.hi)) continue;
11612
- if (!row.id.startsWith(prefix)) continue;
11613
- result[row.id.slice(prefix.length)] = decode(row.data);
11614
- }
11615
- if (rows.length < PREFIX_PAGE_ROWS) return result;
11616
- const last = rows[rows.length - 1];
11617
- if (last === void 0) return result;
11618
- if (range === null) throw new Error(`settings door: prefix "${prefix}" on "${collection}" cannot be expressed as an id range, and the unfiltered read filled a page — the answer would be partial. Use an ASCII, non-empty prefix.`);
11619
- if (last.id === cursor) throw new Error(`settings door: reading "${prefix}" from "${collection}" made no progress at id "${last.id}" — ids under a prefix must be unique.`);
11620
- cursor = last.id;
11621
- }
11543
+ return isRecord$4(value) ? value : {};
11622
11544
  }
11623
11545
  /**
11624
11546
  * Every `system-settings` row under `prefix`, keyed by the suffix — or a throw.
@@ -11646,106 +11568,34 @@ async function loadSystemSettingsPrefix(door, prefix, mayRead = () => true) {
11646
11568
  }
11647
11569
  return result;
11648
11570
  }
11649
- async function replacePrefixed(door, collection, prefix, values, wrap) {
11650
- const range = prefixRange(prefix);
11651
- if (range !== null) await door.deleteWhere({
11652
- collection,
11653
- filter: { whereBetween: { id: [range.lo, range.hi] } }
11654
- });
11655
- for (const [key, value] of Object.entries(values)) {
11656
- if (value === void 0) continue;
11657
- await door.set({
11658
- collection,
11659
- key: `${prefix}${key}`,
11660
- value: wrap(key, value)
11661
- });
11662
- }
11663
- }
11664
11571
  function createDoorSettingsView(addonId, door, sections) {
11665
- const addonPrefix = `${addonId}.`;
11572
+ const addonStore = createAddonSettingsStore(door, addonId);
11573
+ const deviceStore = createDeviceSettingsStore(door, addonId);
11666
11574
  return {
11667
11575
  async readAddonStore() {
11668
- return loadPrefixed(door, "addon-settings", addonPrefix);
11669
- },
11670
- async readAddonStoreKeyResult(key) {
11671
- try {
11672
- const row = await door.get({
11673
- collection: "addon-settings",
11674
- key: `${addonPrefix}${key}`
11675
- });
11676
- return row === void 0 ? { kind: "absent" } : {
11677
- kind: "present",
11678
- value: unwrapValue(row)
11679
- };
11680
- } catch (err) {
11681
- return {
11682
- kind: "unreadable",
11683
- reason: "store-read-failed",
11684
- error: err instanceof Error ? err.message : String(err)
11685
- };
11576
+ const read = await addonStore.read();
11577
+ switch (read.kind) {
11578
+ case "present": return { ...read.value };
11579
+ case "absent": return {};
11580
+ case "unreadable": throw new Error(`settings of "${addonId}" could not be read: ${read.error}`);
11686
11581
  }
11687
11582
  },
11688
- async writeAddonStore(patch) {
11689
- await replacePrefixed(door, "addon-settings", addonPrefix, {
11690
- ...await loadPrefixed(door, "addon-settings", addonPrefix),
11691
- ...patch
11692
- }, (key, value) => ({
11693
- addonId,
11694
- key,
11695
- value
11696
- }));
11583
+ async readAddonStoreKeyResult(key) {
11584
+ const read = await addonStore.readKey(key);
11585
+ return read.kind === "unreadable" ? {
11586
+ kind: "unreadable",
11587
+ reason: "store-read-failed",
11588
+ error: read.error
11589
+ } : read;
11697
11590
  },
11591
+ writeAddonStore: (patch) => addonStore.merge(patch),
11698
11592
  async readDeviceStore(deviceId) {
11699
- return loadPrefixed(door, "addon-device-settings", `${addonId}:${String(deviceId)}.`);
11700
- },
11701
- async readDeviceStoreBatch(deviceIds) {
11702
- const out = /* @__PURE__ */ new Map();
11703
- if (deviceIds.length === 0) return out;
11704
- for (const id of deviceIds) out.set(id, {});
11705
- const wanted = new Set(deviceIds);
11706
- const all = await loadPrefixed(door, "addon-device-settings", `${addonId}:`);
11707
- for (const [suffix, value] of Object.entries(all)) {
11708
- const dot = suffix.indexOf(".");
11709
- if (dot <= 0) continue;
11710
- const deviceId = Number(suffix.slice(0, dot));
11711
- if (!Number.isInteger(deviceId) || !wanted.has(deviceId)) continue;
11712
- const blob = out.get(deviceId);
11713
- if (blob === void 0) continue;
11714
- blob[suffix.slice(dot + 1)] = value;
11715
- }
11716
- return out;
11717
- },
11718
- async listDeviceStoreIds() {
11719
- const all = await loadPrefixed(door, "addon-device-settings", `${addonId}:`, () => null);
11720
- const ids = /* @__PURE__ */ new Set();
11721
- for (const suffix of Object.keys(all)) {
11722
- const dot = suffix.indexOf(".");
11723
- if (dot <= 0) continue;
11724
- const deviceId = Number(suffix.slice(0, dot));
11725
- if (Number.isInteger(deviceId) && deviceId > 0) ids.add(deviceId);
11726
- }
11727
- return [...ids].toSorted((a, b) => a - b);
11728
- },
11729
- async writeDeviceStore(deviceId, patch) {
11730
- const scope = `${addonId}:${String(deviceId)}.`;
11731
- await replacePrefixed(door, "addon-device-settings", scope, {
11732
- ...await loadPrefixed(door, "addon-device-settings", scope),
11733
- ...patch
11734
- }, (key, value) => ({
11735
- addonId,
11736
- deviceId: String(deviceId),
11737
- key,
11738
- value
11739
- }));
11740
- },
11741
- async clearDeviceStore(deviceId) {
11742
- const range = prefixRange(`${addonId}:${String(deviceId)}.`);
11743
- if (range === null) return;
11744
- await door.deleteWhere({
11745
- collection: "addon-device-settings",
11746
- filter: { whereBetween: { id: [range.lo, range.hi] } }
11747
- });
11593
+ return readDeviceOrThrow(deviceStore, deviceId);
11748
11594
  },
11595
+ readDeviceStoreBatch: (deviceIds) => deviceStore.readBatch(deviceIds),
11596
+ listDeviceStoreIds: () => deviceStore.listIds(),
11597
+ writeDeviceStore: (deviceId, patch) => deviceStore.merge(deviceId, patch),
11598
+ clearDeviceStore: (deviceId) => deviceStore.clear(deviceId),
11749
11599
  async readDeviceRuntimeState(deviceId) {
11750
11600
  return asBlob(await door.get({
11751
11601
  collection: "device-runtime-state",
@@ -14259,7 +14109,7 @@ import_js_yaml.default;
14259
14109
  * so that file stays one class. The `console.*` calls are the documented
14260
14110
  * residuals described on `ConfigManager` (no logger exists yet at boot).
14261
14111
  */
14262
- function isRecord$2(value) {
14112
+ function isRecord$3(value) {
14263
14113
  return typeof value === "object" && value !== null && !Array.isArray(value);
14264
14114
  }
14265
14115
  /** Sections that live in config.yaml. Everything else goes to the settings-store. */
@@ -14350,7 +14200,7 @@ function setNested(obj, configPath, value) {
14350
14200
  [head]: value
14351
14201
  };
14352
14202
  const existing = obj[head];
14353
- const child = isRecord$2(existing) ? existing : {};
14203
+ const child = isRecord$3(existing) ? existing : {};
14354
14204
  return {
14355
14205
  ...obj,
14356
14206
  [head]: setNested(child, rest.join("."), value)
@@ -14383,8 +14233,10 @@ function loadYaml(configPath) {
14383
14233
  //#endregion
14384
14234
  //#region src/kernel/store-settings-view.ts
14385
14235
  function createStoreSettingsView(addonId, cm) {
14236
+ const refuseDeviceSettings = async () => {
14237
+ throw new Error(`device settings of addon "${addonId}" are served by the settings-store door only`);
14238
+ };
14386
14239
  const viewAddonConfig = () => !cm.hasSyncStore() ? asJsonObject(cm.getBootstrap(`addons.${addonId}`)) ?? {} : cm.getAddonConfig(addonId);
14387
- const viewAddonDevice = (deviceId) => !cm.hasSyncStore() ? {} : cm.getAddonDevice(addonId, deviceId);
14388
14240
  return {
14389
14241
  async readAddonStore() {
14390
14242
  return viewAddonConfig();
@@ -14416,29 +14268,11 @@ function createStoreSettingsView(addonId, cm) {
14416
14268
  ...patch
14417
14269
  });
14418
14270
  },
14419
- async readDeviceStore(deviceId) {
14420
- return viewAddonDevice(String(deviceId));
14421
- },
14422
- async readDeviceStoreBatch(deviceIds) {
14423
- const out = /* @__PURE__ */ new Map();
14424
- for (const deviceId of deviceIds) try {
14425
- out.set(deviceId, viewAddonDevice(String(deviceId)));
14426
- } catch {
14427
- out.set(deviceId, {});
14428
- }
14429
- return out;
14430
- },
14431
- async writeDeviceStore(deviceId, patch) {
14432
- const key = String(deviceId);
14433
- const existing = viewAddonDevice(key);
14434
- cm.setAddonDevice(addonId, key, {
14435
- ...existing,
14436
- ...patch
14437
- });
14438
- },
14439
- async clearDeviceStore(deviceId) {
14440
- cm.clearAddonDevice(addonId, String(deviceId));
14441
- },
14271
+ readDeviceStore: refuseDeviceSettings,
14272
+ readDeviceStoreBatch: refuseDeviceSettings,
14273
+ listDeviceStoreIds: refuseDeviceSettings,
14274
+ writeDeviceStore: refuseDeviceSettings,
14275
+ clearDeviceStore: refuseDeviceSettings,
14442
14276
  async readDeviceRuntimeState(deviceId) {
14443
14277
  return cm.getDeviceRuntimeState(String(deviceId));
14444
14278
  },
@@ -14623,7 +14457,7 @@ var SystemSettingsMirror = class {
14623
14457
  };
14624
14458
  //#endregion
14625
14459
  //#region src/kernel/config-manager.ts
14626
- function isRecord$1(value) {
14460
+ function isRecord$2(value) {
14627
14461
  return typeof value === "object" && value !== null && !Array.isArray(value);
14628
14462
  }
14629
14463
  function errorText(err) {
@@ -14812,7 +14646,7 @@ var ConfigManager = class {
14812
14646
  async readAddonDeviceStrict(addonId, deviceId) {
14813
14647
  const door = this.settingsDoor;
14814
14648
  switch (this.backendMode()) {
14815
- case "store": return this.getAddonDevice(addonId, String(deviceId));
14649
+ case "store": break;
14816
14650
  case "door":
14817
14651
  if (door === null) break;
14818
14652
  try {
@@ -14830,34 +14664,6 @@ var ConfigManager = class {
14830
14664
  this.settingsStore.setAllAddon(addonId, config);
14831
14665
  }
14832
14666
  /**
14833
- * Read per-device overrides for a specific addon. Returns a flat record
14834
- * of field → value. Missing keys fall back to the addon-global value via
14835
- * `getAddonConfig(addonId)` — that composition is the resolver service's
14836
- * responsibility, not ConfigManager's.
14837
- * SYNC store only — THROWS without one (D675); async: {@link readAddonDeviceStrict}.
14838
- */
14839
- getAddonDevice(addonId, deviceId) {
14840
- if (this.settingsStore === null) throw this.refuseSyncRead("getAddonDevice", `addon "${addonId}" device "${deviceId}"`);
14841
- return this.settingsStore.getAddonDevice(addonId, deviceId);
14842
- }
14843
- /**
14844
- * Write (bulk-replace) per-device overrides for a specific addon.
14845
- * Throws if the settings store is not yet wired.
14846
- */
14847
- setAddonDevice(addonId, deviceId, values) {
14848
- if (this.settingsStore === null) throw new Error("[ConfigManager] SettingsStore not initialized -- call setSettingsStore() first");
14849
- this.settingsStore.setAddonDevice(addonId, deviceId, values);
14850
- }
14851
- /**
14852
- * Clear all per-device overrides for a specific addon/device pair.
14853
- * Resolution falls back to the addon-global value after this call.
14854
- * No-op if the settings store is not yet wired.
14855
- */
14856
- clearAddonDevice(addonId, deviceId) {
14857
- if (this.settingsStore === null) return;
14858
- this.settingsStore.clearAddonDevice(addonId, deviceId);
14859
- }
14860
- /**
14861
14667
  * The device's persisted runtime-state blob. THROWS before the store is
14862
14668
  * wired: a store not asked is not an empty row (D677, D675).
14863
14669
  */
@@ -14934,11 +14740,7 @@ var ConfigManager = class {
14934
14740
  writeAddonStore: (patch) => active().writeAddonStore(patch),
14935
14741
  readDeviceStore: (deviceId) => active().readDeviceStore(deviceId),
14936
14742
  readDeviceStoreBatch: (deviceIds) => active().readDeviceStoreBatch(deviceIds),
14937
- listDeviceStoreIds: async () => {
14938
- const list = active().listDeviceStoreIds;
14939
- if (list === void 0) throw new Error("listDeviceStoreIds is not available on this settings view");
14940
- return list.call(active());
14941
- },
14743
+ listDeviceStoreIds: () => active().listDeviceStoreIds(),
14942
14744
  writeDeviceStore: (deviceId, patch) => active().writeDeviceStore(deviceId, patch),
14943
14745
  clearDeviceStore: (deviceId) => active().clearDeviceStore(deviceId),
14944
14746
  getSection: (section) => active().getSection(section),
@@ -15014,7 +14816,7 @@ var ConfigManager = class {
15014
14816
  const keys = configPath.split(".");
15015
14817
  let current = this.bootstrapConfig;
15016
14818
  for (const key of keys) {
15017
- if (!isRecord$1(current)) return void 0;
14819
+ if (!isRecord$2(current)) return void 0;
15018
14820
  current = current[key];
15019
14821
  }
15020
14822
  return current;
@@ -15865,27 +15667,6 @@ function asName(value) {
15865
15667
  return typeof value === "string" && value.length > 0 ? value : null;
15866
15668
  }
15867
15669
  //#endregion
15868
- //#region src/kernel/moleculer/settings-read-guard.ts
15869
- /**
15870
- * Merge `patch` over the current value and write it — unless the read failed,
15871
- * in which case write NOTHING.
15872
- *
15873
- * Returns whether the write was attempted, so a caller can surface the refusal.
15874
- */
15875
- async function readModifyWrite(input) {
15876
- const current = await input.read();
15877
- if (current.kind === "failed") {
15878
- input.onRefused(current.error);
15879
- return false;
15880
- }
15881
- const base = current.kind === "value" ? current.value : {};
15882
- await input.write({
15883
- ...base,
15884
- ...input.patch
15885
- });
15886
- return true;
15887
- }
15888
- //#endregion
15889
15670
  //#region src/kernel/moleculer/children-batch-loader.ts
15890
15671
  /**
15891
15672
  * Narrow an untyped cap answer to the fields the reconcile reads.
@@ -16020,7 +15801,7 @@ var RuntimeStateLoadError = class extends Error {
16020
15801
  this.name = "RuntimeStateLoadError";
16021
15802
  }
16022
15803
  };
16023
- function isRecord(value) {
15804
+ function isRecord$1(value) {
16024
15805
  return typeof value === "object" && value !== null && !Array.isArray(value);
16025
15806
  }
16026
15807
  function errorMessage(err) {
@@ -16051,7 +15832,7 @@ async function loadRuntimeStateOrRefuse(load, logger, target) {
16051
15832
  } catch (err) {
16052
15833
  return refuse(errorMessage(err), err);
16053
15834
  }
16054
- if (!isRecord(answer)) return refuse(`answered ${answer === null ? "null" : typeof answer}`);
15835
+ if (!isRecord$1(answer)) return refuse(`answered ${answer === null ? "null" : typeof answer}`);
16055
15836
  return answer;
16056
15837
  }
16057
15838
  //#endregion
@@ -23142,6 +22923,261 @@ function createRemoteProviderProxy(broker, serviceName, capName, nodeId, methods
23142
22923
  return dispatcher;
23143
22924
  }
23144
22925
  //#endregion
22926
+ //#region src/kernel/sections-store.ts
22927
+ function createSectionsStore(door) {
22928
+ return {
22929
+ read: (section) => loadSystemSettingsPrefix(door, `${section}.`, (key) => storeMayAnswer(`${section}.${key}`)),
22930
+ write: async (section, patch) => {
22931
+ for (const [key, value] of Object.entries(patch)) await door.set({
22932
+ collection: "system-settings",
22933
+ key: `${section}.${key}`,
22934
+ value
22935
+ });
22936
+ }
22937
+ };
22938
+ }
22939
+ //#endregion
22940
+ //#region src/kernel/moleculer/runner-addon-settings.ts
22941
+ /** @param client the hub's `settingsStore` client; `undefined` makes every call THROW. */
22942
+ function createRunnerAddonSettings(client, addonId, before = async () => {}) {
22943
+ if (client === void 0) {
22944
+ const refuse = async () => {
22945
+ throw new Error(`settings of "${addonId}": settings store not wired`);
22946
+ };
22947
+ return {
22948
+ readAddonStore: refuse,
22949
+ readAddonStoreResult: async () => ({
22950
+ kind: "failed",
22951
+ error: "settings store not wired"
22952
+ }),
22953
+ readAddonStoreKeyResult: async () => ({
22954
+ kind: "unreadable",
22955
+ reason: "store-read-failed",
22956
+ error: "settings store not wired"
22957
+ }),
22958
+ writeAddonStore: refuse,
22959
+ getSection: refuse,
22960
+ setSection: refuse
22961
+ };
22962
+ }
22963
+ const door = {
22964
+ query: async (input) => {
22965
+ await before();
22966
+ return client.query.query(input);
22967
+ },
22968
+ get: async (input) => {
22969
+ await before();
22970
+ return client.get.query(input);
22971
+ },
22972
+ set: async (input) => {
22973
+ await before();
22974
+ return client.set.mutate(input);
22975
+ },
22976
+ transact: async (input) => {
22977
+ await before();
22978
+ return client.transact.mutate(input);
22979
+ }
22980
+ };
22981
+ const store = createAddonSettingsStore(door, addonId);
22982
+ const sections = createSectionsStore(door);
22983
+ return {
22984
+ readAddonStore: async () => {
22985
+ const read = await store.read();
22986
+ switch (read.kind) {
22987
+ case "present": return { ...read.value };
22988
+ case "absent": return {};
22989
+ case "unreadable": throw new Error(`settings of "${addonId}" could not be read: ${read.error}`);
22990
+ }
22991
+ },
22992
+ readAddonStoreResult: async () => {
22993
+ const read = await store.read();
22994
+ switch (read.kind) {
22995
+ case "present": return {
22996
+ kind: "value",
22997
+ value: { ...read.value }
22998
+ };
22999
+ case "absent": return { kind: "missing" };
23000
+ case "unreadable": return {
23001
+ kind: "failed",
23002
+ error: read.error
23003
+ };
23004
+ }
23005
+ },
23006
+ readAddonStoreKeyResult: async (key) => {
23007
+ const read = await store.readKey(key);
23008
+ return read.kind === "unreadable" ? {
23009
+ kind: "unreadable",
23010
+ reason: "store-read-failed",
23011
+ error: read.error
23012
+ } : read;
23013
+ },
23014
+ writeAddonStore: (patch) => store.merge(patch),
23015
+ getSection: (section) => sections.read(section),
23016
+ setSection: (section, patch) => sections.write(section, patch)
23017
+ };
23018
+ }
23019
+ //#endregion
23020
+ //#region src/kernel/legacy-blob-guard.ts
23021
+ /** Root keys retired long before D870: they never count as "settings still to migrate". */
23022
+ var OWN_RETIRED_ROOT_KEYS = new Set([
23023
+ "recordingConfigs",
23024
+ "recordingExports",
23025
+ "recordingOpsLog",
23026
+ "probeSnapshots",
23027
+ "rtspTokens",
23028
+ "profileMap"
23029
+ ]);
23030
+ /** Is `key` of `addonId` a root key that was retired (and must not be migrated)? */
23031
+ function isRetiredRootKey(addonId, key) {
23032
+ if (OWN_RETIRED_ROOT_KEYS.has(key)) return true;
23033
+ return RETIRED_SETTINGS_KEYS.some((spec) => spec.namespace === addonId && (spec.keys.includes(key) || spec.perNodeKeys.some((k) => key === k || key.startsWith(`${k}@`))));
23034
+ }
23035
+ var UnmigratedSettingsError = class extends Error {
23036
+ constructor(addonId, what) {
23037
+ super(`addon "${addonId}" refuses to start: ${what} - the settings it had are still in the per-addon tables and the standard store holds none. Run scripts/hub/migrate-device-store.ts --copy --apply (D870) before starting it.`);
23038
+ this.name = "UnmigratedSettingsError";
23039
+ }
23040
+ };
23041
+ async function countOf(client, input) {
23042
+ const n = await client.count.query(input);
23043
+ if (typeof n !== "number") throw new Error("settings store answered a non-number to a count");
23044
+ return n;
23045
+ }
23046
+ var LEGACY_PAGE = 500;
23047
+ var LEGACY_PAGE_BUDGET = 200;
23048
+ var ID_TAIL = "";
23049
+ function isRecord(value) {
23050
+ return typeof value === "object" && value !== null && !Array.isArray(value);
23051
+ }
23052
+ /**
23053
+ * Does the addon's per-device blob table hold a row the copy would actually
23054
+ * migrate - a positive numeric id with a non-empty record? An addon whose rows
23055
+ * the copy skips anyway must not be refused for them.
23056
+ */
23057
+ async function hasMigratableDeviceRow(client, addonId) {
23058
+ let cursor = "";
23059
+ for (let page = 0; page < LEGACY_PAGE_BUDGET; page++) {
23060
+ const rows = await client.query.query({
23061
+ namespace: addonId,
23062
+ collection: "addon-devices",
23063
+ filter: {
23064
+ whereBetween: { id: [cursor, ID_TAIL] },
23065
+ orderBy: {
23066
+ field: "id",
23067
+ direction: "asc"
23068
+ },
23069
+ limit: LEGACY_PAGE
23070
+ }
23071
+ });
23072
+ if (!Array.isArray(rows)) throw new Error("settings store answered a non-list to a query");
23073
+ let lastId = cursor;
23074
+ for (const row of rows) {
23075
+ if (!isRecord(row) || typeof row["id"] !== "string") continue;
23076
+ lastId = row["id"];
23077
+ const id = Number(row["id"]);
23078
+ const data = row["data"];
23079
+ if (Number.isInteger(id) && id > 0 && isRecord(data) && Object.keys(data).length > 0) return true;
23080
+ }
23081
+ if (rows.length < LEGACY_PAGE || lastId === cursor) return false;
23082
+ cursor = lastId;
23083
+ }
23084
+ throw new Error(`addon "${addonId}": more legacy blob pages than the guard will read`);
23085
+ }
23086
+ /**
23087
+ * THROWS when the addon has blob settings and the standard store has none of
23088
+ * its own. A store that cannot be asked also throws: it is not "no blobs".
23089
+ */
23090
+ async function assertLegacyBlobsMigrated(client, addonId, logger) {
23091
+ const legacyDevices = await hasMigratableDeviceRow(client, addonId);
23092
+ const root = await client.get.query({
23093
+ namespace: addonId,
23094
+ collection: "addon-settings",
23095
+ key: "root"
23096
+ });
23097
+ const legacySettings = isRecord(root) && Object.keys(root).some((key) => !isRetiredRootKey(addonId, key));
23098
+ if (!legacyDevices && !legacySettings) return;
23099
+ const rangeOf = (prefix) => {
23100
+ const range = prefixRange(prefix);
23101
+ if (range === null) throw new Error(`no id range for addon "${addonId}"`);
23102
+ return { whereBetween: { id: [range.lo, range.hi] } };
23103
+ };
23104
+ const newDevices = await countOf(client, {
23105
+ collection: "addon-device-settings",
23106
+ filter: rangeOf(`${addonId}:`)
23107
+ });
23108
+ const newSettings = await countOf(client, {
23109
+ collection: "addon-settings",
23110
+ filter: rangeOf(`${addonId}.`)
23111
+ });
23112
+ const problems = [];
23113
+ if (legacyDevices && newDevices === 0) problems.push("per-device blobs");
23114
+ if (legacySettings && newSettings === 0) problems.push("an addon settings blob");
23115
+ if (problems.length === 0) return;
23116
+ const error = new UnmigratedSettingsError(addonId, problems.join(" and "));
23117
+ logger.error(error.message, { meta: {
23118
+ addonId,
23119
+ newDevices,
23120
+ newSettings
23121
+ } });
23122
+ throw error;
23123
+ }
23124
+ //#endregion
23125
+ //#region src/kernel/moleculer/runner-device-settings.ts
23126
+ /**
23127
+ * The forked runner's `ctx.settings` device methods (D870).
23128
+ *
23129
+ * A runner does not hold the settings door; it holds a tRPC client of the hub's
23130
+ * `settings-store` cap. This file adapts that client to the door slice the
23131
+ * per-device store needs and hands back the five view methods — the SAME module
23132
+ * hub-main's view uses, so a device's settings are one set of rows whichever
23133
+ * process asks. Before D870 the runner kept its own `<addonId>:addon-devices`
23134
+ * blob per device; a hub-side write to the rows was invisible to it.
23135
+ */
23136
+ /** The door slice the per-device store needs, over a tRPC client. */
23137
+ function doorOverClient(client, before = async () => {}) {
23138
+ return {
23139
+ query: async (input) => {
23140
+ await before();
23141
+ return client.query.query(input);
23142
+ },
23143
+ transact: async (input) => {
23144
+ await before();
23145
+ return client.transact.mutate(input);
23146
+ },
23147
+ deleteWhere: async (input) => {
23148
+ await before();
23149
+ return client.deleteWhere.mutate(input);
23150
+ }
23151
+ };
23152
+ }
23153
+ /**
23154
+ * @param client the hub's `settingsStore` client, or `undefined` when the
23155
+ * runner has none — every call then THROWS: a store that could not be asked
23156
+ * is not an empty one.
23157
+ */
23158
+ function createRunnerDeviceSettings(client, addonId, before = async () => {}) {
23159
+ if (client === void 0) {
23160
+ const refuse = () => {
23161
+ throw new Error(`device settings of "${addonId}": settings store not wired`);
23162
+ };
23163
+ return {
23164
+ readDeviceStore: async () => refuse(),
23165
+ readDeviceStoreBatch: async () => refuse(),
23166
+ listDeviceStoreIds: async () => refuse(),
23167
+ writeDeviceStore: async () => refuse(),
23168
+ clearDeviceStore: async () => refuse()
23169
+ };
23170
+ }
23171
+ const store = createDeviceSettingsStore(doorOverClient(client, before), addonId);
23172
+ return {
23173
+ readDeviceStore: (deviceId) => readDeviceOrThrow(store, deviceId),
23174
+ readDeviceStoreBatch: (ids) => store.readBatch(ids),
23175
+ listDeviceStoreIds: () => store.listIds(),
23176
+ writeDeviceStore: (deviceId, patch) => store.merge(deviceId, patch),
23177
+ clearDeviceStore: (deviceId) => store.clear(deviceId)
23178
+ };
23179
+ }
23180
+ //#endregion
23145
23181
  //#region src/kernel/moleculer/addon-context-factory.ts
23146
23182
  /** This runtime's node id, regardless of mode (broker.nodeID or the UDS child nodeId). */
23147
23183
  function runtimeNodeId(runtime) {
@@ -23408,20 +23444,6 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
23408
23444
  }
23409
23445
  return r.kind === "value" ? r.value : {};
23410
23446
  };
23411
- /** Read-modify-write that REFUSES to write when the read failed. */
23412
- const mergeBlob = async (coll, key, patch, namespace) => {
23413
- await readModifyWrite({
23414
- read: () => readBlobResult(coll, key, namespace),
23415
- write: (next) => writeBlob(coll, key, next, namespace),
23416
- patch,
23417
- onRefused: (error) => {
23418
- scopedLogger.error(`settings merge REFUSED (${coll}/${key}) — read failed, refusing to overwrite with a partial blob`, { meta: {
23419
- error,
23420
- patchKeys: Object.keys(patch)
23421
- } });
23422
- }
23423
- });
23424
- };
23425
23447
  const writeBlob = async (coll, key, value, namespace) => {
23426
23448
  if (!settingsApi) return;
23427
23449
  try {
@@ -23444,38 +23466,24 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
23444
23466
  } });
23445
23467
  }
23446
23468
  };
23447
- const rmwChains = /* @__PURE__ */ new Map();
23448
- const serializeRmw = (coll, key, op) => {
23449
- const chainKey = `${coll}/${key}`;
23450
- const next = (rmwChains.get(chainKey) ?? Promise.resolve()).catch(() => void 0).then(op);
23451
- rmwChains.set(chainKey, next);
23452
- next.finally(() => {
23453
- if (rmwChains.get(chainKey) === next) rmwChains.delete(chainKey);
23454
- }).catch(() => void 0);
23455
- return next;
23469
+ const deviceSettingsClient = Reflect.get(api, "settingsStore");
23470
+ let blobsChecked = null;
23471
+ const bootGuard = () => {
23472
+ const client = deviceSettingsClient;
23473
+ if (client === void 0) return Promise.resolve();
23474
+ blobsChecked ??= assertLegacyBlobsMigrated({
23475
+ get: { query: (input) => client.get.query(input) },
23476
+ query: { query: (input) => client.query.query(input) },
23477
+ count: { query: (input) => client.count.query(input) }
23478
+ }, addonId, scopedLogger).catch((err) => {
23479
+ blobsChecked = null;
23480
+ throw err;
23481
+ });
23482
+ return blobsChecked;
23456
23483
  };
23457
23484
  const settingsView = {
23458
- async readAddonStore() {
23459
- return readBlob("addon-settings", "root", addonId);
23460
- },
23461
- readAddonStoreResult: () => readBlobResult("addon-settings", "root", addonId),
23462
- async writeAddonStore(patch) {
23463
- return serializeRmw("addon-settings", "root", async () => {
23464
- await mergeBlob("addon-settings", "root", patch, addonId);
23465
- });
23466
- },
23467
- async readDeviceStore(deviceId) {
23468
- return readBlob("addon-devices", String(deviceId), addonId);
23469
- },
23470
- async writeDeviceStore(deviceId, patch) {
23471
- const key = String(deviceId);
23472
- return serializeRmw("addon-devices", key, async () => {
23473
- await mergeBlob("addon-devices", key, patch, addonId);
23474
- });
23475
- },
23476
- async clearDeviceStore(deviceId) {
23477
- await writeBlob("addon-devices", String(deviceId), {}, addonId);
23478
- },
23485
+ ...createRunnerAddonSettings(deviceSettingsClient, addonId, bootGuard),
23486
+ ...createRunnerDeviceSettings(deviceSettingsClient, addonId, bootGuard),
23479
23487
  async readDeviceRuntimeState(deviceId) {
23480
23488
  return readBlob("device-runtime-state", String(deviceId));
23481
23489
  },
@@ -23484,14 +23492,6 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
23484
23492
  },
23485
23493
  async clearDeviceRuntimeState(deviceId) {
23486
23494
  await writeBlob("device-runtime-state", String(deviceId), {});
23487
- },
23488
- async getSection(section) {
23489
- return readBlob("sections", section);
23490
- },
23491
- async setSection(section, patch) {
23492
- return serializeRmw("sections", section, async () => {
23493
- await mergeBlob("sections", section, patch);
23494
- });
23495
23495
  }
23496
23496
  };
23497
23497
  const readinessRegistry = () => runtime.mode === "broker" ? getOrInitReadinessRegistry(runtime.broker, scopedEventBus, scopedLogger) : getOrInitReadinessRegistryForClient(runtime.client, scopedEventBus, scopedLogger, nodeId);