@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,194 @@
|
|
|
1
|
+
import { canAutoRetry, classifyOperationCompletion, } from '../domain/index.js';
|
|
2
|
+
import { acquireMutationAdmission } from '../ports/index.js';
|
|
3
|
+
export * from './contracts.js';
|
|
4
|
+
const MAX_IDEMPOTENT_READ_ATTEMPTS = 2;
|
|
5
|
+
export class OperationEngine {
|
|
6
|
+
#root;
|
|
7
|
+
#currentGeneration;
|
|
8
|
+
#preconditions;
|
|
9
|
+
#reconciliationRequired = new Set();
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.#root = options.root;
|
|
12
|
+
this.#currentGeneration = options.currentGeneration;
|
|
13
|
+
this.#preconditions = options.preconditions;
|
|
14
|
+
}
|
|
15
|
+
invoke(execution) {
|
|
16
|
+
if (execution.descriptor.mutationImpact === 'read') {
|
|
17
|
+
return this.#invokeNow(execution);
|
|
18
|
+
}
|
|
19
|
+
return this.#root
|
|
20
|
+
.actorFor(execution.physicalModemId)
|
|
21
|
+
.run(execution.physicalModemId, () => this.#invokeNow(execution));
|
|
22
|
+
}
|
|
23
|
+
reconcile(execution) {
|
|
24
|
+
return this.#root
|
|
25
|
+
.actorFor(execution.physicalModemId)
|
|
26
|
+
.run(execution.physicalModemId, async () => {
|
|
27
|
+
const current = this.#currentGeneration(execution.physicalModemId);
|
|
28
|
+
if (!this.#reconciliationRequired.has(execution.physicalModemId)) {
|
|
29
|
+
return { status: 'not-required', generation: current };
|
|
30
|
+
}
|
|
31
|
+
if (current !== execution.generation) {
|
|
32
|
+
return { status: 'failed', reason: 'stale-generation', generation: current };
|
|
33
|
+
}
|
|
34
|
+
const result = await execution.run();
|
|
35
|
+
const completedGeneration = this.#currentGeneration(execution.physicalModemId);
|
|
36
|
+
if (completedGeneration !== execution.generation) {
|
|
37
|
+
return {
|
|
38
|
+
status: 'failed',
|
|
39
|
+
reason: 'stale-generation',
|
|
40
|
+
generation: completedGeneration,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (result.status === 'failed') {
|
|
44
|
+
return { ...result, generation: completedGeneration };
|
|
45
|
+
}
|
|
46
|
+
this.#reconciliationRequired.delete(execution.physicalModemId);
|
|
47
|
+
return { status: 'reconciled', generation: completedGeneration };
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async #invokeNow(execution) {
|
|
51
|
+
const generation = this.#currentGeneration(execution.physicalModemId);
|
|
52
|
+
const kind = execution.descriptor.mutationImpact === 'read' ? 'read' : 'write';
|
|
53
|
+
if (kind === 'write' && this.#reconciliationRequired.has(execution.physicalModemId)) {
|
|
54
|
+
return refused('reconciliation-required', generation);
|
|
55
|
+
}
|
|
56
|
+
const descriptorRefusal = descriptorRefusalReason(execution);
|
|
57
|
+
if (descriptorRefusal !== undefined)
|
|
58
|
+
return refused(descriptorRefusal, generation);
|
|
59
|
+
const precondition = await this.#preconditions.check({
|
|
60
|
+
operationId: execution.operationId,
|
|
61
|
+
physicalModemId: execution.physicalModemId,
|
|
62
|
+
generation,
|
|
63
|
+
descriptorId: execution.descriptor.id,
|
|
64
|
+
preconditions: execution.descriptor.livePreconditions,
|
|
65
|
+
});
|
|
66
|
+
if (precondition.status === 'refused')
|
|
67
|
+
return refused(precondition.reason, generation);
|
|
68
|
+
const admission = await acquireMutationAdmission({
|
|
69
|
+
operationId: execution.operationId,
|
|
70
|
+
physicalModemId: execution.physicalModemId,
|
|
71
|
+
impact: execution.descriptor.mutationImpact,
|
|
72
|
+
requirement: execution.descriptor.admission,
|
|
73
|
+
}, kind === 'write' ? this.#root.admission : undefined);
|
|
74
|
+
if (admission.status === 'refused')
|
|
75
|
+
return refused(admission.reason, generation);
|
|
76
|
+
try {
|
|
77
|
+
await execution.journal?.record({
|
|
78
|
+
phase: 'started',
|
|
79
|
+
operationId: execution.operationId,
|
|
80
|
+
physicalModemId: execution.physicalModemId,
|
|
81
|
+
generation,
|
|
82
|
+
descriptor: execution.descriptor,
|
|
83
|
+
input: execution.input,
|
|
84
|
+
});
|
|
85
|
+
const result = await this.#executeWithRetry(execution, generation, kind);
|
|
86
|
+
const withHooks = await this.#runCompletionHooks(execution, result, generation);
|
|
87
|
+
if (withHooks.status === 'unknown-outcome') {
|
|
88
|
+
this.#reconciliationRequired.add(execution.physicalModemId);
|
|
89
|
+
}
|
|
90
|
+
await execution.journal?.record({
|
|
91
|
+
phase: 'completed',
|
|
92
|
+
operationId: execution.operationId,
|
|
93
|
+
physicalModemId: execution.physicalModemId,
|
|
94
|
+
generation,
|
|
95
|
+
descriptor: execution.descriptor,
|
|
96
|
+
result: withHooks,
|
|
97
|
+
});
|
|
98
|
+
return withHooks;
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
await releaseAdmission(admission);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async #executeWithRetry(execution, generation, kind) {
|
|
105
|
+
for (let attempt = 1; attempt <= MAX_IDEMPOTENT_READ_ATTEMPTS; attempt += 1) {
|
|
106
|
+
const completion = await execution.execute({
|
|
107
|
+
operationId: execution.operationId,
|
|
108
|
+
generation,
|
|
109
|
+
attempt,
|
|
110
|
+
});
|
|
111
|
+
const result = classifyOperationCompletion({
|
|
112
|
+
operation: kind,
|
|
113
|
+
completionGeneration: generation,
|
|
114
|
+
currentGeneration: this.#currentGeneration(execution.physicalModemId),
|
|
115
|
+
completion,
|
|
116
|
+
});
|
|
117
|
+
if (!canAutoRetry(execution.descriptor, result) || attempt === MAX_IDEMPOTENT_READ_ATTEMPTS) {
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return refused('retry-budget-exhausted', generation);
|
|
122
|
+
}
|
|
123
|
+
async #runCompletionHooks(execution, result, generation) {
|
|
124
|
+
const context = {
|
|
125
|
+
operationId: execution.operationId,
|
|
126
|
+
generation,
|
|
127
|
+
attempt: 1,
|
|
128
|
+
};
|
|
129
|
+
if (result.status === 'applied' && execution.descriptor.readback.required) {
|
|
130
|
+
const completion = await execution.readback?.(context);
|
|
131
|
+
if (completion === undefined)
|
|
132
|
+
return refused('readback-hook-missing', generation);
|
|
133
|
+
const readback = classifyOperationCompletion({
|
|
134
|
+
operation: 'read',
|
|
135
|
+
completionGeneration: generation,
|
|
136
|
+
currentGeneration: this.#currentGeneration(execution.physicalModemId),
|
|
137
|
+
completion,
|
|
138
|
+
});
|
|
139
|
+
if (readback.status === 'applied' &&
|
|
140
|
+
execution.descriptor.readback.matches(execution.input, readback.value)) {
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
if (readback.status === 'unknown-outcome')
|
|
144
|
+
return readback;
|
|
145
|
+
await execution.rollback?.(context);
|
|
146
|
+
return refused(readback.status === 'applied' ? 'readback-mismatch' : `readback-${readback.reason}`, generation, 'failed');
|
|
147
|
+
}
|
|
148
|
+
if (result.status === 'failed' && execution.descriptor.rollback.required) {
|
|
149
|
+
await execution.rollback?.(context);
|
|
150
|
+
}
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export function createOperationEngine(options) {
|
|
155
|
+
return new OperationEngine(options);
|
|
156
|
+
}
|
|
157
|
+
function descriptorRefusalReason(execution) {
|
|
158
|
+
const { descriptor } = execution;
|
|
159
|
+
const support = descriptor.mutationImpact === 'read' ? descriptor.support.read : descriptor.support.write;
|
|
160
|
+
if (!support.supported)
|
|
161
|
+
return support.reason;
|
|
162
|
+
if (descriptor.availability.state !== 'available')
|
|
163
|
+
return descriptor.availability.reason;
|
|
164
|
+
if (!inputAllowed(descriptor, execution.input))
|
|
165
|
+
return 'constraint-refused';
|
|
166
|
+
if (descriptor.readback.required && execution.readback === undefined)
|
|
167
|
+
return 'readback-hook-missing';
|
|
168
|
+
if (descriptor.rollback.required && execution.rollback === undefined)
|
|
169
|
+
return 'rollback-hook-missing';
|
|
170
|
+
if (descriptor.journal.required && execution.journal === undefined)
|
|
171
|
+
return 'journal-hook-missing';
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
function inputAllowed(descriptor, input) {
|
|
175
|
+
switch (descriptor.constraints.kind) {
|
|
176
|
+
case 'unconstrained':
|
|
177
|
+
return true;
|
|
178
|
+
case 'allowed-values':
|
|
179
|
+
return descriptor.constraints.values.some((value) => Object.is(value, input));
|
|
180
|
+
case 'numeric-range':
|
|
181
|
+
return (typeof input === 'number' &&
|
|
182
|
+
input >= descriptor.constraints.min &&
|
|
183
|
+
input <= descriptor.constraints.max &&
|
|
184
|
+
(descriptor.constraints.step === undefined ||
|
|
185
|
+
(input - descriptor.constraints.min) % descriptor.constraints.step === 0));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function refused(reason, generation, status = 'refused') {
|
|
189
|
+
return { status, reason, generation, requiresReconciliation: false };
|
|
190
|
+
}
|
|
191
|
+
async function releaseAdmission(admission) {
|
|
192
|
+
if (admission.status === 'admitted')
|
|
193
|
+
await admission.lease.release();
|
|
194
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './location.js';
|
|
2
|
+
export * from './modem-manager.js';
|
|
3
|
+
export * from './mutation-admission.js';
|
|
4
|
+
export * from './network-manager.js';
|
|
5
|
+
export * from './observation.js';
|
|
6
|
+
export * from './ops.js';
|
|
7
|
+
export * from './receipts.js';
|
|
8
|
+
export * from './reconcile.js';
|
|
9
|
+
export * from './resource-ownership.js';
|
|
10
|
+
export * from './router.js';
|
|
11
|
+
export * from './sms.js';
|
|
12
|
+
export * from './uhubctl.js';
|
|
@@ -6,11 +6,15 @@
|
|
|
6
6
|
// autoconnect / activation; MM owns radio / SIM ops; the local controller owns
|
|
7
7
|
// recovery + usage policy. The ModemManagerPort has NO bearer / connect verb —
|
|
8
8
|
// enforced at build time by forbidden-surface.test.ts.
|
|
9
|
-
|
|
10
|
-
export * from './modem-manager';
|
|
11
|
-
export * from './
|
|
12
|
-
export * from './
|
|
13
|
-
export * from './
|
|
14
|
-
export * from './
|
|
15
|
-
export * from './
|
|
16
|
-
export * from './
|
|
9
|
+
export * from './location.js';
|
|
10
|
+
export * from './modem-manager.js';
|
|
11
|
+
export * from './mutation-admission.js';
|
|
12
|
+
export * from './network-manager.js';
|
|
13
|
+
export * from './observation.js';
|
|
14
|
+
export * from './ops.js';
|
|
15
|
+
export * from './receipts.js';
|
|
16
|
+
export * from './reconcile.js';
|
|
17
|
+
export * from './resource-ownership.js';
|
|
18
|
+
export * from './router.js';
|
|
19
|
+
export * from './sms.js';
|
|
20
|
+
export * from './uhubctl.js';
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { EpochMillis } from '../domain/index.js';
|
|
2
|
+
import type { ModemRef } from './modem-manager.js';
|
|
3
|
+
/**
|
|
4
|
+
* The GNSS sources `Modem.Location.Capabilities` can advertise, as decoded source
|
|
5
|
+
* names. `3gpp-lac-ci` is deliberately absent: coarse cell location is the cell-info
|
|
6
|
+
* module's surface, and MM advertises it on devices with no GNSS receiver at all.
|
|
7
|
+
*/
|
|
8
|
+
export declare const GNSS_SOURCES: readonly ['gps-raw', 'gps-nmea', 'gps-unmanaged', 'agps-msa', 'agps-msb'];
|
|
9
|
+
export type GnssSource = (typeof GNSS_SOURCES)[number];
|
|
10
|
+
/** What `Modem.Location` advertises and what is switched on right now. */
|
|
11
|
+
export interface LocationStatus {
|
|
12
|
+
/** Every source name in `Capabilities`, GNSS or not (`3gpp-lac-ci` included). */
|
|
13
|
+
readonly capabilities: ReadonlySet<string>;
|
|
14
|
+
/** Every source name in `Enabled`. */
|
|
15
|
+
readonly enabledSources: ReadonlySet<string>;
|
|
16
|
+
/** True when at least one GNSS source is advertised. */
|
|
17
|
+
readonly gnssCapable: boolean;
|
|
18
|
+
/** True when at least one GNSS source is switched on. */
|
|
19
|
+
readonly gnssEnabled: boolean;
|
|
20
|
+
}
|
|
21
|
+
export type LocationStatusResult = {
|
|
22
|
+
readonly ok: true;
|
|
23
|
+
readonly status: LocationStatus;
|
|
24
|
+
} | {
|
|
25
|
+
readonly ok: false;
|
|
26
|
+
readonly reason: string;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* One GNSS fix. SENSITIVE: never persist it, never upload it, never put it in a log
|
|
30
|
+
* line. `observedAt` is when this process READ the fix, which is what staleness is
|
|
31
|
+
* measured against — a modem's own `utc-time` cannot be trusted to advance.
|
|
32
|
+
*/
|
|
33
|
+
export interface GnssFix {
|
|
34
|
+
readonly latitude: number;
|
|
35
|
+
readonly longitude: number;
|
|
36
|
+
readonly altitude?: number;
|
|
37
|
+
readonly utcTime?: string;
|
|
38
|
+
readonly observedAt: EpochMillis;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The outcome of ONE read attempt. `no-fix` is a first-class success: the modem
|
|
42
|
+
* answered and has not acquired a position. It is never conflated with an error and
|
|
43
|
+
* never answered with a previous fix — a stale coordinate rendered as current is the
|
|
44
|
+
* one failure mode this module exists to prevent.
|
|
45
|
+
*/
|
|
46
|
+
export type FixRead = {
|
|
47
|
+
readonly outcome: 'fix';
|
|
48
|
+
readonly fix: GnssFix;
|
|
49
|
+
} | {
|
|
50
|
+
readonly outcome: 'no-fix';
|
|
51
|
+
readonly reason: string;
|
|
52
|
+
} | {
|
|
53
|
+
readonly outcome: 'disabled';
|
|
54
|
+
readonly reason: string;
|
|
55
|
+
} | {
|
|
56
|
+
readonly outcome: 'unsupported';
|
|
57
|
+
readonly reason: string;
|
|
58
|
+
} | {
|
|
59
|
+
readonly outcome: 'error';
|
|
60
|
+
readonly reason: string;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* The outcome of switching GNSS on or off. Modelled like `SimUnlockResult` rather
|
|
64
|
+
* than as a reconcile `Receipt`: GNSS is an operator-invoked, gate-defaulted-off
|
|
65
|
+
* action with no desired-state dimension, so it is not something the planner
|
|
66
|
+
* converges.
|
|
67
|
+
*/
|
|
68
|
+
export interface LocationToggleResult {
|
|
69
|
+
readonly outcome: 'applied' | 'unsupported' | 'failed';
|
|
70
|
+
readonly reason: string;
|
|
71
|
+
/** The GNSS sources enabled after the call — empty after a successful disable. */
|
|
72
|
+
readonly enabledSources: ReadonlySet<string>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Read GNSS status, switch GNSS on/off, and read the current fix.
|
|
76
|
+
*
|
|
77
|
+
* Deliberately NOT part of `ModemManagerPort`: a consumer that only reconciles
|
|
78
|
+
* radio and SIM state has no business holding a handle that can read a position.
|
|
79
|
+
*/
|
|
80
|
+
export interface ModemLocationPort {
|
|
81
|
+
getLocationStatus(modem: ModemRef): Promise<LocationStatusResult>;
|
|
82
|
+
enableGnss(modem: ModemRef, sources: readonly GnssSource[]): Promise<LocationToggleResult>;
|
|
83
|
+
disableGnss(modem: ModemRef): Promise<LocationToggleResult>;
|
|
84
|
+
readFix(modem: ModemRef): Promise<FixRead>;
|
|
85
|
+
}
|
|
86
|
+
export declare function isGnssSource(value: string): value is GnssSource;
|
|
87
|
+
export declare function hasGnssSource(sources: Iterable<string>): boolean;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// The GNSS location port — ModemManager's `Modem.Location` interface, scoped by a
|
|
2
|
+
// privacy fence that is a PRODUCT rule, not a phase limitation.
|
|
3
|
+
//
|
|
4
|
+
// THE FENCE: this port reads the CURRENT fix and nothing else. There is no history
|
|
5
|
+
// verb, no track verb, no export verb, and no upload verb, and none may ever be
|
|
6
|
+
// added — a fix is held in memory for a live display and is gone the moment GNSS is
|
|
7
|
+
// disabled or the fix goes stale. `location-fence.test.ts` fails the build if a
|
|
8
|
+
// member whose name implies history, tracking, persistence, or upload appears here.
|
|
9
|
+
//
|
|
10
|
+
// A fix also carries coordinates, which say where the operator physically is. That
|
|
11
|
+
// class is redacted by `../redact` (`latitude` / `longitude` / `altitude` / `nmea`),
|
|
12
|
+
// so a fix that reaches a log line or a receipt comes out as the redaction marker
|
|
13
|
+
// rather than a position.
|
|
14
|
+
/**
|
|
15
|
+
* The GNSS sources `Modem.Location.Capabilities` can advertise, as decoded source
|
|
16
|
+
* names. `3gpp-lac-ci` is deliberately absent: coarse cell location is the cell-info
|
|
17
|
+
* module's surface, and MM advertises it on devices with no GNSS receiver at all.
|
|
18
|
+
*/
|
|
19
|
+
export const GNSS_SOURCES = [
|
|
20
|
+
'gps-raw',
|
|
21
|
+
'gps-nmea',
|
|
22
|
+
'gps-unmanaged',
|
|
23
|
+
'agps-msa',
|
|
24
|
+
'agps-msb',
|
|
25
|
+
];
|
|
26
|
+
export function isGnssSource(value) {
|
|
27
|
+
return GNSS_SOURCES.includes(value);
|
|
28
|
+
}
|
|
29
|
+
export function hasGnssSource(sources) {
|
|
30
|
+
for (const source of sources) {
|
|
31
|
+
if (isGnssSource(source)) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { DesiredRadio, EpochMillis, RuntimePath } from '../domain/index.js';
|
|
2
|
+
import type { ModemObservationPort } from './observation.js';
|
|
3
|
+
import type { Receipt } from './receipts.js';
|
|
4
|
+
/** A live handle to one modem — its ModemManager D-Bus object path (per-boot). */
|
|
5
|
+
export type ModemRef = RuntimePath;
|
|
6
|
+
/** Outcome of a SIM PIN unlock attempt. */
|
|
7
|
+
export interface SimUnlockResult {
|
|
8
|
+
readonly outcome: 'unlocked' | 'incorrect-pin' | 'sim-puk-required' | 'unsupported' | 'error';
|
|
9
|
+
readonly remainingAttempts?: number;
|
|
10
|
+
readonly reason: string;
|
|
11
|
+
}
|
|
12
|
+
/** Outcome of a SIM PUK unblock attempt. */
|
|
13
|
+
export interface SimPukUnlockResult {
|
|
14
|
+
readonly outcome: 'unlocked' | 'incorrect-puk' | 'permanently-blocked' | 'unsupported' | 'error';
|
|
15
|
+
readonly remainingAttempts?: number;
|
|
16
|
+
readonly reason: string;
|
|
17
|
+
}
|
|
18
|
+
/** One operator returned by a network scan. */
|
|
19
|
+
export interface ScannedNetwork {
|
|
20
|
+
/** MCC+MNC operator code. */
|
|
21
|
+
readonly operatorCode: string;
|
|
22
|
+
readonly operatorName?: string;
|
|
23
|
+
readonly availability: 'available' | 'current' | 'forbidden' | 'unknown';
|
|
24
|
+
}
|
|
25
|
+
/** Result of a network scan — discriminated, retaining the reason on failure. */
|
|
26
|
+
export type NetworkScanResult = {
|
|
27
|
+
readonly ok: true;
|
|
28
|
+
readonly networks: readonly ScannedNetwork[];
|
|
29
|
+
} | {
|
|
30
|
+
readonly ok: false;
|
|
31
|
+
readonly reason: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* What a modem says about its bands, right now.
|
|
35
|
+
*
|
|
36
|
+
* `supported` and `current` are BOTH required, and both may legitimately be
|
|
37
|
+
* empty: a modem that advertises no band control answers an empty `supported`,
|
|
38
|
+
* which is a real reading and the thing that keeps a control from being offered.
|
|
39
|
+
* A read that could not happen at all is the `ok: false` arm instead — the same
|
|
40
|
+
* "unknown is an answer about the READ" split the capability probes follow.
|
|
41
|
+
*/
|
|
42
|
+
export interface ModemBands {
|
|
43
|
+
readonly supported: readonly string[];
|
|
44
|
+
readonly current: readonly string[];
|
|
45
|
+
}
|
|
46
|
+
export type BandReadResult = {
|
|
47
|
+
readonly ok: true;
|
|
48
|
+
readonly bands: ModemBands;
|
|
49
|
+
} | {
|
|
50
|
+
readonly ok: false;
|
|
51
|
+
readonly reason: string;
|
|
52
|
+
};
|
|
53
|
+
/** A held inhibition over a modem, released via `uninhibit`. */
|
|
54
|
+
export interface InhibitLease {
|
|
55
|
+
/** The equipment UID the inhibition is keyed to. */
|
|
56
|
+
readonly uid: string;
|
|
57
|
+
readonly acquiredAt: EpochMillis;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The ModemManager port — EXTENDS the read-only observation port with the modem
|
|
61
|
+
* mutations MM legitimately owns: radio modes, primary SIM slot, PIN / PUK unlock,
|
|
62
|
+
* network scan, and inhibit / uninhibit. It owns NO bearer / connection lifecycle;
|
|
63
|
+
* bearers and activation belong to `NetworkManagerPort`.
|
|
64
|
+
*/
|
|
65
|
+
export interface ModemManagerInhibitPort {
|
|
66
|
+
/** Inhibit MM from managing a device (for a maintenance lease), keyed by UID. */
|
|
67
|
+
inhibit(uid: string): Promise<InhibitLease>;
|
|
68
|
+
/** Release a previously-taken inhibition. */
|
|
69
|
+
uninhibit(lease: InhibitLease): Promise<void>;
|
|
70
|
+
}
|
|
71
|
+
export interface ModemManagerPort extends ModemObservationPort, ModemManagerInhibitPort {
|
|
72
|
+
/** Set the modem's radio access-technology preference. */
|
|
73
|
+
setRadioModes(modem: ModemRef, preference: DesiredRadio): Promise<Receipt>;
|
|
74
|
+
/** Read the modem's supported and currently-selected bands. */
|
|
75
|
+
readBands(modem: ModemRef): Promise<BandReadResult>;
|
|
76
|
+
/**
|
|
77
|
+
* Lock the modem to a band selection. Passing exactly `['any']` releases the
|
|
78
|
+
* lock — ModemManager has no separate reset verb, so this IS the reset.
|
|
79
|
+
*/
|
|
80
|
+
setCurrentBands(modem: ModemRef, bands: readonly string[]): Promise<Receipt>;
|
|
81
|
+
/** Select the primary SIM slot (multi-slot modems only). */
|
|
82
|
+
setPrimarySimSlot(modem: ModemRef, slotIndex: number): Promise<Receipt>;
|
|
83
|
+
/** Submit a SIM PIN (exactly-once; read-before-submit is the adapter's job). */
|
|
84
|
+
sendPin(modem: ModemRef, pin: string): Promise<SimUnlockResult>;
|
|
85
|
+
/** Submit a SIM PUK plus the new PIN (exactly-once). */
|
|
86
|
+
sendPuk(modem: ModemRef, puk: string, newPin: string): Promise<SimPukUnlockResult>;
|
|
87
|
+
/** Scan for visible networks (long-running). */
|
|
88
|
+
scanNetworks(modem: ModemRef): Promise<NetworkScanResult>;
|
|
89
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// The ModemManager port — radio, SIM, scan, inhibit, and observation.
|
|
2
|
+
//
|
|
3
|
+
// CRITICAL INVARIANT (the single most safety-critical constraint in the package):
|
|
4
|
+
// this port has NO bearer / connection verb. There is no connect, no createBearer,
|
|
5
|
+
// no bearerConnect — MM's Simple.Connect / CreateBearer / Bearer.Connect are NEVER
|
|
6
|
+
// callable through here. NetworkManager is the sole owner of bearers and activation
|
|
7
|
+
// (see README ownership table). `forbidden-surface.test.ts` fails the build if any
|
|
8
|
+
// such method is ever added to this file.
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { MutationImpact, OperationRequirement, PhysicalModemId } from '../domain/index.js';
|
|
2
|
+
export type MutationAdmissionRequest = {
|
|
3
|
+
readonly operationId: string;
|
|
4
|
+
readonly physicalModemId: PhysicalModemId;
|
|
5
|
+
readonly impact: MutationImpact;
|
|
6
|
+
readonly requirement: OperationRequirement;
|
|
7
|
+
};
|
|
8
|
+
export interface MutationAdmissionLease {
|
|
9
|
+
release(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export type MutationAdmissionRefusalReason = 'admission-port-missing' | 'admission-refused';
|
|
12
|
+
export type MutationAdmissionDecision = {
|
|
13
|
+
readonly status: 'admitted';
|
|
14
|
+
readonly lease: MutationAdmissionLease;
|
|
15
|
+
} | {
|
|
16
|
+
readonly status: 'refused';
|
|
17
|
+
readonly reason: MutationAdmissionRefusalReason;
|
|
18
|
+
readonly detail?: string;
|
|
19
|
+
};
|
|
20
|
+
export type MutationAdmissionResult = {
|
|
21
|
+
readonly status: 'not-required';
|
|
22
|
+
} | MutationAdmissionDecision;
|
|
23
|
+
/** Consumer-owned admission authority. The package neither derives nor interprets its policy. */
|
|
24
|
+
export interface MutationAdmissionPort {
|
|
25
|
+
acquire(request: MutationAdmissionRequest): Promise<MutationAdmissionDecision>;
|
|
26
|
+
}
|
|
27
|
+
export declare function acquireMutationAdmission(request: MutationAdmissionRequest, port: MutationAdmissionPort | undefined): Promise<MutationAdmissionResult>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function acquireMutationAdmission(request, port) {
|
|
2
|
+
if (!request.requirement.required) {
|
|
3
|
+
return Promise.resolve({ status: 'not-required' });
|
|
4
|
+
}
|
|
5
|
+
if (port === undefined) {
|
|
6
|
+
return Promise.resolve({ status: 'refused', reason: 'admission-port-missing' });
|
|
7
|
+
}
|
|
8
|
+
return port.acquire(request);
|
|
9
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { Brand, EpochMillis } from '../domain/index.js';
|
|
2
|
+
import type { Receipt } from './receipts.js';
|
|
3
|
+
/** A NetworkManager connection-profile UUID. */
|
|
4
|
+
export type ConnectionId = Brand<string, 'ConnectionId'>;
|
|
5
|
+
/** A kernel network-device interface name (e.g. `wwan0`). */
|
|
6
|
+
export type DeviceIfname = Brand<string, 'DeviceIfname'>;
|
|
7
|
+
/** Construct a `ConnectionId` from a non-empty NM connection UUID. */
|
|
8
|
+
export declare function connectionId(value: string): ConnectionId;
|
|
9
|
+
/** Construct a `DeviceIfname` from a non-empty interface name. */
|
|
10
|
+
export declare function deviceIfname(value: string): DeviceIfname;
|
|
11
|
+
/**
|
|
12
|
+
* A GSM connection profile — NM's `gsm.*` setting group. `password` is SENSITIVE
|
|
13
|
+
* and MUST be redacted in every log / output (see `../redact`). The full nine-field
|
|
14
|
+
* nmcli write parity lands with the concrete adapter (A4.1); this is the port
|
|
15
|
+
* contract that adapter fulfils.
|
|
16
|
+
*/
|
|
17
|
+
export interface GsmProfileInput {
|
|
18
|
+
readonly connectionName: string;
|
|
19
|
+
/** Concrete APN, or the empty string when `autoConfig` drives it (A4.1). */
|
|
20
|
+
readonly apn: string;
|
|
21
|
+
readonly username?: string;
|
|
22
|
+
/** SENSITIVE — redact everywhere. */
|
|
23
|
+
readonly password?: string;
|
|
24
|
+
/** `true` ⇒ `gsm.home-only` (roaming disabled). */
|
|
25
|
+
readonly homeOnly: boolean;
|
|
26
|
+
/** `true` ⇒ `gsm.auto-config yes` (Auto-APN); mutually exclusive with creds. */
|
|
27
|
+
readonly autoConfig: boolean;
|
|
28
|
+
/** Manual operator selection (`gsm.network-id`); empty unless roaming-pinned. */
|
|
29
|
+
readonly networkId?: string;
|
|
30
|
+
}
|
|
31
|
+
/** A persisted GSM profile as read back from NM, keyed by its connection id. */
|
|
32
|
+
export interface GsmProfile extends GsmProfileInput {
|
|
33
|
+
readonly connectionId: ConnectionId;
|
|
34
|
+
}
|
|
35
|
+
/** A partial update to an existing GSM profile. */
|
|
36
|
+
export type GsmProfilePatch = Partial<GsmProfileInput>;
|
|
37
|
+
/**
|
|
38
|
+
* A quiesce lease — a held guarantee that the connection on THIS exact device stays
|
|
39
|
+
* deactivated while a disruptive MM operation runs, then is reactivated on release.
|
|
40
|
+
* Bound to BOTH `connectionId` and `deviceIfname` so it can never quiesce the wrong
|
|
41
|
+
* device (the two-device isolation invariant, A4.1).
|
|
42
|
+
*/
|
|
43
|
+
export interface QuiesceLease {
|
|
44
|
+
readonly connectionId: ConnectionId;
|
|
45
|
+
readonly deviceIfname: DeviceIfname;
|
|
46
|
+
readonly acquiredAt: EpochMillis;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The NetworkManager port. Activation and deactivation take BOTH the connection id
|
|
50
|
+
* AND the device interface name — never an id alone. This encodes, at the type
|
|
51
|
+
* level, that we address a connection on an EXACT device (`nmcli connection up
|
|
52
|
+
* <uuid> ifname <dev>` / verify-then-`device disconnect <ifname>`); the id-only
|
|
53
|
+
* `nmcli connection down` is structurally impossible to express here (A4.1).
|
|
54
|
+
*/
|
|
55
|
+
export interface NetworkManagerPort {
|
|
56
|
+
createGsmProfile(profile: GsmProfileInput): Promise<GsmProfile>;
|
|
57
|
+
readGsmProfile(id: ConnectionId): Promise<GsmProfile | undefined>;
|
|
58
|
+
updateGsmProfile(id: ConnectionId, patch: GsmProfilePatch): Promise<GsmProfile>;
|
|
59
|
+
deleteGsmProfile(id: ConnectionId): Promise<void>;
|
|
60
|
+
/** Activate `id` on the EXACT device `ifname`. */
|
|
61
|
+
activate(id: ConnectionId, ifname: DeviceIfname): Promise<Receipt>;
|
|
62
|
+
/** Deactivate `id` on the EXACT device `ifname`. */
|
|
63
|
+
deactivate(id: ConnectionId, ifname: DeviceIfname): Promise<Receipt>;
|
|
64
|
+
/** Take a quiesce lease over `id` on the EXACT device `ifname`. */
|
|
65
|
+
acquireQuiesceLease(id: ConnectionId, ifname: DeviceIfname): Promise<QuiesceLease>;
|
|
66
|
+
/** Release a quiesce lease, reactivating the connection it held down. */
|
|
67
|
+
releaseQuiesceLease(lease: QuiesceLease): Promise<void>;
|
|
68
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// The NetworkManager port — NM is the SOLE owner of bearers, APN, auth, roaming,
|
|
2
|
+
// autoconnect, and activation (see README ownership table). This port is the ONLY
|
|
3
|
+
// way the controller touches any of those resources; the ModemManager port has no
|
|
4
|
+
// bearer / connection verbs at all.
|
|
5
|
+
import { nonEmptyString } from '../domain/index.js';
|
|
6
|
+
/** Construct a `ConnectionId` from a non-empty NM connection UUID. */
|
|
7
|
+
export function connectionId(value) {
|
|
8
|
+
return nonEmptyString(value, 'connectionId');
|
|
9
|
+
}
|
|
10
|
+
/** Construct a `DeviceIfname` from a non-empty interface name. */
|
|
11
|
+
export function deviceIfname(value) {
|
|
12
|
+
return nonEmptyString(value, 'deviceIfname');
|
|
13
|
+
}
|
|
@@ -1,18 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
// `ModemObservationPort` is the NARROW port: it observes modems and reports them as
|
|
4
|
-
// a stream of discriminated list results. It carries NO mutation methods at all.
|
|
5
|
-
// The full `ModemManagerPort` (mutations) EXTENDS this, so a shadow / read-only
|
|
6
|
-
// consumer can depend on observation alone and never gain a mutating verb.
|
|
7
|
-
|
|
8
|
-
import type { CellularSnapshot } from '../domain';
|
|
9
|
-
|
|
1
|
+
import type { CellularSnapshot } from '../domain/index.js';
|
|
10
2
|
/** Cancels a subscription created by `observe`. Calling it twice is a no-op. */
|
|
11
3
|
export type Unsubscribe = () => void;
|
|
12
|
-
|
|
13
4
|
/** Why an observation could not produce an authoritative list this cycle. */
|
|
14
5
|
export type ObservationFailureReason = 'not-started' | 'source-unavailable' | 'bus-error';
|
|
15
|
-
|
|
16
6
|
/**
|
|
17
7
|
* The result of listing modems — DISCRIMINATED on `ok`, and it NEVER throws away
|
|
18
8
|
* rows. Even the failure arm carries `rows`: the last-known modems are RETAINED so
|
|
@@ -20,18 +10,17 @@ export type ObservationFailureReason = 'not-started' | 'source-unavailable' | 'b
|
|
|
20
10
|
* expressed by an `ok: true` snapshot that OMITS a modem (A3.1 epoch authority) —
|
|
21
11
|
* the false-removal class is dead by construction.
|
|
22
12
|
*/
|
|
23
|
-
export type ObservationList<T = CellularSnapshot> =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
13
|
+
export type ObservationList<T = CellularSnapshot> = {
|
|
14
|
+
readonly ok: true;
|
|
15
|
+
readonly rows: readonly T[];
|
|
16
|
+
} | {
|
|
17
|
+
readonly ok: false;
|
|
18
|
+
readonly reason: ObservationFailureReason;
|
|
19
|
+
/** Retained rows from the last authoritative snapshot — never dropped. */
|
|
20
|
+
readonly rows: readonly T[];
|
|
21
|
+
};
|
|
32
22
|
/** A subscriber to the ongoing observation stream. */
|
|
33
23
|
export type ObservationListener<T = CellularSnapshot> = (list: ObservationList<T>) => void;
|
|
34
|
-
|
|
35
24
|
/**
|
|
36
25
|
* The read-only observation port. `start()` connects, subscribes, and resolves the
|
|
37
26
|
* first AUTHORITATIVE list; `observe()` streams every subsequent list (each snapshot
|
|
@@ -39,10 +28,10 @@ export type ObservationListener<T = CellularSnapshot> = (list: ObservationList<T
|
|
|
39
28
|
* here can change modem state — that narrowness is the whole point of the port.
|
|
40
29
|
*/
|
|
41
30
|
export interface ModemObservationPort {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
31
|
+
/** Connect, subscribe, and resolve the first authoritative observation list. */
|
|
32
|
+
start(): Promise<ObservationList>;
|
|
33
|
+
/** Subscribe to the ongoing stream of observation lists. Returns an unsubscribe. */
|
|
34
|
+
observe(listener: ObservationListener): Unsubscribe;
|
|
35
|
+
/** Tear down the subscription and release the source. Idempotent. */
|
|
36
|
+
stop(): Promise<void>;
|
|
48
37
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// The observation contract — the read side shared by every ModemManager backend.
|
|
2
|
+
//
|
|
3
|
+
// `ModemObservationPort` is the NARROW port: it observes modems and reports them as
|
|
4
|
+
// a stream of discriminated list results. It carries NO mutation methods at all.
|
|
5
|
+
// The full `ModemManagerPort` (mutations) EXTENDS this, so a shadow / read-only
|
|
6
|
+
// consumer can depend on observation alone and never gain a mutating verb.
|
|
7
|
+
export {};
|