@ceralive/modem-control 0.2.0 → 1.1.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 +317 -0
- package/dist/backend/at-lease.d.ts +57 -0
- package/dist/backend/at-lease.js +109 -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 +76 -0
- package/dist/backend/transition-preconditions.js +64 -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 +196 -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 +19 -0
- package/dist/index.js +26 -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 +47 -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/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 +8 -0
- package/dist/providers/modem-manager/generic-operations.js +207 -0
- package/dist/providers/modem-manager/index.d.ts +3 -0
- package/dist/providers/modem-manager/index.js +3 -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 +39 -0
- package/dist/providers/modem-manager/provider.js +152 -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 +135 -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 +163 -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 +51 -0
- package/dist/providers/zte-goform/provider.js +101 -0
- package/dist/providers/zte-goform/session.d.ts +17 -0
- package/dist/providers/zte-goform/session.js +133 -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 +78 -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 +151 -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 +5 -0
- package/dist/usb-mode/index.js +15 -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/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 +84 -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 -221
- 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/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 -37
- package/src/backend/usage/proc-net-dev.test.ts +0 -56
- package/src/backend/usage/sampler.test.ts +0 -219
- package/src/backend/usage/sampler.ts +0 -228
- 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 -27
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { DomainError } from './errors.js';
|
|
2
|
+
import type { DeviceGeneration } from './generation.js';
|
|
3
|
+
export type OperationSupport = {
|
|
4
|
+
readonly supported: true;
|
|
5
|
+
} | {
|
|
6
|
+
readonly supported: false;
|
|
7
|
+
readonly reason: string;
|
|
8
|
+
};
|
|
9
|
+
export type OperationConstraints<I> = {
|
|
10
|
+
readonly kind: 'unconstrained';
|
|
11
|
+
} | {
|
|
12
|
+
readonly kind: 'allowed-values';
|
|
13
|
+
readonly values: readonly I[];
|
|
14
|
+
} | {
|
|
15
|
+
readonly kind: 'numeric-range';
|
|
16
|
+
readonly min: number;
|
|
17
|
+
readonly max: number;
|
|
18
|
+
readonly step?: number;
|
|
19
|
+
};
|
|
20
|
+
export type OperationAvailability = {
|
|
21
|
+
readonly state: 'available';
|
|
22
|
+
} | {
|
|
23
|
+
readonly state: 'unavailable';
|
|
24
|
+
readonly reason: string;
|
|
25
|
+
} | {
|
|
26
|
+
readonly state: 'refused';
|
|
27
|
+
readonly reason: string;
|
|
28
|
+
};
|
|
29
|
+
export type MutationImpact = 'read' | 'write' | 'session' | 'disruptive' | 'recovery';
|
|
30
|
+
export type RetryClass = 'never' | 'idempotent-read';
|
|
31
|
+
export type OperationConfidence = 'high' | 'medium' | 'low' | 'unknown';
|
|
32
|
+
export type OperationRequirement = {
|
|
33
|
+
readonly required: false;
|
|
34
|
+
} | {
|
|
35
|
+
readonly required: true;
|
|
36
|
+
readonly reason: string;
|
|
37
|
+
};
|
|
38
|
+
export type OperationReadback<I, O> = {
|
|
39
|
+
readonly required: false;
|
|
40
|
+
} | {
|
|
41
|
+
readonly required: true;
|
|
42
|
+
readonly reason: string;
|
|
43
|
+
readonly matches: (input: I, observed: O) => boolean;
|
|
44
|
+
};
|
|
45
|
+
export type OperationDescriptor<I, O> = {
|
|
46
|
+
readonly id: string;
|
|
47
|
+
readonly support: {
|
|
48
|
+
readonly read: OperationSupport;
|
|
49
|
+
readonly write: OperationSupport;
|
|
50
|
+
};
|
|
51
|
+
readonly authority: 'provider' | 'controller' | 'hardware';
|
|
52
|
+
readonly provider: string;
|
|
53
|
+
readonly constraints: OperationConstraints<I>;
|
|
54
|
+
readonly livePreconditions: readonly string[];
|
|
55
|
+
readonly availability: OperationAvailability;
|
|
56
|
+
readonly mutationImpact: MutationImpact;
|
|
57
|
+
readonly retryClass: RetryClass;
|
|
58
|
+
readonly readback: OperationReadback<I, O>;
|
|
59
|
+
readonly rollback: OperationRequirement;
|
|
60
|
+
readonly journal: OperationRequirement;
|
|
61
|
+
readonly admission: OperationRequirement;
|
|
62
|
+
readonly evidence: {
|
|
63
|
+
readonly profiles: readonly string[];
|
|
64
|
+
readonly firmware: readonly string[];
|
|
65
|
+
};
|
|
66
|
+
readonly confidence: OperationConfidence;
|
|
67
|
+
};
|
|
68
|
+
export declare class OperationDescriptorError extends DomainError {
|
|
69
|
+
readonly name = "OperationDescriptorError";
|
|
70
|
+
readonly reason = "retry-requires-idempotent-read";
|
|
71
|
+
constructor();
|
|
72
|
+
}
|
|
73
|
+
export declare function defineOperationDescriptor<I, O>(descriptor: OperationDescriptor<I, O>): OperationDescriptor<I, O>;
|
|
74
|
+
export type OperationCompletion<O> = {
|
|
75
|
+
readonly status: 'applied';
|
|
76
|
+
readonly value: O;
|
|
77
|
+
} | {
|
|
78
|
+
readonly status: 'refused';
|
|
79
|
+
readonly reason: string;
|
|
80
|
+
} | {
|
|
81
|
+
readonly status: 'failed';
|
|
82
|
+
readonly reason: string;
|
|
83
|
+
} | {
|
|
84
|
+
readonly status: 'timed-out';
|
|
85
|
+
} | {
|
|
86
|
+
readonly status: 'dropped';
|
|
87
|
+
};
|
|
88
|
+
export type OperationResult<O> = {
|
|
89
|
+
readonly status: 'applied';
|
|
90
|
+
readonly value: O;
|
|
91
|
+
readonly generation: DeviceGeneration;
|
|
92
|
+
readonly requiresReconciliation: false;
|
|
93
|
+
} | {
|
|
94
|
+
readonly status: 'refused';
|
|
95
|
+
readonly reason: string;
|
|
96
|
+
readonly generation: DeviceGeneration;
|
|
97
|
+
readonly requiresReconciliation: false;
|
|
98
|
+
} | {
|
|
99
|
+
readonly status: 'unknown-outcome';
|
|
100
|
+
readonly reason: 'stale-generation' | 'write-reply-timed-out' | 'write-reply-dropped';
|
|
101
|
+
readonly generation: DeviceGeneration;
|
|
102
|
+
readonly requiresReconciliation: true;
|
|
103
|
+
} | {
|
|
104
|
+
readonly status: 'failed';
|
|
105
|
+
readonly reason: string;
|
|
106
|
+
readonly generation: DeviceGeneration;
|
|
107
|
+
readonly requiresReconciliation: false;
|
|
108
|
+
};
|
|
109
|
+
export type OperationCompletionContext<O> = {
|
|
110
|
+
readonly operation: 'read' | 'write';
|
|
111
|
+
readonly completionGeneration: DeviceGeneration;
|
|
112
|
+
readonly currentGeneration: DeviceGeneration;
|
|
113
|
+
readonly completion: OperationCompletion<O>;
|
|
114
|
+
};
|
|
115
|
+
/** Apply generation and write-reply uncertainty before exposing a completion to callers. */
|
|
116
|
+
export declare function classifyOperationCompletion<O>(context: OperationCompletionContext<O>): OperationResult<O>;
|
|
117
|
+
/** Automatic retries are restricted to explicitly classified idempotent reads. */
|
|
118
|
+
export declare function canAutoRetry<I, O>(descriptor: OperationDescriptor<I, O>, result: OperationResult<O>): boolean;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { DomainError } from './errors.js';
|
|
2
|
+
export class OperationDescriptorError extends DomainError {
|
|
3
|
+
name = 'OperationDescriptorError';
|
|
4
|
+
reason = 'retry-requires-idempotent-read';
|
|
5
|
+
constructor() {
|
|
6
|
+
super('operation descriptor refused: automatic retry requires a supported idempotent read');
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function defineOperationDescriptor(descriptor) {
|
|
10
|
+
if (descriptor.retryClass === 'idempotent-read' &&
|
|
11
|
+
(descriptor.mutationImpact !== 'read' || !descriptor.support.read.supported)) {
|
|
12
|
+
throw new OperationDescriptorError();
|
|
13
|
+
}
|
|
14
|
+
return descriptor;
|
|
15
|
+
}
|
|
16
|
+
/** Apply generation and write-reply uncertainty before exposing a completion to callers. */
|
|
17
|
+
export function classifyOperationCompletion(context) {
|
|
18
|
+
const generation = context.completionGeneration;
|
|
19
|
+
if (generation !== context.currentGeneration) {
|
|
20
|
+
return {
|
|
21
|
+
status: 'unknown-outcome',
|
|
22
|
+
reason: 'stale-generation',
|
|
23
|
+
requiresReconciliation: true,
|
|
24
|
+
generation,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
switch (context.completion.status) {
|
|
28
|
+
case 'applied':
|
|
29
|
+
return {
|
|
30
|
+
status: 'applied',
|
|
31
|
+
value: context.completion.value,
|
|
32
|
+
generation,
|
|
33
|
+
requiresReconciliation: false,
|
|
34
|
+
};
|
|
35
|
+
case 'refused':
|
|
36
|
+
return {
|
|
37
|
+
status: 'refused',
|
|
38
|
+
reason: context.completion.reason,
|
|
39
|
+
generation,
|
|
40
|
+
requiresReconciliation: false,
|
|
41
|
+
};
|
|
42
|
+
case 'failed':
|
|
43
|
+
return {
|
|
44
|
+
status: 'failed',
|
|
45
|
+
reason: context.completion.reason,
|
|
46
|
+
generation,
|
|
47
|
+
requiresReconciliation: false,
|
|
48
|
+
};
|
|
49
|
+
case 'timed-out':
|
|
50
|
+
return context.operation === 'write'
|
|
51
|
+
? {
|
|
52
|
+
status: 'unknown-outcome',
|
|
53
|
+
reason: 'write-reply-timed-out',
|
|
54
|
+
requiresReconciliation: true,
|
|
55
|
+
generation,
|
|
56
|
+
}
|
|
57
|
+
: {
|
|
58
|
+
status: 'failed',
|
|
59
|
+
reason: 'read-reply-timed-out',
|
|
60
|
+
requiresReconciliation: false,
|
|
61
|
+
generation,
|
|
62
|
+
};
|
|
63
|
+
case 'dropped':
|
|
64
|
+
return context.operation === 'write'
|
|
65
|
+
? {
|
|
66
|
+
status: 'unknown-outcome',
|
|
67
|
+
reason: 'write-reply-dropped',
|
|
68
|
+
requiresReconciliation: true,
|
|
69
|
+
generation,
|
|
70
|
+
}
|
|
71
|
+
: {
|
|
72
|
+
status: 'failed',
|
|
73
|
+
reason: 'read-reply-dropped',
|
|
74
|
+
requiresReconciliation: false,
|
|
75
|
+
generation,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** Automatic retries are restricted to explicitly classified idempotent reads. */
|
|
80
|
+
export function canAutoRetry(descriptor, result) {
|
|
81
|
+
return (descriptor.mutationImpact === 'read' &&
|
|
82
|
+
descriptor.support.read.supported &&
|
|
83
|
+
descriptor.retryClass === 'idempotent-read' &&
|
|
84
|
+
result.status === 'failed');
|
|
85
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Brand } from './brand.js';
|
|
2
|
+
import { DomainError } from './errors.js';
|
|
3
|
+
export type PhysicalModemId = Brand<string, 'PhysicalModemId'>;
|
|
4
|
+
export type StableKey = Brand<string, 'StableKey'>;
|
|
5
|
+
export type PhysicalIdentitySource = 'serial' | 'id-path' | 'fallback';
|
|
6
|
+
export type PhysicalIdentityFacts = {
|
|
7
|
+
readonly serial?: string;
|
|
8
|
+
readonly idPath?: string;
|
|
9
|
+
readonly fallback?: string;
|
|
10
|
+
};
|
|
11
|
+
export type ResolvedPhysicalModemIdentity = {
|
|
12
|
+
readonly physicalModemId: PhysicalModemId;
|
|
13
|
+
readonly stableKey: StableKey;
|
|
14
|
+
readonly source: PhysicalIdentitySource;
|
|
15
|
+
};
|
|
16
|
+
export type PhysicalModemIdentityErrorReason = 'empty' | 'unsupported-shape' | 'mm-object-path' | 'interface-name' | 'ip-address' | 'equipment-identifier' | 'subscriber-identifier' | 'fallback-too-long' | 'no-identity-facts';
|
|
17
|
+
export declare class PhysicalModemIdentityError extends DomainError {
|
|
18
|
+
readonly reason: PhysicalModemIdentityErrorReason;
|
|
19
|
+
readonly name = "PhysicalModemIdentityError";
|
|
20
|
+
constructor(reason: PhysicalModemIdentityErrorReason);
|
|
21
|
+
}
|
|
22
|
+
/** Construct only from a canonical serial/ID_PATH/bounded-fallback identity. */
|
|
23
|
+
export declare function physicalModemId(value: string): PhysicalModemId;
|
|
24
|
+
/** Construct the actor/storage key for a validated physical modem identity. */
|
|
25
|
+
export declare function stableKeyFromPhysicalModemId(id: PhysicalModemId): StableKey;
|
|
26
|
+
/** Parse a canonical stable key while re-validating its embedded physical identity. */
|
|
27
|
+
export declare function stableKey(value: string): StableKey;
|
|
28
|
+
/** Resolve one identity with the frozen serial → ID_PATH → bounded-fallback precedence. */
|
|
29
|
+
export declare function resolvePhysicalModemIdentity(facts: PhysicalIdentityFacts): ResolvedPhysicalModemIdentity;
|
|
30
|
+
export type PortableIdentityAnchor = 'usb-serial' | 'id-path' | 'ifname';
|
|
31
|
+
export interface PortablePhysicalObservation {
|
|
32
|
+
readonly ifname: string;
|
|
33
|
+
readonly idPath?: string;
|
|
34
|
+
readonly vendorId?: string;
|
|
35
|
+
readonly serial?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface PortablePhysicalDeviceIdentity {
|
|
38
|
+
readonly identityKey: string;
|
|
39
|
+
readonly anchor: PortableIdentityAnchor;
|
|
40
|
+
readonly linkId: string;
|
|
41
|
+
}
|
|
42
|
+
export declare function mintLinkId(identityKey: string): string;
|
|
43
|
+
export declare function resolvePortablePhysicalIdentity(observation: PortablePhysicalObservation): PortablePhysicalDeviceIdentity;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { DomainError } from './errors.js';
|
|
3
|
+
export class PhysicalModemIdentityError extends DomainError {
|
|
4
|
+
reason;
|
|
5
|
+
name = 'PhysicalModemIdentityError';
|
|
6
|
+
constructor(reason) {
|
|
7
|
+
super(`physical modem identity refused: ${reason}`);
|
|
8
|
+
this.reason = reason;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const PHYSICAL_ID_PREFIXES = [
|
|
12
|
+
['serial', 'serial:'],
|
|
13
|
+
['id-path', 'id-path:'],
|
|
14
|
+
['fallback', 'fallback:'],
|
|
15
|
+
];
|
|
16
|
+
const FALLBACK_MAX_LENGTH = 128;
|
|
17
|
+
const MM_OBJECT_PATH = /^\/org\/freedesktop\/ModemManager1\/Modem(?:\/|$)/;
|
|
18
|
+
const INTERFACE_NAME = /^(?:wwan|eth|enp|ens|eno|wlan|wl|ppp|usb|rmnet|cdc-wdm|ttyUSB|ttyACM)\d[\w.-]*$/i;
|
|
19
|
+
const EQUIPMENT_IDENTIFIER = /^\d{14,16}$/;
|
|
20
|
+
const SUBSCRIBER_IDENTIFIER = /^(?:89\d{16,30}|\d{18,32})$/;
|
|
21
|
+
function isIpv4(value) {
|
|
22
|
+
const octets = value.split('.');
|
|
23
|
+
return (octets.length === 4 && octets.every((octet) => /^\d{1,3}$/.test(octet) && Number(octet) <= 255));
|
|
24
|
+
}
|
|
25
|
+
function isIpv6(value) {
|
|
26
|
+
if (!value.includes(':') || !/^[0-9a-f:]+$/i.test(value)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const groups = value.split(':');
|
|
30
|
+
const hasCompression = value.includes('::');
|
|
31
|
+
const populatedGroups = groups.filter((group) => group.length > 0);
|
|
32
|
+
return (populatedGroups.every((group) => group.length <= 4) &&
|
|
33
|
+
(hasCompression ? populatedGroups.length < 8 : populatedGroups.length === 8));
|
|
34
|
+
}
|
|
35
|
+
function assertSafeIdentityPart(value) {
|
|
36
|
+
if (value.length === 0) {
|
|
37
|
+
throw new PhysicalModemIdentityError('empty');
|
|
38
|
+
}
|
|
39
|
+
if (MM_OBJECT_PATH.test(value)) {
|
|
40
|
+
throw new PhysicalModemIdentityError('mm-object-path');
|
|
41
|
+
}
|
|
42
|
+
if (INTERFACE_NAME.test(value)) {
|
|
43
|
+
throw new PhysicalModemIdentityError('interface-name');
|
|
44
|
+
}
|
|
45
|
+
if (isIpv4(value) || isIpv6(value)) {
|
|
46
|
+
throw new PhysicalModemIdentityError('ip-address');
|
|
47
|
+
}
|
|
48
|
+
if (SUBSCRIBER_IDENTIFIER.test(value)) {
|
|
49
|
+
throw new PhysicalModemIdentityError('subscriber-identifier');
|
|
50
|
+
}
|
|
51
|
+
if (EQUIPMENT_IDENTIFIER.test(value)) {
|
|
52
|
+
throw new PhysicalModemIdentityError('equipment-identifier');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function splitPhysicalModemId(value) {
|
|
56
|
+
for (const [source, prefix] of PHYSICAL_ID_PREFIXES) {
|
|
57
|
+
if (value.startsWith(prefix)) {
|
|
58
|
+
return [source, value.slice(prefix.length)];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
throw new PhysicalModemIdentityError('unsupported-shape');
|
|
62
|
+
}
|
|
63
|
+
/** Construct only from a canonical serial/ID_PATH/bounded-fallback identity. */
|
|
64
|
+
export function physicalModemId(value) {
|
|
65
|
+
const [source, identityPart] = splitPhysicalModemId(value.trim());
|
|
66
|
+
assertSafeIdentityPart(identityPart);
|
|
67
|
+
if (source === 'fallback' && identityPart.length > FALLBACK_MAX_LENGTH) {
|
|
68
|
+
throw new PhysicalModemIdentityError('fallback-too-long');
|
|
69
|
+
}
|
|
70
|
+
return `${source}:${identityPart}`;
|
|
71
|
+
}
|
|
72
|
+
/** Construct the actor/storage key for a validated physical modem identity. */
|
|
73
|
+
export function stableKeyFromPhysicalModemId(id) {
|
|
74
|
+
return `modem:${id}`;
|
|
75
|
+
}
|
|
76
|
+
/** Parse a canonical stable key while re-validating its embedded physical identity. */
|
|
77
|
+
export function stableKey(value) {
|
|
78
|
+
if (!value.startsWith('modem:')) {
|
|
79
|
+
throw new PhysicalModemIdentityError('unsupported-shape');
|
|
80
|
+
}
|
|
81
|
+
return stableKeyFromPhysicalModemId(physicalModemId(value.slice('modem:'.length)));
|
|
82
|
+
}
|
|
83
|
+
/** Resolve one identity with the frozen serial → ID_PATH → bounded-fallback precedence. */
|
|
84
|
+
export function resolvePhysicalModemIdentity(facts) {
|
|
85
|
+
const candidates = [
|
|
86
|
+
['serial', facts.serial],
|
|
87
|
+
['id-path', facts.idPath],
|
|
88
|
+
['fallback', facts.fallback],
|
|
89
|
+
];
|
|
90
|
+
for (const [source, candidate] of candidates) {
|
|
91
|
+
const normalized = candidate?.trim();
|
|
92
|
+
if (normalized !== undefined && normalized.length > 0) {
|
|
93
|
+
const id = physicalModemId(`${source}:${normalized}`);
|
|
94
|
+
return { physicalModemId: id, stableKey: stableKeyFromPhysicalModemId(id), source };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
throw new PhysicalModemIdentityError('no-identity-facts');
|
|
98
|
+
}
|
|
99
|
+
export function mintLinkId(identityKey) {
|
|
100
|
+
return `lnk_${createHash('sha256').update(identityKey).digest('hex').slice(0, 16)}`;
|
|
101
|
+
}
|
|
102
|
+
export function resolvePortablePhysicalIdentity(observation) {
|
|
103
|
+
const serial = observation.serial?.trim();
|
|
104
|
+
const idPath = observation.idPath?.trim();
|
|
105
|
+
const vendor = observation.vendorId?.trim().toLowerCase();
|
|
106
|
+
const identityKey = serial
|
|
107
|
+
? `usb-serial:${vendor ? `${vendor}:` : ''}${serial}`
|
|
108
|
+
: idPath
|
|
109
|
+
? `id-path:${idPath}`
|
|
110
|
+
: `ifname:${observation.ifname}`;
|
|
111
|
+
const anchor = serial ? 'usb-serial' : idPath ? 'id-path' : 'ifname';
|
|
112
|
+
return { identityKey, anchor, linkId: mintLinkId(identityKey) };
|
|
113
|
+
}
|
|
@@ -1,14 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
// A2.1 stubbed this shape so the domain could enforce "an ambiguous identity may
|
|
4
|
-
// never bind durable policy" (see `policyBindingKey`). A2.2 owns the REAL
|
|
5
|
-
// definition below: the full connection / roaming / radio / simSlot / recovery /
|
|
6
|
-
// usage intent the desired-state planner reconciles (see `../ports/reconcile`).
|
|
7
|
-
|
|
8
|
-
import type { EquipmentId, LogicalSlotId, ModemIdentity } from './identity';
|
|
9
|
-
import { assertCanBindPolicy } from './identity';
|
|
10
|
-
import type { RadioAccessTechnology } from './state';
|
|
11
|
-
|
|
1
|
+
import type { EquipmentId, LogicalSlotId, ModemIdentity } from './identity.js';
|
|
2
|
+
import type { RadioAccessTechnology } from './state.js';
|
|
12
3
|
/**
|
|
13
4
|
* The subset of identity a durable policy is allowed to key off.
|
|
14
5
|
*
|
|
@@ -17,13 +8,11 @@ import type { RadioAccessTechnology } from './state';
|
|
|
17
8
|
* anchored by the equipment id — never to a per-boot handle or a subscriber id.
|
|
18
9
|
*/
|
|
19
10
|
export interface PolicyBindingKey {
|
|
20
|
-
|
|
21
|
-
|
|
11
|
+
readonly logicalSlotId?: LogicalSlotId;
|
|
12
|
+
readonly equipmentId: EquipmentId;
|
|
22
13
|
}
|
|
23
|
-
|
|
24
14
|
/** IP address family a connection requests. */
|
|
25
15
|
export type IpFamily = 'ipv4' | 'ipv6' | 'ipv4v6';
|
|
26
|
-
|
|
27
16
|
/**
|
|
28
17
|
* Connection credentials. TODAY'S SEMANTICS (draft §round-5 auth): these are
|
|
29
18
|
* persisted ONLY in the NetworkManager profile; the controller keeps them
|
|
@@ -31,24 +20,22 @@ export type IpFamily = 'ipv4' | 'ipv6' | 'ipv4v6';
|
|
|
31
20
|
* SENSITIVE and is ALWAYS redacted in logs / output / receipts (see `../redact`).
|
|
32
21
|
*/
|
|
33
22
|
export interface DesiredAuth {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
23
|
+
readonly username?: string;
|
|
24
|
+
/** SENSITIVE — always redacted; NM-profile-persisted only, transient in memory. */
|
|
25
|
+
readonly password?: string;
|
|
37
26
|
}
|
|
38
|
-
|
|
39
27
|
/** The data-connection intent. `apn: "auto"` selects NM Auto-APN (A4.1). */
|
|
40
28
|
export interface DesiredConnection {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
29
|
+
readonly apn: 'auto' | string;
|
|
30
|
+
readonly ipFamily: IpFamily;
|
|
31
|
+
readonly auth?: DesiredAuth;
|
|
32
|
+
/**
|
|
33
|
+
* Manual operator selection (`gsm.network-id`) — pin registration to a specific
|
|
34
|
+
* PLMN. Honored only while roaming (A4.1 amendment): the NM profile writes
|
|
35
|
+
* `gsm.network-id = roaming ? networkId : ""`, so it is cleared when roaming is off.
|
|
36
|
+
*/
|
|
37
|
+
readonly networkId?: string;
|
|
50
38
|
}
|
|
51
|
-
|
|
52
39
|
/**
|
|
53
40
|
* Radio access-technology intent. `preferenceOrdered` is ranked most-preferred
|
|
54
41
|
* first ("prefer 5G" ⇒ `['5gnr', 'lte', …]`); it must be honored as a PREFERENCE,
|
|
@@ -57,27 +44,24 @@ export interface DesiredConnection {
|
|
|
57
44
|
* when present, hard-limits the technologies the modem may use at all.
|
|
58
45
|
*/
|
|
59
46
|
export interface DesiredRadio {
|
|
60
|
-
|
|
61
|
-
|
|
47
|
+
readonly preferenceOrdered: readonly RadioAccessTechnology[];
|
|
48
|
+
readonly allowedSet?: ReadonlySet<RadioAccessTechnology>;
|
|
62
49
|
}
|
|
63
|
-
|
|
64
50
|
/**
|
|
65
51
|
* Recovery intent. DISABLED BY DEFAULT — the evidence-gated recovery ladder (A3.4)
|
|
66
52
|
* adds the per-step budgets / cooldowns; in Phase A `enabled` defaults to `false`
|
|
67
53
|
* and no recovery action is ever taken unless it is explicitly turned on.
|
|
68
54
|
*/
|
|
69
55
|
export interface DesiredRecovery {
|
|
70
|
-
|
|
56
|
+
readonly enabled: boolean;
|
|
71
57
|
}
|
|
72
|
-
|
|
73
58
|
/** Data-usage policy — local-controller owned (see ports README ownership table). */
|
|
74
59
|
export interface DesiredUsage {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
60
|
+
/** Day of month (1–31) the usage cycle resets; UTC, month-length clamped (A4.3). */
|
|
61
|
+
readonly cycleDay?: number;
|
|
62
|
+
/** Advisory threshold in bytes; crossing it raises an advisory, never gates. */
|
|
63
|
+
readonly thresholdBytes?: number;
|
|
79
64
|
}
|
|
80
|
-
|
|
81
65
|
/**
|
|
82
66
|
* Operator-authored durable cellular policy, bound to a high/medium-confidence
|
|
83
67
|
* identity via `boundTo` (a low-confidence identity cannot produce a binding key,
|
|
@@ -92,46 +76,27 @@ export interface DesiredUsage {
|
|
|
92
76
|
* - usage — data-usage cycle + advisory threshold.
|
|
93
77
|
*/
|
|
94
78
|
export interface DesiredCellularPolicy {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
79
|
+
readonly boundTo: PolicyBindingKey;
|
|
80
|
+
readonly enabled: boolean;
|
|
81
|
+
readonly connection: DesiredConnection;
|
|
82
|
+
readonly roaming: boolean;
|
|
83
|
+
readonly radio: DesiredRadio;
|
|
84
|
+
readonly simSlot?: number;
|
|
85
|
+
readonly recovery: DesiredRecovery;
|
|
86
|
+
readonly usage: DesiredUsage;
|
|
103
87
|
}
|
|
104
|
-
|
|
105
88
|
/** Recovery disabled — the Phase-A default (A3.4 adds budgets / cooldowns). */
|
|
106
|
-
export const RECOVERY_DISABLED: DesiredRecovery
|
|
107
|
-
|
|
89
|
+
export declare const RECOVERY_DISABLED: DesiredRecovery;
|
|
108
90
|
/**
|
|
109
91
|
* Build a sensible default policy for a bindable identity: NM activation on,
|
|
110
92
|
* Auto-APN, dual-stack IP, roaming off, prefer newest RAT down to GSM, recovery
|
|
111
93
|
* disabled, no usage limits. Concrete adapters and the planner refine from here.
|
|
112
94
|
*/
|
|
113
|
-
export function defaultCellularPolicy(boundTo: PolicyBindingKey): DesiredCellularPolicy
|
|
114
|
-
return {
|
|
115
|
-
boundTo,
|
|
116
|
-
enabled: true,
|
|
117
|
-
connection: { apn: 'auto', ipFamily: 'ipv4v6' },
|
|
118
|
-
roaming: false,
|
|
119
|
-
radio: { preferenceOrdered: ['5gnr', 'lte', 'umts', 'gsm'] },
|
|
120
|
-
recovery: RECOVERY_DISABLED,
|
|
121
|
-
usage: {},
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
95
|
+
export declare function defaultCellularPolicy(boundTo: PolicyBindingKey): DesiredCellularPolicy;
|
|
125
96
|
/**
|
|
126
97
|
* Derive the durable binding key for an identity, REFUSING low-confidence
|
|
127
98
|
* (ambiguous) identities. This is the structural gate that makes it impossible to
|
|
128
99
|
* bind durable policy to a duplicate/zero-IMEI modem: no key can be produced, so
|
|
129
100
|
* no `DesiredCellularPolicy` can name it. Throws `PolicyBindingRefusedError`.
|
|
130
101
|
*/
|
|
131
|
-
export function policyBindingKey(identity: ModemIdentity): PolicyBindingKey
|
|
132
|
-
assertCanBindPolicy(identity);
|
|
133
|
-
// exactOptionalPropertyTypes: only include logicalSlotId when actually present.
|
|
134
|
-
return identity.logicalSlotId !== undefined
|
|
135
|
-
? { logicalSlotId: identity.logicalSlotId, equipmentId: identity.equipmentId }
|
|
136
|
-
: { equipmentId: identity.equipmentId };
|
|
137
|
-
}
|
|
102
|
+
export declare function policyBindingKey(identity: ModemIdentity): PolicyBindingKey;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Durable desired-state policy — the operator's intent for one modem.
|
|
2
|
+
//
|
|
3
|
+
// A2.1 stubbed this shape so the domain could enforce "an ambiguous identity may
|
|
4
|
+
// never bind durable policy" (see `policyBindingKey`). A2.2 owns the REAL
|
|
5
|
+
// definition below: the full connection / roaming / radio / simSlot / recovery /
|
|
6
|
+
// usage intent the desired-state planner reconciles (see `../ports/reconcile`).
|
|
7
|
+
import { assertCanBindPolicy } from './identity.js';
|
|
8
|
+
/** Recovery disabled — the Phase-A default (A3.4 adds budgets / cooldowns). */
|
|
9
|
+
export const RECOVERY_DISABLED = { enabled: false };
|
|
10
|
+
/**
|
|
11
|
+
* Build a sensible default policy for a bindable identity: NM activation on,
|
|
12
|
+
* Auto-APN, dual-stack IP, roaming off, prefer newest RAT down to GSM, recovery
|
|
13
|
+
* disabled, no usage limits. Concrete adapters and the planner refine from here.
|
|
14
|
+
*/
|
|
15
|
+
export function defaultCellularPolicy(boundTo) {
|
|
16
|
+
return {
|
|
17
|
+
boundTo,
|
|
18
|
+
enabled: true,
|
|
19
|
+
connection: { apn: 'auto', ipFamily: 'ipv4v6' },
|
|
20
|
+
roaming: false,
|
|
21
|
+
radio: { preferenceOrdered: ['5gnr', 'lte', 'umts', 'gsm'] },
|
|
22
|
+
recovery: RECOVERY_DISABLED,
|
|
23
|
+
usage: {},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Derive the durable binding key for an identity, REFUSING low-confidence
|
|
28
|
+
* (ambiguous) identities. This is the structural gate that makes it impossible to
|
|
29
|
+
* bind durable policy to a duplicate/zero-IMEI modem: no key can be produced, so
|
|
30
|
+
* no `DesiredCellularPolicy` can name it. Throws `PolicyBindingRefusedError`.
|
|
31
|
+
*/
|
|
32
|
+
export function policyBindingKey(identity) {
|
|
33
|
+
assertCanBindPolicy(identity);
|
|
34
|
+
// exactOptionalPropertyTypes: only include logicalSlotId when actually present.
|
|
35
|
+
return identity.logicalSlotId !== undefined
|
|
36
|
+
? { logicalSlotId: identity.logicalSlotId, equipmentId: identity.equipmentId }
|
|
37
|
+
: { equipmentId: identity.equipmentId };
|
|
38
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type ShadowGeneration = '2G' | '3G' | '4G' | '5G';
|
|
2
|
+
export type ShadowSignalBucket = 'none' | 'poor' | 'fair' | 'good' | 'excellent';
|
|
3
|
+
export interface ShadowModemState {
|
|
4
|
+
readonly deviceKey: string;
|
|
5
|
+
readonly present: boolean;
|
|
6
|
+
readonly registration?: string;
|
|
7
|
+
readonly signalBucket?: ShadowSignalBucket;
|
|
8
|
+
readonly operatorName?: string;
|
|
9
|
+
readonly simPresent?: boolean;
|
|
10
|
+
readonly networkType?: ShadowGeneration;
|
|
11
|
+
}
|
|
12
|
+
export declare const SHADOW_COMPARABLE_FIELDS: readonly ["present", "registration", "signalBucket", "operatorName", "simPresent", "networkType"];
|
|
13
|
+
export type ShadowComparableField = (typeof SHADOW_COMPARABLE_FIELDS)[number];
|
|
14
|
+
export interface ShadowFieldDivergence {
|
|
15
|
+
readonly field: ShadowComparableField;
|
|
16
|
+
readonly mmcli: unknown;
|
|
17
|
+
readonly dbus: unknown;
|
|
18
|
+
}
|
|
19
|
+
export interface ShadowModemDivergence {
|
|
20
|
+
readonly deviceKey: string;
|
|
21
|
+
readonly kind: 'only-in-mmcli' | 'only-in-dbus' | 'field-mismatch';
|
|
22
|
+
readonly fields?: readonly ShadowFieldDivergence[];
|
|
23
|
+
}
|
|
24
|
+
export declare function classifyShadowDivergences(mmcli: readonly ShadowModemState[], dbus: readonly ShadowModemState[]): ShadowModemDivergence[];
|
|
25
|
+
export declare function foldGeneration(token: string): ShadowGeneration | undefined;
|
|
26
|
+
export declare function foldGenerations(tokens: Iterable<string>): ShadowGeneration | undefined;
|
|
27
|
+
export declare function foldSignalBucket(quality: number | undefined): ShadowSignalBucket | undefined;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export const SHADOW_COMPARABLE_FIELDS = [
|
|
2
|
+
'present',
|
|
3
|
+
'registration',
|
|
4
|
+
'signalBucket',
|
|
5
|
+
'operatorName',
|
|
6
|
+
'simPresent',
|
|
7
|
+
'networkType',
|
|
8
|
+
];
|
|
9
|
+
export function classifyShadowDivergences(mmcli, dbus) {
|
|
10
|
+
const left = new Map(mmcli.map((state) => [state.deviceKey, state]));
|
|
11
|
+
const right = new Map(dbus.map((state) => [state.deviceKey, state]));
|
|
12
|
+
const result = [];
|
|
13
|
+
for (const [deviceKey, mmcliState] of left) {
|
|
14
|
+
const dbusState = right.get(deviceKey);
|
|
15
|
+
if (dbusState === undefined) {
|
|
16
|
+
result.push({ deviceKey, kind: 'only-in-mmcli' });
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const fields = [];
|
|
20
|
+
for (const field of SHADOW_COMPARABLE_FIELDS) {
|
|
21
|
+
const a = mmcliState[field];
|
|
22
|
+
const b = dbusState[field];
|
|
23
|
+
if (a !== undefined && b !== undefined && a !== b)
|
|
24
|
+
fields.push({ field, mmcli: a, dbus: b });
|
|
25
|
+
}
|
|
26
|
+
if (fields.length > 0)
|
|
27
|
+
result.push({ deviceKey, kind: 'field-mismatch', fields });
|
|
28
|
+
}
|
|
29
|
+
for (const deviceKey of right.keys())
|
|
30
|
+
if (!left.has(deviceKey))
|
|
31
|
+
result.push({ deviceKey, kind: 'only-in-dbus' });
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
const GENERATIONS = {
|
|
35
|
+
'2g': '2G',
|
|
36
|
+
gsm: '2G',
|
|
37
|
+
'3g': '3G',
|
|
38
|
+
'3g+': '3G',
|
|
39
|
+
umts: '3G',
|
|
40
|
+
'4g': '4G',
|
|
41
|
+
lte: '4G',
|
|
42
|
+
'5g': '5G',
|
|
43
|
+
'5gnr': '5G',
|
|
44
|
+
};
|
|
45
|
+
const ORDER = ['2G', '3G', '4G', '5G'];
|
|
46
|
+
export function foldGeneration(token) {
|
|
47
|
+
return GENERATIONS[token.trim().toLowerCase()];
|
|
48
|
+
}
|
|
49
|
+
export function foldGenerations(tokens) {
|
|
50
|
+
let best;
|
|
51
|
+
for (const token of tokens) {
|
|
52
|
+
const value = foldGeneration(token);
|
|
53
|
+
if (value !== undefined && (best === undefined || ORDER.indexOf(value) > ORDER.indexOf(best)))
|
|
54
|
+
best = value;
|
|
55
|
+
}
|
|
56
|
+
return best;
|
|
57
|
+
}
|
|
58
|
+
export function foldSignalBucket(quality) {
|
|
59
|
+
if (quality === undefined || !Number.isFinite(quality))
|
|
60
|
+
return undefined;
|
|
61
|
+
return quality <= 0
|
|
62
|
+
? 'none'
|
|
63
|
+
: quality < 25
|
|
64
|
+
? 'poor'
|
|
65
|
+
: quality < 50
|
|
66
|
+
? 'fair'
|
|
67
|
+
: quality < 75
|
|
68
|
+
? 'good'
|
|
69
|
+
: 'excellent';
|
|
70
|
+
}
|