@ceralive/modem-control 1.0.0 → 1.2.0
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/README.md +341 -0
- package/dist/backend/at-lease.d.ts +59 -0
- package/dist/backend/at-lease.js +118 -0
- package/dist/backend/cell-info.d.ts +46 -0
- package/dist/backend/cell-info.js +124 -0
- package/dist/backend/constants.d.ts +24 -0
- package/{src/backend/constants.ts → dist/backend/constants.js} +4 -9
- package/dist/backend/device-classifier.d.ts +46 -0
- package/dist/backend/device-classifier.js +258 -0
- package/dist/backend/enrichment.d.ts +28 -0
- package/dist/backend/enrichment.js +59 -0
- package/dist/backend/features.d.ts +61 -0
- package/dist/backend/features.js +109 -0
- package/dist/backend/identity-ladder.d.ts +57 -0
- package/dist/backend/identity-ladder.js +158 -0
- package/dist/backend/identity-registry.d.ts +51 -0
- package/dist/backend/identity-registry.js +101 -0
- package/dist/backend/index.d.ts +30 -0
- package/dist/backend/index.js +35 -0
- package/dist/backend/lifecycle-interlock.d.ts +25 -0
- package/dist/backend/lifecycle-interlock.js +18 -0
- package/dist/backend/managed-objects.d.ts +39 -0
- package/dist/backend/managed-objects.js +82 -0
- package/dist/backend/mapping.d.ts +11 -0
- package/dist/backend/mapping.js +148 -0
- package/dist/backend/mm-backend.d.ts +44 -0
- package/dist/backend/mm-backend.js +154 -0
- package/dist/backend/mm-location.d.ts +21 -0
- package/dist/backend/mm-location.js +237 -0
- package/dist/backend/mm-mutations.d.ts +42 -0
- package/dist/backend/mm-mutations.js +259 -0
- package/dist/backend/modem-actor.d.ts +41 -0
- package/dist/backend/modem-actor.js +78 -0
- package/dist/backend/nm-auto-apn.d.ts +54 -0
- package/dist/backend/nm-auto-apn.js +124 -0
- package/dist/backend/nm-gsm-fields.d.ts +23 -0
- package/dist/backend/nm-gsm-fields.js +107 -0
- package/dist/backend/nmcli-nm-port.d.ts +28 -0
- package/dist/backend/nmcli-nm-port.js +173 -0
- package/dist/backend/nmcli-runner.d.ts +24 -0
- package/dist/backend/nmcli-runner.js +35 -0
- package/dist/backend/observer.d.ts +37 -0
- package/dist/backend/observer.js +219 -0
- package/dist/backend/power-contract.d.ts +49 -0
- package/dist/backend/power-contract.js +34 -0
- package/dist/backend/recovery-attribution.d.ts +32 -0
- package/dist/backend/recovery-attribution.js +57 -0
- package/dist/backend/recovery-budget.d.ts +45 -0
- package/dist/backend/recovery-budget.js +44 -0
- package/dist/backend/recovery-ladder.d.ts +94 -0
- package/dist/backend/recovery-ladder.js +116 -0
- package/dist/backend/router-ethernet.d.ts +19 -0
- package/dist/backend/router-ethernet.js +66 -0
- package/dist/backend/row-store.d.ts +13 -0
- package/dist/backend/row-store.js +82 -0
- package/dist/backend/signal-setup.d.ts +30 -0
- package/dist/backend/signal-setup.js +92 -0
- package/dist/backend/sim-unlock.d.ts +13 -0
- package/dist/backend/sim-unlock.js +153 -0
- package/dist/backend/transition-preconditions.d.ts +101 -0
- package/dist/backend/transition-preconditions.js +132 -0
- package/dist/backend/usage/accounting.d.ts +39 -0
- package/dist/backend/usage/accounting.js +73 -0
- package/dist/backend/usage/billing-cycle.d.ts +11 -0
- package/dist/backend/usage/billing-cycle.js +39 -0
- package/dist/backend/usage/boot-id.d.ts +6 -0
- package/{src/backend/usage/boot-id.ts → dist/backend/usage/boot-id.js} +7 -7
- package/dist/backend/usage/index.d.ts +8 -0
- package/dist/backend/usage/index.js +11 -0
- package/dist/backend/usage/policy-store.d.ts +50 -0
- package/dist/backend/usage/policy-store.js +161 -0
- package/dist/backend/usage/policy-write.d.ts +65 -0
- package/dist/backend/usage/policy-write.js +112 -0
- package/dist/backend/usage/proc-net-dev.d.ts +18 -0
- package/{src/backend/usage/proc-net-dev.ts → dist/backend/usage/proc-net-dev.js} +39 -47
- package/dist/backend/usage/sampler.d.ts +75 -0
- package/dist/backend/usage/sampler.js +211 -0
- package/dist/backend/usage/store.d.ts +38 -0
- package/dist/backend/usage/store.js +126 -0
- package/dist/backend/usb-device-snapshot.d.ts +31 -0
- package/dist/backend/usb-device-snapshot.js +1 -0
- package/dist/backend/usb-enumerator.d.ts +21 -0
- package/dist/backend/usb-enumerator.js +153 -0
- package/dist/backend/usb-mode-transition.d.ts +29 -0
- package/dist/backend/usb-mode-transition.js +216 -0
- package/dist/band/band-names.d.ts +42 -0
- package/dist/band/band-names.js +150 -0
- package/dist/band/certification.d.ts +84 -0
- package/dist/band/certification.js +127 -0
- package/dist/band/certified-bands.json +4 -0
- package/dist/band/index.d.ts +2 -0
- package/dist/band/index.js +8 -0
- package/dist/capability/detect.d.ts +52 -0
- package/dist/capability/detect.js +86 -0
- package/dist/capability/five-g-preference.d.ts +104 -0
- package/dist/capability/five-g-preference.js +171 -0
- package/dist/capability/index.d.ts +3 -0
- package/dist/capability/index.js +10 -0
- package/dist/capability/support-claim.d.ts +39 -0
- package/dist/capability/support-claim.js +81 -0
- package/dist/domain/brand.d.ts +10 -0
- package/dist/domain/brand.js +21 -0
- package/dist/domain/errors.d.ts +32 -0
- package/dist/domain/errors.js +48 -0
- package/dist/domain/generation.d.ts +8 -0
- package/dist/domain/generation.js +12 -0
- package/dist/domain/guards.d.ts +6 -0
- package/dist/domain/guards.js +127 -0
- package/dist/domain/identity.d.ts +109 -0
- package/dist/domain/identity.js +86 -0
- package/dist/domain/index.d.ts +14 -0
- package/dist/domain/index.js +18 -0
- package/dist/domain/mm-enums.d.ts +12 -0
- package/dist/domain/mm-enums.js +139 -0
- package/dist/domain/modem-presentation.d.ts +10 -0
- package/dist/domain/modem-presentation.js +39 -0
- package/dist/domain/observation.d.ts +39 -0
- package/dist/domain/observation.js +4 -0
- package/dist/domain/operation.d.ts +118 -0
- package/dist/domain/operation.js +85 -0
- package/dist/domain/physical-identity.d.ts +43 -0
- package/dist/domain/physical-identity.js +113 -0
- package/{src/domain/policy.ts → dist/domain/policy.d.ts} +34 -69
- package/dist/domain/policy.js +38 -0
- package/dist/domain/shadow-divergence.d.ts +27 -0
- package/dist/domain/shadow-divergence.js +70 -0
- package/dist/domain/snapshot.d.ts +53 -0
- package/dist/domain/snapshot.js +75 -0
- package/{src/domain/state.ts → dist/domain/state.d.ts} +23 -122
- package/dist/domain/state.js +38 -0
- package/dist/fcc/coverage.d.ts +63 -0
- package/dist/fcc/coverage.js +102 -0
- package/dist/fcc/index.d.ts +3 -0
- package/dist/fcc/index.js +12 -0
- package/dist/fcc/policy-store.d.ts +40 -0
- package/dist/fcc/policy-store.js +146 -0
- package/dist/fcc/policy-write.d.ts +32 -0
- package/dist/fcc/policy-write.js +37 -0
- package/dist/hardware/hilink-protocol.d.ts +39 -0
- package/dist/hardware/hilink-protocol.js +58 -0
- package/dist/hardware/index.d.ts +3 -0
- package/dist/hardware/index.js +15 -0
- package/dist/hardware/router-parsers.d.ts +89 -0
- package/dist/hardware/router-parsers.js +234 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +27 -0
- package/dist/journal/codec.d.ts +44 -0
- package/dist/journal/codec.js +198 -0
- package/dist/journal/engine.d.ts +28 -0
- package/dist/journal/engine.js +68 -0
- package/dist/journal/entry.d.ts +74 -0
- package/dist/journal/entry.js +56 -0
- package/dist/journal/index.d.ts +6 -0
- package/dist/journal/index.js +6 -0
- package/dist/journal/legacy-ceraui.d.ts +73 -0
- package/dist/journal/legacy-ceraui.js +227 -0
- package/dist/journal/recovery.d.ts +58 -0
- package/dist/journal/recovery.js +117 -0
- package/dist/journal/store.d.ts +55 -0
- package/dist/journal/store.js +150 -0
- package/dist/location/fix-state.d.ts +53 -0
- package/dist/location/fix-state.js +75 -0
- package/dist/location/index.d.ts +2 -0
- package/dist/location/index.js +8 -0
- package/dist/location/nmea.d.ts +10 -0
- package/dist/location/nmea.js +89 -0
- package/dist/observations/envelope.d.ts +63 -0
- package/dist/observations/envelope.js +77 -0
- package/dist/observations/freshness.d.ts +28 -0
- package/dist/observations/freshness.js +76 -0
- package/dist/observations/index.d.ts +13 -0
- package/dist/observations/index.js +24 -0
- package/dist/observations/metric.d.ts +61 -0
- package/dist/observations/metric.js +73 -0
- package/dist/observations/model.d.ts +80 -0
- package/dist/observations/model.js +11 -0
- package/dist/observations/provenance.d.ts +94 -0
- package/dist/observations/provenance.js +67 -0
- package/dist/observations/raw.d.ts +42 -0
- package/dist/observations/raw.js +146 -0
- package/dist/observations/reading.d.ts +51 -0
- package/dist/observations/reading.js +65 -0
- package/dist/observations/sources/hilink.d.ts +10 -0
- package/dist/observations/sources/hilink.js +84 -0
- package/dist/observations/sources/modemmanager.d.ts +18 -0
- package/dist/observations/sources/modemmanager.js +239 -0
- package/dist/observations/sources/router-shared.d.ts +29 -0
- package/dist/observations/sources/router-shared.js +68 -0
- package/dist/observations/sources/ufi.d.ts +10 -0
- package/dist/observations/sources/ufi.js +111 -0
- package/dist/observations/sources/zte.d.ts +7 -0
- package/dist/observations/sources/zte.js +71 -0
- package/dist/observations/state-separation.d.ts +64 -0
- package/dist/observations/state-separation.js +52 -0
- package/dist/operation-ids.d.ts +2 -0
- package/dist/operation-ids.js +26 -0
- package/dist/operations/contracts.d.ts +72 -0
- package/dist/operations/contracts.js +1 -0
- package/dist/operations/index.d.ts +1 -0
- package/dist/operations/index.js +1 -0
- package/dist/operations/operation-engine.d.ts +16 -0
- package/dist/operations/operation-engine.js +194 -0
- package/dist/ports/index.d.ts +12 -0
- package/{src/ports/index.ts → dist/ports/index.js} +12 -8
- package/dist/ports/location.d.ts +87 -0
- package/dist/ports/location.js +36 -0
- package/dist/ports/modem-manager.d.ts +89 -0
- package/dist/ports/modem-manager.js +9 -0
- package/dist/ports/mutation-admission.d.ts +27 -0
- package/dist/ports/mutation-admission.js +9 -0
- package/dist/ports/network-manager.d.ts +68 -0
- package/dist/ports/network-manager.js +13 -0
- package/{src/ports/observation.ts → dist/ports/observation.d.ts} +16 -27
- package/dist/ports/observation.js +7 -0
- package/dist/ports/ops.d.ts +44 -0
- package/dist/ports/ops.js +16 -0
- package/{src/ports/receipts.ts → dist/ports/receipts.d.ts} +5 -27
- package/dist/ports/receipts.js +9 -0
- package/dist/ports/reconcile.d.ts +33 -0
- package/dist/ports/reconcile.js +200 -0
- package/dist/ports/resource-ownership.d.ts +29 -0
- package/dist/ports/resource-ownership.js +1 -0
- package/dist/ports/router.d.ts +19 -0
- package/dist/ports/router.js +7 -0
- package/dist/ports/sms.d.ts +64 -0
- package/dist/ports/sms.js +24 -0
- package/dist/ports/uhubctl.d.ts +6 -0
- package/dist/ports/uhubctl.js +1 -0
- package/dist/providers/contracts.d.ts +124 -0
- package/dist/providers/contracts.js +10 -0
- package/dist/providers/huawei-hilink/index.d.ts +2 -0
- package/dist/providers/huawei-hilink/index.js +2 -0
- package/dist/providers/huawei-hilink/operations.d.ts +20 -0
- package/dist/providers/huawei-hilink/operations.js +56 -0
- package/dist/providers/huawei-hilink/provider.d.ts +52 -0
- package/dist/providers/huawei-hilink/provider.js +76 -0
- package/dist/providers/huawei-hilink/runtime.d.ts +22 -0
- package/dist/providers/huawei-hilink/runtime.js +171 -0
- package/dist/providers/huawei-hilink/session.d.ts +28 -0
- package/dist/providers/huawei-hilink/session.js +120 -0
- package/dist/providers/huawei-hilink/transport.d.ts +19 -0
- package/dist/providers/huawei-hilink/transport.js +1 -0
- package/dist/providers/index.d.ts +8 -0
- package/dist/providers/index.js +8 -0
- package/dist/providers/matcher.d.ts +3 -0
- package/dist/providers/matcher.js +205 -0
- package/dist/providers/modem-manager/errors.d.ts +7 -0
- package/dist/providers/modem-manager/errors.js +37 -0
- package/dist/providers/modem-manager/generic-operations.d.ts +10 -0
- package/dist/providers/modem-manager/generic-operations.js +209 -0
- package/dist/providers/modem-manager/index.d.ts +4 -0
- package/dist/providers/modem-manager/index.js +4 -0
- package/dist/providers/modem-manager/module-operations.d.ts +21 -0
- package/dist/providers/modem-manager/module-operations.js +118 -0
- package/dist/providers/modem-manager/provider.d.ts +41 -0
- package/dist/providers/modem-manager/provider.js +155 -0
- package/dist/providers/modem-manager/runtime-composition-operation.d.ts +32 -0
- package/dist/providers/modem-manager/runtime-composition-operation.js +151 -0
- package/dist/providers/modem-manager/snapshot.d.ts +5 -0
- package/dist/providers/modem-manager/snapshot.js +204 -0
- package/dist/providers/modem-manager/types.d.ts +137 -0
- package/dist/providers/modem-manager/types.js +1 -0
- package/dist/providers/network-manager/adapter.d.ts +71 -0
- package/dist/providers/network-manager/adapter.js +348 -0
- package/dist/providers/network-manager/index.d.ts +2 -0
- package/dist/providers/network-manager/index.js +2 -0
- package/dist/providers/network-manager/types.d.ts +171 -0
- package/dist/providers/network-manager/types.js +77 -0
- package/dist/providers/registry.d.ts +13 -0
- package/dist/providers/registry.js +33 -0
- package/dist/providers/ufi-himi/index.d.ts +6 -0
- package/dist/providers/ufi-himi/index.js +6 -0
- package/dist/providers/ufi-himi/operations.d.ts +41 -0
- package/dist/providers/ufi-himi/operations.js +66 -0
- package/dist/providers/ufi-himi/prohibitions.d.ts +62 -0
- package/dist/providers/ufi-himi/prohibitions.js +88 -0
- package/dist/providers/ufi-himi/provider.d.ts +41 -0
- package/dist/providers/ufi-himi/provider.js +204 -0
- package/dist/providers/ufi-himi/qualcomm-evidence.d.ts +32 -0
- package/dist/providers/ufi-himi/qualcomm-evidence.js +51 -0
- package/dist/providers/ufi-himi/session.d.ts +46 -0
- package/dist/providers/ufi-himi/session.js +92 -0
- package/dist/providers/ufi-himi/transport.d.ts +29 -0
- package/dist/providers/ufi-himi/transport.js +25 -0
- package/dist/providers/zte-goform/index.d.ts +2 -0
- package/dist/providers/zte-goform/index.js +2 -0
- package/dist/providers/zte-goform/provider.d.ts +56 -0
- package/dist/providers/zte-goform/provider.js +101 -0
- package/dist/providers/zte-goform/session.d.ts +23 -0
- package/dist/providers/zte-goform/session.js +197 -0
- package/dist/providers/zte-goform/transport.d.ts +16 -0
- package/dist/providers/zte-goform/transport.js +1 -0
- package/dist/radio/band-truth.d.ts +50 -0
- package/dist/radio/band-truth.js +92 -0
- package/dist/radio/index.d.ts +3 -0
- package/dist/radio/index.js +10 -0
- package/dist/radio/mode-combinations.d.ts +88 -0
- package/dist/radio/mode-combinations.js +198 -0
- package/dist/radio/mode-truth.d.ts +67 -0
- package/dist/radio/mode-truth.js +112 -0
- package/dist/redact.d.ts +15 -0
- package/dist/redact.js +189 -0
- package/dist/safety/composition-root.d.ts +28 -0
- package/dist/safety/composition-root.js +65 -0
- package/dist/safety/flock-resource-ownership.d.ts +11 -0
- package/dist/safety/flock-resource-ownership.js +138 -0
- package/dist/safety/index.d.ts +2 -0
- package/dist/safety/index.js +2 -0
- package/dist/sms/dbus-messaging.d.ts +17 -0
- package/dist/sms/dbus-messaging.js +185 -0
- package/dist/sms/inbox-store.d.ts +10 -0
- package/dist/sms/inbox-store.js +82 -0
- package/dist/sms/index.d.ts +4 -0
- package/dist/sms/index.js +10 -0
- package/dist/sms/mmcli-parse.d.ts +54 -0
- package/dist/sms/mmcli-parse.js +224 -0
- package/dist/sms/normalize.d.ts +42 -0
- package/dist/sms/normalize.js +95 -0
- package/dist/testing/domain-fakes.d.ts +58 -0
- package/dist/testing/domain-fakes.js +98 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +17 -0
- package/dist/testing/provider-fakes.d.ts +45 -0
- package/dist/testing/provider-fakes.js +64 -0
- package/dist/transport/calls.d.ts +9 -0
- package/dist/transport/calls.js +88 -0
- package/dist/transport/codec.d.ts +3 -0
- package/dist/transport/codec.js +207 -0
- package/dist/transport/dbus-native.d.ts +57 -0
- package/dist/transport/dbus-native.js +17 -0
- package/dist/transport/errors.d.ts +21 -0
- package/{src/transport/errors.ts → dist/transport/errors.js} +34 -46
- package/dist/transport/index.d.ts +4 -0
- package/dist/transport/index.js +9 -0
- package/dist/transport/signals.d.ts +15 -0
- package/dist/transport/signals.js +123 -0
- package/dist/transport/signature.d.ts +7 -0
- package/dist/transport/signature.js +94 -0
- package/dist/transport/transport.d.ts +2 -0
- package/dist/transport/transport.js +202 -0
- package/dist/transport/types.d.ts +61 -0
- package/dist/transport/types.js +19 -0
- package/dist/usb-mode/catalog-schema.d.ts +139 -0
- package/dist/usb-mode/catalog-schema.js +97 -0
- package/dist/usb-mode/catalog.d.ts +21 -0
- package/{src/usb-mode/catalog.ts → dist/usb-mode/catalog.js} +10 -32
- package/dist/usb-mode/certified-catalog.json +67 -0
- package/dist/usb-mode/index.d.ts +6 -0
- package/dist/usb-mode/index.js +16 -0
- package/dist/usb-mode/ingestion.d.ts +111 -0
- package/dist/usb-mode/ingestion.js +187 -0
- package/dist/usb-mode/promotion-review.d.ts +21 -0
- package/dist/usb-mode/promotion-review.js +87 -0
- package/dist/usb-mode/runtime-capability.d.ts +59 -0
- package/dist/usb-mode/runtime-capability.js +157 -0
- package/dist/usb-mode/usb-devices-parse.d.ts +36 -0
- package/dist/usb-mode/usb-devices-parse.js +157 -0
- package/dist/ussd/calls.d.ts +32 -0
- package/dist/ussd/calls.js +96 -0
- package/dist/ussd/index.d.ts +5 -0
- package/dist/ussd/index.js +12 -0
- package/dist/ussd/mm-ussd.d.ts +37 -0
- package/dist/ussd/mm-ussd.js +205 -0
- package/dist/ussd/refusal.d.ts +53 -0
- package/dist/ussd/refusal.js +154 -0
- package/dist/ussd/registration.d.ts +20 -0
- package/dist/ussd/registration.js +101 -0
- package/dist/ussd/session.d.ts +106 -0
- package/dist/ussd/session.js +163 -0
- package/package.json +38 -4
- package/src/backend/at-lease.test.ts +0 -106
- package/src/backend/at-lease.ts +0 -158
- package/src/backend/cell-info.test.ts +0 -154
- package/src/backend/cell-info.ts +0 -160
- package/src/backend/device-classifier.test.ts +0 -168
- package/src/backend/device-classifier.ts +0 -248
- package/src/backend/enrichment.ts +0 -96
- package/src/backend/features.test.ts +0 -162
- package/src/backend/features.ts +0 -179
- package/src/backend/identity-ladder.test.ts +0 -117
- package/src/backend/identity-ladder.ts +0 -221
- package/src/backend/identity-registry.test.ts +0 -89
- package/src/backend/identity-registry.ts +0 -151
- package/src/backend/index.ts +0 -236
- package/src/backend/lifecycle-interlock.ts +0 -38
- package/src/backend/managed-objects.ts +0 -108
- package/src/backend/mapping.ts +0 -160
- package/src/backend/mm-backend.ts +0 -191
- package/src/backend/mm-mutations.ts +0 -228
- package/src/backend/modem-actor.test.ts +0 -95
- package/src/backend/modem-actor.ts +0 -112
- package/src/backend/nm-auto-apn.ts +0 -161
- package/src/backend/nm-gsm-fields.ts +0 -122
- package/src/backend/nmcli-nm-port.ts +0 -228
- package/src/backend/nmcli-runner.ts +0 -52
- package/src/backend/observer.ts +0 -297
- package/src/backend/power-contract.test.ts +0 -40
- package/src/backend/power-contract.ts +0 -83
- package/src/backend/recovery-attribution.test.ts +0 -102
- package/src/backend/recovery-attribution.ts +0 -86
- package/src/backend/recovery-budget.test.ts +0 -64
- package/src/backend/recovery-budget.ts +0 -84
- package/src/backend/recovery-ladder.test.ts +0 -257
- package/src/backend/recovery-ladder.ts +0 -249
- package/src/backend/router-ethernet.test.ts +0 -71
- package/src/backend/router-ethernet.ts +0 -90
- package/src/backend/row-store.ts +0 -105
- package/src/backend/signal-setup.ts +0 -112
- package/src/backend/sim-unlock.ts +0 -193
- package/src/backend/transition-preconditions.ts +0 -149
- package/src/backend/uhubctl-power-hook.test.ts +0 -274
- package/src/backend/uhubctl-power-hook.ts +0 -377
- package/src/backend/usage/accounting.test.ts +0 -147
- package/src/backend/usage/accounting.ts +0 -123
- package/src/backend/usage/billing-cycle.test.ts +0 -62
- package/src/backend/usage/billing-cycle.ts +0 -45
- package/src/backend/usage/index.ts +0 -60
- package/src/backend/usage/policy-store.test.ts +0 -164
- package/src/backend/usage/policy-store.ts +0 -216
- package/src/backend/usage/policy-write.test.ts +0 -198
- package/src/backend/usage/policy-write.ts +0 -207
- package/src/backend/usage/proc-net-dev.test.ts +0 -56
- package/src/backend/usage/sampler.test.ts +0 -327
- package/src/backend/usage/sampler.ts +0 -282
- package/src/backend/usage/store.test.ts +0 -148
- package/src/backend/usage/store.ts +0 -177
- package/src/backend/usb-enumerator.test.ts +0 -87
- package/src/backend/usb-enumerator.ts +0 -181
- package/src/backend/usb-mode-transition.test.ts +0 -323
- package/src/backend/usb-mode-transition.ts +0 -253
- package/src/domain/brand.ts +0 -29
- package/src/domain/errors.ts +0 -77
- package/src/domain/guards.test.ts +0 -218
- package/src/domain/guards.ts +0 -144
- package/src/domain/identity.test.ts +0 -83
- package/src/domain/identity.ts +0 -165
- package/src/domain/index.ts +0 -12
- package/src/domain/snapshot.test.ts +0 -266
- package/src/domain/snapshot.ts +0 -120
- package/src/index.test.ts +0 -6
- package/src/index.ts +0 -15
- package/src/ports/README.md +0 -61
- package/src/ports/forbidden-surface.test.ts +0 -241
- package/src/ports/modem-manager.ts +0 -72
- package/src/ports/network-manager.ts +0 -87
- package/src/ports/ops.ts +0 -60
- package/src/ports/ops.type-test.ts +0 -39
- package/src/ports/receipts.test.ts +0 -153
- package/src/ports/reconcile.test.ts +0 -152
- package/src/ports/reconcile.ts +0 -338
- package/src/ports/router.ts +0 -29
- package/src/redact.test.ts +0 -82
- package/src/redact.ts +0 -73
- package/src/transport/README.md +0 -65
- package/src/transport/calls.ts +0 -113
- package/src/transport/characterization.test.ts +0 -260
- package/src/transport/codec.test.ts +0 -118
- package/src/transport/codec.ts +0 -240
- package/src/transport/conformance-python.test.ts +0 -152
- package/src/transport/conformance-same-lib.test.ts +0 -115
- package/src/transport/dbus-native-lib.d.ts +0 -19
- package/src/transport/dbus-native.ts +0 -85
- package/src/transport/index.ts +0 -30
- package/src/transport/no-library-leak.test.ts +0 -61
- package/src/transport/reliability.test.ts +0 -173
- package/src/transport/signals.ts +0 -150
- package/src/transport/signature.ts +0 -110
- package/src/transport/test-support/fake-service.ts +0 -168
- package/src/transport/test-support/independent-producer.py +0 -110
- package/src/transport/test-support/private-bus.ts +0 -66
- package/src/transport/transport.ts +0 -250
- package/src/transport/types.ts +0 -118
- package/src/usb-mode/catalog-schema.test.ts +0 -181
- package/src/usb-mode/catalog-schema.ts +0 -113
- package/src/usb-mode/certified-catalog.json +0 -67
- package/src/usb-mode/index.ts +0 -58
- package/src/usb-mode/ingestion.test.ts +0 -268
- package/src/usb-mode/ingestion.ts +0 -297
- package/src/usb-mode/promotion-review.ts +0 -117
- package/src/usb-mode/usb-devices-parse.ts +0 -196
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// The claims every router admin API makes identically, in one place.
|
|
2
|
+
//
|
|
3
|
+
// HiLink, goform and HIMI are three vendor HTTP surfaces over a modem, and they share
|
|
4
|
+
// a capability boundary: none of them exposes ModemManager's modem state, its 3GPP
|
|
5
|
+
// registration enum, its lock enum or its SIM-type enum. Those read `unsupported`,
|
|
6
|
+
// which is a durable claim about the SOURCE rather than about one poll — and stating
|
|
7
|
+
// it once here is what stops the three normalizers from drifting into three different
|
|
8
|
+
// answers for the same structural fact.
|
|
9
|
+
//
|
|
10
|
+
// SIM PRESENCE is deliberately NOT claimed. Each vendor reports a presence code of
|
|
11
|
+
// its own (`SimStatus`, `simcard_state`, `simstate`) with vendor semantics, and no
|
|
12
|
+
// migrated decoder covers them; guessing one would be exactly the invented reading
|
|
13
|
+
// this layer exists to prevent. It reads `not-reported` and the vendor code stays
|
|
14
|
+
// verbatim in the diagnostics block for the per-vendor providers to claim later.
|
|
15
|
+
import { isUninformativeIdentity, modemHardwareLabel, modemHardwareName, } from '../../domain/index.js';
|
|
16
|
+
import { knownMetric, unknownMetric } from '../metric.js';
|
|
17
|
+
export function unsupportedRadioMetric(provenance) {
|
|
18
|
+
return unknownMetric('unsupported', provenance([]));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The SIM block every router source produces.
|
|
22
|
+
*
|
|
23
|
+
* `vendorPresenceField` NAMES the vendor's own presence code when the payload carried
|
|
24
|
+
* one, so the evidence records that a code exists and was deliberately not decoded —
|
|
25
|
+
* distinct from `no-evidence`, which says the payload offered nothing at all. Neither
|
|
26
|
+
* one is ever `absent`: claiming absence from a vendor code whose semantics no
|
|
27
|
+
* migrated decoder covers is precisely the invented reading this layer refuses.
|
|
28
|
+
*/
|
|
29
|
+
export function routerSim(provenance, vendorPresenceField) {
|
|
30
|
+
// The vendor field is NAMED in the evidence but is NOT consumed: no metric claims its
|
|
31
|
+
// value, so it must stay in the diagnostics block's `unmapped` set, verbatim, for the
|
|
32
|
+
// per-vendor provider that will one day decode it with evidence.
|
|
33
|
+
return {
|
|
34
|
+
presence: unknownMetric('not-reported', provenance([])),
|
|
35
|
+
presenceEvidence: vendorPresenceField === undefined
|
|
36
|
+
? { kind: 'no-evidence', inspected: [] }
|
|
37
|
+
: { kind: 'vendor-code-unclaimed', field: vendorPresenceField },
|
|
38
|
+
lockRequired: unknownMetric('unsupported', provenance([])),
|
|
39
|
+
kind: unknownMetric('unsupported', provenance([])),
|
|
40
|
+
esimStatus: unknownMetric('unsupported', provenance([])),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** Router admin APIs report no measurement-recency flag; that is a source capability claim. */
|
|
44
|
+
export function unsupportedQualityRecent(provenance) {
|
|
45
|
+
return unknownMetric('unsupported', provenance([]));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A router's product name, screened by the migrated presentation rules.
|
|
49
|
+
*
|
|
50
|
+
* `modemHardwareLabel` falls back to a generic label when its input says nothing, and
|
|
51
|
+
* publishing that as a KNOWN value would be reporting a name no device gave. So an
|
|
52
|
+
* uninformative product string is refused up front and reads `not-reported` instead.
|
|
53
|
+
*/
|
|
54
|
+
export function routerHardware(provenance, product) {
|
|
55
|
+
if (product === undefined || isUninformativeIdentity(product.name)) {
|
|
56
|
+
const source = provenance(product === undefined ? [] : [product.field]);
|
|
57
|
+
return {
|
|
58
|
+
label: unknownMetric('not-reported', source),
|
|
59
|
+
displayName: unknownMetric('not-reported', source),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const source = provenance([product.field]);
|
|
63
|
+
const identity = { model: product.name };
|
|
64
|
+
return {
|
|
65
|
+
label: knownMetric(modemHardwareLabel(identity), source),
|
|
66
|
+
displayName: knownMetric(modemHardwareName(identity), source),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ObservationEnvelope } from '../../domain/index.js';
|
|
2
|
+
import { type NormalizationContext } from '../envelope.js';
|
|
3
|
+
import type { NormalizedModemObservation } from '../model.js';
|
|
4
|
+
export type UfiObservationInput = {
|
|
5
|
+
readonly sysinfo: string;
|
|
6
|
+
readonly overview: string;
|
|
7
|
+
readonly status: string;
|
|
8
|
+
readonly produceInfo?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function normalizeUfiObservation(input: UfiObservationInput, context: NormalizationContext): ObservationEnvelope<NormalizedModemObservation>;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// Qualcomm UFI / HIMI → normalized observation.
|
|
2
|
+
//
|
|
3
|
+
// UFI answers three endpoints per read, each an envelope of its own (`{reply, params}`),
|
|
4
|
+
// and the migrated `parseUfiSignal` already folds them in the vendor's own precedence
|
|
5
|
+
// order. This module keeps all three bodies verbatim — including the `reply` field,
|
|
6
|
+
// which is how a `SessionOut` refusal is told apart from a body that simply lacked the
|
|
7
|
+
// key — and reports the ONE signal reading the source actually provides.
|
|
8
|
+
//
|
|
9
|
+
// Its overview endpoint returns an IMSI and an ICCID. They are retained, because
|
|
10
|
+
// normalization does not get to decide what a diagnostician may need, and they are
|
|
11
|
+
// masked by the shared key-based redactor on the way to anything that renders. That
|
|
12
|
+
// split — retain here, redact at the boundary — is why `raw` is documented as a
|
|
13
|
+
// redaction-class surface rather than as safe-by-default data.
|
|
14
|
+
import { parseUfiDetails, parseUfiSignal } from '../../hardware/router-parsers.js';
|
|
15
|
+
import { freshObservation, metricProvenance } from '../envelope.js';
|
|
16
|
+
import { metricFromRouterSignal, unknownMetric } from '../metric.js';
|
|
17
|
+
import { createObservationDiagnostics, } from '../provenance.js';
|
|
18
|
+
import { hasRawField, mergeRawRecords, normalizeRawValue, parseJsonObject, prefixRawRecord, rawKey, } from '../raw.js';
|
|
19
|
+
import { routerHardware, routerSim, unsupportedQualityRecent, unsupportedRadioMetric, } from './router-shared.js';
|
|
20
|
+
const SOURCE = 'ufi-himiapi';
|
|
21
|
+
const SYSINFO = 'sysinfo';
|
|
22
|
+
const OVERVIEW = 'overview';
|
|
23
|
+
const STATUS = 'status';
|
|
24
|
+
const PRODUCE_INFO = 'produce-info';
|
|
25
|
+
const SESSION_REFUSAL = 'SessionOut';
|
|
26
|
+
/** HIMI's own SIM presence code. Named as evidence, never decoded into a presence. */
|
|
27
|
+
const SIM_STATE = rawKey(SYSINFO, 'simstate');
|
|
28
|
+
export function normalizeUfiObservation(input, context) {
|
|
29
|
+
const bodies = [
|
|
30
|
+
[SYSINFO, input.sysinfo],
|
|
31
|
+
[OVERVIEW, input.overview],
|
|
32
|
+
[STATUS, input.status],
|
|
33
|
+
[PRODUCE_INFO, input.produceInfo ?? ''],
|
|
34
|
+
];
|
|
35
|
+
const notes = [];
|
|
36
|
+
const records = bodies.map(([name, text]) => {
|
|
37
|
+
const parsed = parseJsonObject(text);
|
|
38
|
+
if (parsed === undefined && text.trim() !== '') {
|
|
39
|
+
notes.push({ code: 'unparseable-body', field: name });
|
|
40
|
+
}
|
|
41
|
+
if (parsed?.reply === SESSION_REFUSAL) {
|
|
42
|
+
notes.push({ code: 'auth-expired', field: rawKey(name, 'reply') });
|
|
43
|
+
}
|
|
44
|
+
return prefixRawRecord(name, flattenUfiEnvelope(parsed));
|
|
45
|
+
});
|
|
46
|
+
const raw = mergeRawRecords(...records);
|
|
47
|
+
const consumed = [];
|
|
48
|
+
const provenance = (fields, authority) => {
|
|
49
|
+
consumed.push(...fields);
|
|
50
|
+
return authority === undefined
|
|
51
|
+
? metricProvenance(SOURCE, context, fields)
|
|
52
|
+
: metricProvenance(SOURCE, context, fields, authority);
|
|
53
|
+
};
|
|
54
|
+
const signalModel = parseUfiSignal({
|
|
55
|
+
sysinfo: input.sysinfo,
|
|
56
|
+
overview: input.overview,
|
|
57
|
+
status: input.status,
|
|
58
|
+
});
|
|
59
|
+
const details = parseUfiDetails({
|
|
60
|
+
overview: input.overview,
|
|
61
|
+
sysinfo: input.sysinfo,
|
|
62
|
+
...(input.produceInfo === undefined ? {} : { produceInfo: input.produceInfo }),
|
|
63
|
+
});
|
|
64
|
+
const product = details?.product;
|
|
65
|
+
return freshObservation(SOURCE, context, {
|
|
66
|
+
source: SOURCE,
|
|
67
|
+
hardware: routerHardware(provenance, product === undefined
|
|
68
|
+
? undefined
|
|
69
|
+
: { name: product, field: rawKey(PRODUCE_INFO, 'productname') }),
|
|
70
|
+
radio: {
|
|
71
|
+
modemState: unsupportedRadioMetric(provenance),
|
|
72
|
+
registration: unsupportedRadioMetric(provenance),
|
|
73
|
+
accessTechnologies: unknownMetric('unsupported', provenance([])),
|
|
74
|
+
modeLabel: unknownMetric('not-reported', provenance([])),
|
|
75
|
+
},
|
|
76
|
+
signal: {
|
|
77
|
+
quality: unknownMetric('unsupported', provenance([])),
|
|
78
|
+
qualityRecent: unsupportedQualityRecent(provenance),
|
|
79
|
+
bars: metricFromRouterSignal(signalModel.bars, provenance([])),
|
|
80
|
+
maxBars: metricFromRouterSignal(signalModel.max_bars, provenance([])),
|
|
81
|
+
dbm: metricFromRouterSignal(signalModel.dbm, provenance([
|
|
82
|
+
rawKey(SYSINFO, 'SIGNAL'),
|
|
83
|
+
rawKey(OVERVIEW, 'SIGNAL'),
|
|
84
|
+
rawKey(STATUS, 'signalStrength'),
|
|
85
|
+
])),
|
|
86
|
+
rsrp: metricFromRouterSignal(signalModel.rsrp, provenance([])),
|
|
87
|
+
rsrq: metricFromRouterSignal(signalModel.rsrq, provenance([])),
|
|
88
|
+
snr: metricFromRouterSignal(signalModel.snr, provenance([])),
|
|
89
|
+
sinr: metricFromRouterSignal(signalModel.sinr, provenance([])),
|
|
90
|
+
},
|
|
91
|
+
sim: routerSim(provenance, hasRawField(raw, SIM_STATE) ? SIM_STATE : undefined),
|
|
92
|
+
diagnostics: createObservationDiagnostics({ source: SOURCE, raw, consumed, notes }),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/** `{reply, params}` flattened to one level — `params` members keep their own names. */
|
|
96
|
+
function flattenUfiEnvelope(parsed) {
|
|
97
|
+
const out = {};
|
|
98
|
+
if (parsed === undefined) {
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
102
|
+
if (key === 'params' && typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
103
|
+
for (const [field, param] of Object.entries(value)) {
|
|
104
|
+
out[field] = normalizeRawValue(param);
|
|
105
|
+
}
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
out[key] = normalizeRawValue(value);
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ObservationEnvelope } from '../../domain/index.js';
|
|
2
|
+
import { type NormalizationContext } from '../envelope.js';
|
|
3
|
+
import type { NormalizedModemObservation } from '../model.js';
|
|
4
|
+
export type ZteObservationInput = {
|
|
5
|
+
readonly body: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function normalizeZteObservation(input: ZteObservationInput, context: NormalizationContext): ObservationEnvelope<NormalizedModemObservation>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// ZTE goform → normalized observation.
|
|
2
|
+
//
|
|
3
|
+
// One goform response answers many keys at once, so the migrated `parseZteSignal` and
|
|
4
|
+
// `parseZteDetails` are both driven from the SAME body here — and everything neither
|
|
5
|
+
// of them claims (`wan_lte_ca`, `rmcc`, `lte_pci`, the cell id, the roaming flag) is
|
|
6
|
+
// retained verbatim in the diagnostics block. That retention is the point: a
|
|
7
|
+
// carrier-aggregation flag no normalized field has a slot for is still the difference
|
|
8
|
+
// between a diagnosable report and a shrug.
|
|
9
|
+
//
|
|
10
|
+
// A body that will not parse is an OBSERVATION whose metrics are `malformed`, not an
|
|
11
|
+
// unavailable one. The bytes arrived; what failed was decoding them, and an
|
|
12
|
+
// unavailable envelope would have to discard them to say so.
|
|
13
|
+
import { parseZteDetails, parseZteSignal } from '../../hardware/router-parsers.js';
|
|
14
|
+
import { freshObservation, metricProvenance } from '../envelope.js';
|
|
15
|
+
import { knownMetric, metricFromRouterSignal, unknownMetric, } from '../metric.js';
|
|
16
|
+
import { createObservationDiagnostics } from '../provenance.js';
|
|
17
|
+
import { hasRawField, parseJsonRecord, prefixRawRecord, rawKey } from '../raw.js';
|
|
18
|
+
import { routerHardware, routerSim, unsupportedQualityRecent, unsupportedRadioMetric, } from './router-shared.js';
|
|
19
|
+
const SOURCE = 'zte-goform';
|
|
20
|
+
const BODY = 'goform';
|
|
21
|
+
/** ZTE's own SIM presence code. Named as evidence, never decoded into a presence. */
|
|
22
|
+
const SIM_CARD_STATE = rawKey(BODY, 'simcard_state');
|
|
23
|
+
export function normalizeZteObservation(input, context) {
|
|
24
|
+
const parsed = parseJsonRecord(input.body);
|
|
25
|
+
const raw = prefixRawRecord(BODY, parsed);
|
|
26
|
+
const notes = [];
|
|
27
|
+
if (parsed === undefined) {
|
|
28
|
+
notes.push({ code: input.body.trim() === '' ? 'empty-body' : 'unparseable-body', field: BODY });
|
|
29
|
+
}
|
|
30
|
+
const consumed = [];
|
|
31
|
+
const provenance = (fields, authority) => {
|
|
32
|
+
consumed.push(...fields);
|
|
33
|
+
return authority === undefined
|
|
34
|
+
? metricProvenance(SOURCE, context, fields)
|
|
35
|
+
: metricProvenance(SOURCE, context, fields, authority);
|
|
36
|
+
};
|
|
37
|
+
const signalModel = parseZteSignal(input.body);
|
|
38
|
+
const details = parseZteDetails(input.body);
|
|
39
|
+
return freshObservation(SOURCE, context, {
|
|
40
|
+
source: SOURCE,
|
|
41
|
+
hardware: routerHardware(provenance, undefined),
|
|
42
|
+
radio: {
|
|
43
|
+
modemState: unsupportedRadioMetric(provenance),
|
|
44
|
+
registration: unsupportedRadioMetric(provenance),
|
|
45
|
+
accessTechnologies: unknownMetric('unsupported', provenance([])),
|
|
46
|
+
modeLabel: normalizeModeLabel(details?.network_type, parsed !== undefined, provenance),
|
|
47
|
+
},
|
|
48
|
+
signal: {
|
|
49
|
+
quality: unknownMetric('unsupported', provenance([])),
|
|
50
|
+
qualityRecent: unsupportedQualityRecent(provenance),
|
|
51
|
+
bars: metricFromRouterSignal(signalModel.bars, provenance([rawKey(BODY, 'signalbar')], 'derived')),
|
|
52
|
+
// The goform payload carries no maximum; the migrated parser states the
|
|
53
|
+
// vendor's fixed five-bar scale, which is a derivation and says so.
|
|
54
|
+
maxBars: metricFromRouterSignal(signalModel.max_bars, provenance([], 'derived')),
|
|
55
|
+
dbm: metricFromRouterSignal(signalModel.dbm, provenance([rawKey(BODY, 'rssi')])),
|
|
56
|
+
rsrp: metricFromRouterSignal(signalModel.rsrp, provenance([rawKey(BODY, 'lte_rsrp')])),
|
|
57
|
+
rsrq: metricFromRouterSignal(signalModel.rsrq, provenance([rawKey(BODY, 'lte_rsrq')])),
|
|
58
|
+
snr: metricFromRouterSignal(signalModel.snr, provenance([rawKey(BODY, 'lte_snr')])),
|
|
59
|
+
sinr: metricFromRouterSignal(signalModel.sinr, provenance([])),
|
|
60
|
+
},
|
|
61
|
+
sim: routerSim(provenance, hasRawField(raw, SIM_CARD_STATE) ? SIM_CARD_STATE : undefined),
|
|
62
|
+
diagnostics: createObservationDiagnostics({ source: SOURCE, raw, consumed, notes }),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function normalizeModeLabel(networkType, parsed, provenance) {
|
|
66
|
+
const source = provenance([rawKey(BODY, 'network_type')]);
|
|
67
|
+
if (networkType !== undefined) {
|
|
68
|
+
return knownMetric(networkType, source);
|
|
69
|
+
}
|
|
70
|
+
return unknownMetric(parsed ? 'not-reported' : 'malformed', source);
|
|
71
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { DeviceGeneration, EpochMillis, ObservationEnvelope } from '../domain/index.js';
|
|
2
|
+
/** What an operator asked for. Not a claim that anything acted on it. */
|
|
3
|
+
export type DesiredProfile<T> = {
|
|
4
|
+
readonly kind: 'desired';
|
|
5
|
+
readonly profile: T;
|
|
6
|
+
readonly requestedAt: EpochMillis;
|
|
7
|
+
/** Opaque origin label (an RPC caller, a policy engine). Never a credential. */
|
|
8
|
+
readonly requestedBy: string;
|
|
9
|
+
};
|
|
10
|
+
/** What was actually put into force, and by which operation, in which generation. */
|
|
11
|
+
export type AppliedConfiguration<T> = {
|
|
12
|
+
readonly kind: 'applied';
|
|
13
|
+
readonly configuration: T;
|
|
14
|
+
readonly appliedAt: EpochMillis;
|
|
15
|
+
readonly generation: DeviceGeneration;
|
|
16
|
+
readonly operationId: string;
|
|
17
|
+
};
|
|
18
|
+
/** What the device itself reports — always through an envelope, never bare. */
|
|
19
|
+
export type ObservedState<T> = {
|
|
20
|
+
readonly kind: 'observed';
|
|
21
|
+
readonly observation: ObservationEnvelope<T>;
|
|
22
|
+
};
|
|
23
|
+
export declare function desiredProfile<T>(profile: T, requestedAt: EpochMillis, requestedBy: string): DesiredProfile<T>;
|
|
24
|
+
export declare function appliedConfiguration<T>(input: {
|
|
25
|
+
readonly configuration: T;
|
|
26
|
+
readonly appliedAt: EpochMillis;
|
|
27
|
+
readonly generation: DeviceGeneration;
|
|
28
|
+
readonly operationId: string;
|
|
29
|
+
}): AppliedConfiguration<T>;
|
|
30
|
+
export declare function observedState<T>(observation: ObservationEnvelope<T>): ObservedState<T>;
|
|
31
|
+
/**
|
|
32
|
+
* The three-slot view. There is deliberately no fourth field holding a merged
|
|
33
|
+
* "effective" value: an effective value is a RENDERING decision, and computing one
|
|
34
|
+
* here would bake a policy every consumer would then have to work around.
|
|
35
|
+
*/
|
|
36
|
+
export type ModemStateView<TDesired, TApplied, TObserved> = {
|
|
37
|
+
readonly desired: DesiredProfile<TDesired> | null;
|
|
38
|
+
readonly applied: AppliedConfiguration<TApplied> | null;
|
|
39
|
+
readonly observed: ObservedState<TObserved>;
|
|
40
|
+
};
|
|
41
|
+
export declare const STATE_VIEW_SLOTS: readonly ['desired', 'applied', 'observed'];
|
|
42
|
+
export type StateViewSlot = (typeof STATE_VIEW_SLOTS)[number];
|
|
43
|
+
export type SlotComparison = {
|
|
44
|
+
readonly status: 'aligned';
|
|
45
|
+
} | {
|
|
46
|
+
readonly status: 'diverged';
|
|
47
|
+
}
|
|
48
|
+
/** One side has nothing to compare — NOT the same as agreeing. */
|
|
49
|
+
| {
|
|
50
|
+
readonly status: 'indeterminate';
|
|
51
|
+
readonly missing: StateViewSlot;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Two independent comparisons, never one verdict.
|
|
55
|
+
*
|
|
56
|
+
* `desiredVsApplied` answers "did our write happen"; `appliedVsObserved` answers "did
|
|
57
|
+
* it stick". A single boolean cannot distinguish a request that was never carried out
|
|
58
|
+
* from one the network undid a second later, and those need opposite responses.
|
|
59
|
+
*/
|
|
60
|
+
export type StateDivergence = {
|
|
61
|
+
readonly desiredVsApplied: SlotComparison;
|
|
62
|
+
readonly appliedVsObserved: SlotComparison;
|
|
63
|
+
};
|
|
64
|
+
export declare function describeStateDivergence<T>(view: ModemStateView<T, T, T>, equals: (left: T, right: T) => boolean): StateDivergence;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Desired, applied and observed are THREE things, and this module refuses to make
|
|
2
|
+
// them one.
|
|
3
|
+
//
|
|
4
|
+
// The pattern is NetworkManager's: a connection PROFILE is what an operator asked
|
|
5
|
+
// for, an active connection's BEARER is what was actually put into force, and the
|
|
6
|
+
// device's own reported state is what the hardware is currently doing. All three are
|
|
7
|
+
// routinely different at once — a profile edited but not re-activated, a bearer that
|
|
8
|
+
// survived a profile change, a device that dropped registration without anything
|
|
9
|
+
// having been re-applied — and a merged "current state" blob has to pick one of them
|
|
10
|
+
// and silently lose the other two.
|
|
11
|
+
//
|
|
12
|
+
// Concretely, collapsing them makes these questions unanswerable:
|
|
13
|
+
// · did the operator's last change actually take effect, or is it only pending?
|
|
14
|
+
// · is what the device reports the consequence of our write, or of the network?
|
|
15
|
+
// · when a rollback is needed, what exactly do we roll back TO?
|
|
16
|
+
//
|
|
17
|
+
// So the three live in separate slots with separate `kind` discriminants, and the
|
|
18
|
+
// comparison below reports TWO independent divergences instead of one verdict.
|
|
19
|
+
// `NetworkManagerAdapter` (a later change) consumes exactly this shape.
|
|
20
|
+
import { viewEnvelope } from './envelope.js';
|
|
21
|
+
export function desiredProfile(profile, requestedAt, requestedBy) {
|
|
22
|
+
return { kind: 'desired', profile, requestedAt, requestedBy };
|
|
23
|
+
}
|
|
24
|
+
export function appliedConfiguration(input) {
|
|
25
|
+
return { kind: 'applied', ...input };
|
|
26
|
+
}
|
|
27
|
+
export function observedState(observation) {
|
|
28
|
+
return { kind: 'observed', observation };
|
|
29
|
+
}
|
|
30
|
+
export const STATE_VIEW_SLOTS = ['desired', 'applied', 'observed'];
|
|
31
|
+
export function describeStateDivergence(view, equals) {
|
|
32
|
+
return {
|
|
33
|
+
desiredVsApplied: compareSlots(view.desired === null ? undefined : view.desired.profile, view.applied === null ? undefined : view.applied.configuration, view.desired === null ? 'desired' : 'applied', equals),
|
|
34
|
+
appliedVsObserved: compareSlots(view.applied === null ? undefined : view.applied.configuration, observedValue(view.observed), view.applied === null ? 'applied' : 'observed', equals),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The observed value, or `undefined` when the observation carries none.
|
|
39
|
+
*
|
|
40
|
+
* An unavailable observation is `indeterminate` against anything, never `aligned`:
|
|
41
|
+
* "we could not read it" is not evidence that it matches.
|
|
42
|
+
*/
|
|
43
|
+
function observedValue(observed) {
|
|
44
|
+
const view = viewEnvelope(observed.observation);
|
|
45
|
+
return view.kind === 'unavailable' ? undefined : view.value;
|
|
46
|
+
}
|
|
47
|
+
function compareSlots(left, right, missing, equals) {
|
|
48
|
+
if (left === undefined || right === undefined) {
|
|
49
|
+
return { status: 'indeterminate', missing };
|
|
50
|
+
}
|
|
51
|
+
return equals(left, right) ? { status: 'aligned' } : { status: 'diverged' };
|
|
52
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const MODEM_OPERATION_IDS: readonly ["modemmanager.radio-modes", "modemmanager.mode-combination", "modemmanager.bands", "modemmanager.signal", "modemmanager.sim", "modemmanager.power", "modemmanager.usb-composition", "status", "signal", "mode", "data", "ufi.signal.read", "ufi.details.read", "nv.write", "efs.write", "identity.write", "calibration.write", "firmware.flash", "edl.automation", "driver.blind-retry", "interface.blind-retry", "diag.write", "diag.info-probe", "shell.transport-fallback"];
|
|
2
|
+
export type ModemOperationId = (typeof MODEM_OPERATION_IDS)[number];
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const MODEM_OPERATION_IDS = Object.freeze([
|
|
2
|
+
'modemmanager.radio-modes',
|
|
3
|
+
'modemmanager.mode-combination',
|
|
4
|
+
'modemmanager.bands',
|
|
5
|
+
'modemmanager.signal',
|
|
6
|
+
'modemmanager.sim',
|
|
7
|
+
'modemmanager.power',
|
|
8
|
+
'modemmanager.usb-composition',
|
|
9
|
+
'status',
|
|
10
|
+
'signal',
|
|
11
|
+
'mode',
|
|
12
|
+
'data',
|
|
13
|
+
'ufi.signal.read',
|
|
14
|
+
'ufi.details.read',
|
|
15
|
+
'nv.write',
|
|
16
|
+
'efs.write',
|
|
17
|
+
'identity.write',
|
|
18
|
+
'calibration.write',
|
|
19
|
+
'firmware.flash',
|
|
20
|
+
'edl.automation',
|
|
21
|
+
'driver.blind-retry',
|
|
22
|
+
'interface.blind-retry',
|
|
23
|
+
'diag.write',
|
|
24
|
+
'diag.info-probe',
|
|
25
|
+
'shell.transport-fallback',
|
|
26
|
+
]);
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { DeviceGeneration, OperationCompletion, OperationDescriptor, OperationResult, PhysicalModemId } from '../domain/index.js';
|
|
2
|
+
export type OperationPreconditionRequest = {
|
|
3
|
+
readonly operationId: string;
|
|
4
|
+
readonly physicalModemId: PhysicalModemId;
|
|
5
|
+
readonly generation: DeviceGeneration;
|
|
6
|
+
readonly descriptorId: string;
|
|
7
|
+
readonly preconditions: readonly string[];
|
|
8
|
+
};
|
|
9
|
+
export type OperationPreconditionResult = {
|
|
10
|
+
readonly status: 'satisfied';
|
|
11
|
+
} | {
|
|
12
|
+
readonly status: 'refused';
|
|
13
|
+
readonly reason: string;
|
|
14
|
+
};
|
|
15
|
+
/** Live operation facts. Implementations must inspect them for every invocation. */
|
|
16
|
+
export interface OperationPreconditionPort {
|
|
17
|
+
check(request: OperationPreconditionRequest): Promise<OperationPreconditionResult>;
|
|
18
|
+
}
|
|
19
|
+
export type OperationAttemptContext = {
|
|
20
|
+
readonly operationId: string;
|
|
21
|
+
readonly generation: DeviceGeneration;
|
|
22
|
+
readonly attempt: number;
|
|
23
|
+
};
|
|
24
|
+
export type OperationJournalEvent<I, O> = {
|
|
25
|
+
readonly phase: 'started';
|
|
26
|
+
readonly operationId: string;
|
|
27
|
+
readonly physicalModemId: PhysicalModemId;
|
|
28
|
+
readonly generation: DeviceGeneration;
|
|
29
|
+
readonly descriptor: OperationDescriptor<I, O>;
|
|
30
|
+
readonly input: I;
|
|
31
|
+
} | {
|
|
32
|
+
readonly phase: 'completed';
|
|
33
|
+
readonly operationId: string;
|
|
34
|
+
readonly physicalModemId: PhysicalModemId;
|
|
35
|
+
readonly generation: DeviceGeneration;
|
|
36
|
+
readonly descriptor: OperationDescriptor<I, O>;
|
|
37
|
+
readonly result: OperationResult<O>;
|
|
38
|
+
};
|
|
39
|
+
export interface OperationJournalHook<I, O> {
|
|
40
|
+
record(event: OperationJournalEvent<I, O>): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
export type OperationExecution<I, O> = {
|
|
43
|
+
readonly operationId: string;
|
|
44
|
+
readonly physicalModemId: PhysicalModemId;
|
|
45
|
+
readonly descriptor: OperationDescriptor<I, O>;
|
|
46
|
+
readonly input: I;
|
|
47
|
+
readonly execute: (context: OperationAttemptContext) => Promise<OperationCompletion<O>>;
|
|
48
|
+
readonly readback?: (context: OperationAttemptContext) => Promise<OperationCompletion<O>>;
|
|
49
|
+
readonly rollback?: (context: OperationAttemptContext) => Promise<OperationCompletion<void>>;
|
|
50
|
+
readonly journal?: OperationJournalHook<I, O>;
|
|
51
|
+
};
|
|
52
|
+
export type ReconciliationExecution = {
|
|
53
|
+
readonly physicalModemId: PhysicalModemId;
|
|
54
|
+
readonly generation: DeviceGeneration;
|
|
55
|
+
readonly run: () => Promise<{
|
|
56
|
+
readonly status: 'reconciled';
|
|
57
|
+
} | {
|
|
58
|
+
readonly status: 'failed';
|
|
59
|
+
readonly reason: string;
|
|
60
|
+
}>;
|
|
61
|
+
};
|
|
62
|
+
export type ReconciliationResult = {
|
|
63
|
+
readonly status: 'reconciled';
|
|
64
|
+
readonly generation: DeviceGeneration;
|
|
65
|
+
} | {
|
|
66
|
+
readonly status: 'not-required';
|
|
67
|
+
readonly generation: DeviceGeneration;
|
|
68
|
+
} | {
|
|
69
|
+
readonly status: 'failed';
|
|
70
|
+
readonly reason: 'stale-generation' | string;
|
|
71
|
+
readonly generation: DeviceGeneration;
|
|
72
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './operation-engine.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './operation-engine.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type DeviceGeneration, type OperationResult, type PhysicalModemId } from '../domain/index.js';
|
|
2
|
+
import type { ModemControlCompositionRoot } from '../safety/index.js';
|
|
3
|
+
import type { OperationExecution, OperationPreconditionPort, ReconciliationExecution, ReconciliationResult } from './contracts.js';
|
|
4
|
+
export * from './contracts.js';
|
|
5
|
+
export type OperationEngineOptions = {
|
|
6
|
+
readonly root: ModemControlCompositionRoot;
|
|
7
|
+
readonly currentGeneration: (physicalModemId: PhysicalModemId) => DeviceGeneration;
|
|
8
|
+
readonly preconditions: OperationPreconditionPort;
|
|
9
|
+
};
|
|
10
|
+
export declare class OperationEngine {
|
|
11
|
+
#private;
|
|
12
|
+
constructor(options: OperationEngineOptions);
|
|
13
|
+
invoke<I, O>(execution: OperationExecution<I, O>): Promise<OperationResult<O>>;
|
|
14
|
+
reconcile(execution: ReconciliationExecution): Promise<ReconciliationResult>;
|
|
15
|
+
}
|
|
16
|
+
export declare function createOperationEngine(options: OperationEngineOptions): OperationEngine;
|