@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,205 @@
|
|
|
1
|
+
const STRENGTH_POINTS = {
|
|
2
|
+
none: 0,
|
|
3
|
+
weak: 1,
|
|
4
|
+
moderate: 2,
|
|
5
|
+
strong: 3,
|
|
6
|
+
};
|
|
7
|
+
function scoreOf(points) {
|
|
8
|
+
if (points <= 0)
|
|
9
|
+
return 'unsupported';
|
|
10
|
+
if (points === 1)
|
|
11
|
+
return 'maybe';
|
|
12
|
+
if (points === 2)
|
|
13
|
+
return 'likely';
|
|
14
|
+
return 'supported';
|
|
15
|
+
}
|
|
16
|
+
function confidenceOf(points) {
|
|
17
|
+
return Math.min(1, points / 3);
|
|
18
|
+
}
|
|
19
|
+
function cacheSignature(request, registryRevision) {
|
|
20
|
+
return JSON.stringify({
|
|
21
|
+
registryRevision,
|
|
22
|
+
generation: request.generation,
|
|
23
|
+
transport: request.transport,
|
|
24
|
+
passiveFacts: request.passiveFacts,
|
|
25
|
+
composition: request.composition,
|
|
26
|
+
firmware: request.firmware ?? null,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function addCandidate(candidates, provider, profile, strength) {
|
|
30
|
+
const key = `${provider.id}\u0000${profile}`;
|
|
31
|
+
const existing = candidates.get(key);
|
|
32
|
+
if (existing === undefined) {
|
|
33
|
+
candidates.set(key, { provider, profile, points: STRENGTH_POINTS[strength] });
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
existing.points += STRENGTH_POINTS[strength];
|
|
37
|
+
}
|
|
38
|
+
function unresolvedResult(status, request, points, evidence, conflicts) {
|
|
39
|
+
return {
|
|
40
|
+
status,
|
|
41
|
+
provider: null,
|
|
42
|
+
profile: null,
|
|
43
|
+
score: scoreOf(points),
|
|
44
|
+
confidence: confidenceOf(points),
|
|
45
|
+
evidence,
|
|
46
|
+
conflicts,
|
|
47
|
+
operations: null,
|
|
48
|
+
writable: false,
|
|
49
|
+
generation: request.generation,
|
|
50
|
+
physicalModemId: request.physicalModemId,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
async function evaluate(registry, request) {
|
|
54
|
+
const evidence = [];
|
|
55
|
+
const conflicts = [];
|
|
56
|
+
const candidates = new Map();
|
|
57
|
+
const eligible = [];
|
|
58
|
+
for (const provider of registry.list()) {
|
|
59
|
+
const transportMatches = provider.eligibleTransports.includes(request.transport);
|
|
60
|
+
evidence.push({
|
|
61
|
+
provider: provider.id,
|
|
62
|
+
profile: null,
|
|
63
|
+
stage: 'transport-eligibility',
|
|
64
|
+
source: request.transport,
|
|
65
|
+
signal: transportMatches ? 'match' : 'mismatch',
|
|
66
|
+
strength: 'none',
|
|
67
|
+
detail: transportMatches ? 'eligible-transport' : 'ineligible-transport',
|
|
68
|
+
});
|
|
69
|
+
if (transportMatches)
|
|
70
|
+
eligible.push(provider);
|
|
71
|
+
}
|
|
72
|
+
for (const provider of eligible) {
|
|
73
|
+
for (const matcher of provider.passiveMatchers) {
|
|
74
|
+
const actual = request.passiveFacts
|
|
75
|
+
.filter((fact) => fact.kind === matcher.fact)
|
|
76
|
+
.map((fact) => fact.value);
|
|
77
|
+
const signal = actual.length === 0
|
|
78
|
+
? 'unknown'
|
|
79
|
+
: actual.some((value) => matcher.expected.includes(value))
|
|
80
|
+
? 'match'
|
|
81
|
+
: 'mismatch';
|
|
82
|
+
const item = {
|
|
83
|
+
provider: provider.id,
|
|
84
|
+
profile: matcher.profiles.length === 1 ? (matcher.profiles[0] ?? null) : null,
|
|
85
|
+
stage: 'passive-facts',
|
|
86
|
+
source: matcher.id,
|
|
87
|
+
signal,
|
|
88
|
+
strength: matcher.strength,
|
|
89
|
+
detail: signal === 'match' ? 'expected-fact-present' : 'expected-fact-not-proven',
|
|
90
|
+
};
|
|
91
|
+
evidence.push(item);
|
|
92
|
+
if (signal === 'mismatch' && matcher.required)
|
|
93
|
+
conflicts.push(item);
|
|
94
|
+
if (signal === 'match') {
|
|
95
|
+
for (const profile of matcher.profiles) {
|
|
96
|
+
addCandidate(candidates, provider, profile, matcher.strength);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (const provider of eligible) {
|
|
102
|
+
for (const probe of provider.unauthenticatedProbes) {
|
|
103
|
+
const result = await probe.run(request);
|
|
104
|
+
const item = {
|
|
105
|
+
provider: provider.id,
|
|
106
|
+
profile: result.profiles.length === 1 ? (result.profiles[0] ?? null) : null,
|
|
107
|
+
stage: 'unauthenticated-fingerprint',
|
|
108
|
+
source: probe.id,
|
|
109
|
+
signal: result.signal,
|
|
110
|
+
strength: result.strength,
|
|
111
|
+
detail: result.detail,
|
|
112
|
+
};
|
|
113
|
+
evidence.push(item);
|
|
114
|
+
if (result.signal === 'mismatch')
|
|
115
|
+
conflicts.push(item);
|
|
116
|
+
if (result.signal === 'match') {
|
|
117
|
+
for (const profile of result.profiles) {
|
|
118
|
+
addCandidate(candidates, provider, profile, result.strength);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const ranked = [...candidates.values()].sort((left, right) => right.points - left.points);
|
|
124
|
+
const top = ranked[0];
|
|
125
|
+
if (top === undefined)
|
|
126
|
+
return unresolvedResult('unsupported', request, 0, evidence, conflicts);
|
|
127
|
+
if (ranked[1]?.points === top.points) {
|
|
128
|
+
return unresolvedResult('ambiguous', request, top.points, evidence, conflicts);
|
|
129
|
+
}
|
|
130
|
+
if (top.provider.authenticatedProfile !== undefined) {
|
|
131
|
+
const auth = await top.provider.authenticatedProfile.authenticate(request, [top.profile]);
|
|
132
|
+
const matched = auth.status === 'matched' && auth.profile === top.profile;
|
|
133
|
+
const item = {
|
|
134
|
+
provider: top.provider.id,
|
|
135
|
+
profile: top.profile,
|
|
136
|
+
stage: 'authenticated-profile',
|
|
137
|
+
source: top.provider.authenticatedProfile.algorithm,
|
|
138
|
+
signal: matched ? 'match' : auth.status === 'unavailable' ? 'unknown' : 'mismatch',
|
|
139
|
+
strength: 'strong',
|
|
140
|
+
detail: auth.detail,
|
|
141
|
+
};
|
|
142
|
+
evidence.push(item);
|
|
143
|
+
if (!matched) {
|
|
144
|
+
conflicts.push(item);
|
|
145
|
+
return unresolvedResult('ambiguous', request, top.points, evidence, conflicts);
|
|
146
|
+
}
|
|
147
|
+
top.points += STRENGTH_POINTS.strong;
|
|
148
|
+
}
|
|
149
|
+
const context = { ...request, profile: top.profile };
|
|
150
|
+
for (const reader of top.provider.capabilityReaders) {
|
|
151
|
+
const result = await reader.read(context);
|
|
152
|
+
const item = {
|
|
153
|
+
provider: top.provider.id,
|
|
154
|
+
profile: top.profile,
|
|
155
|
+
stage: 'capability-read',
|
|
156
|
+
source: reader.id,
|
|
157
|
+
signal: result.signal,
|
|
158
|
+
strength: result.strength,
|
|
159
|
+
detail: result.detail,
|
|
160
|
+
};
|
|
161
|
+
evidence.push(item);
|
|
162
|
+
if (result.signal === 'match')
|
|
163
|
+
top.points += STRENGTH_POINTS[result.strength];
|
|
164
|
+
if (result.signal === 'mismatch')
|
|
165
|
+
conflicts.push(item);
|
|
166
|
+
}
|
|
167
|
+
if (scoreOf(top.points) !== 'supported') {
|
|
168
|
+
return unresolvedResult('ambiguous', request, top.points, evidence, conflicts);
|
|
169
|
+
}
|
|
170
|
+
const operations = top.provider.operations(top.profile);
|
|
171
|
+
return {
|
|
172
|
+
status: 'selected',
|
|
173
|
+
provider: top.provider.id,
|
|
174
|
+
profile: top.profile,
|
|
175
|
+
score: 'supported',
|
|
176
|
+
confidence: confidenceOf(top.points),
|
|
177
|
+
evidence,
|
|
178
|
+
conflicts,
|
|
179
|
+
operations,
|
|
180
|
+
writable: operations.access === 'read-write',
|
|
181
|
+
generation: request.generation,
|
|
182
|
+
physicalModemId: request.physicalModemId,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
class GenerationScopedProviderMatcher {
|
|
186
|
+
registry;
|
|
187
|
+
/** Cache mutation is scoped by physical modem, generation, firmware and composition. */
|
|
188
|
+
#cache = new Map();
|
|
189
|
+
constructor(registry) {
|
|
190
|
+
this.registry = registry;
|
|
191
|
+
}
|
|
192
|
+
async match(request) {
|
|
193
|
+
const key = request.physicalModemId;
|
|
194
|
+
const signature = cacheSignature(request, this.registry.revision);
|
|
195
|
+
const cached = this.#cache.get(key);
|
|
196
|
+
if (cached?.signature === signature)
|
|
197
|
+
return cached.result;
|
|
198
|
+
const result = await evaluate(this.registry, request);
|
|
199
|
+
this.#cache.set(key, { signature, result });
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
export function createProviderMatcher(registry) {
|
|
204
|
+
return new GenerationScopedProviderMatcher(registry);
|
|
205
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const MODEM_MANAGER_REFUSAL_REASONS: readonly ['unauthorized', 'unsupported', 'wrong-state', 'busy', 'not-found', 'timed-out', 'disconnected', 'failed'];
|
|
2
|
+
export type ModemManagerRefusalReason = (typeof MODEM_MANAGER_REFUSAL_REASONS)[number];
|
|
3
|
+
export type ModemManagerRefusal = {
|
|
4
|
+
readonly reason: ModemManagerRefusalReason;
|
|
5
|
+
readonly retryable: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare function mapModemManagerError(error: unknown): ModemManagerRefusal;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { DisconnectedError, TransportError } from '../../transport/index.js';
|
|
2
|
+
export const MODEM_MANAGER_REFUSAL_REASONS = [
|
|
3
|
+
'unauthorized',
|
|
4
|
+
'unsupported',
|
|
5
|
+
'wrong-state',
|
|
6
|
+
'busy',
|
|
7
|
+
'not-found',
|
|
8
|
+
'timed-out',
|
|
9
|
+
'disconnected',
|
|
10
|
+
'failed',
|
|
11
|
+
];
|
|
12
|
+
function errorIdentity(error) {
|
|
13
|
+
if (!(error instanceof Error))
|
|
14
|
+
return String(error);
|
|
15
|
+
const dbusName = Reflect.get(error, 'dbusName');
|
|
16
|
+
return `${typeof dbusName === 'string' ? dbusName : error.name} ${error.message}`;
|
|
17
|
+
}
|
|
18
|
+
export function mapModemManagerError(error) {
|
|
19
|
+
if (error instanceof DisconnectedError)
|
|
20
|
+
return { reason: 'disconnected', retryable: true };
|
|
21
|
+
const identity = errorIdentity(error);
|
|
22
|
+
if (/Unauthorized|AccessDenied/i.test(identity))
|
|
23
|
+
return { reason: 'unauthorized', retryable: false };
|
|
24
|
+
if (/Unsupported|NotSupported/i.test(identity))
|
|
25
|
+
return { reason: 'unsupported', retryable: false };
|
|
26
|
+
if (/WrongState|InvalidState/i.test(identity))
|
|
27
|
+
return { reason: 'wrong-state', retryable: true };
|
|
28
|
+
if (/InProgress|Busy/i.test(identity))
|
|
29
|
+
return { reason: 'busy', retryable: true };
|
|
30
|
+
if (/NotFound|UnknownObject|UnknownModem/i.test(identity)) {
|
|
31
|
+
return { reason: 'not-found', retryable: false };
|
|
32
|
+
}
|
|
33
|
+
if (error instanceof TransportError && /timed out/i.test(error.message)) {
|
|
34
|
+
return { reason: 'timed-out', retryable: true };
|
|
35
|
+
}
|
|
36
|
+
return { reason: 'failed', retryable: false };
|
|
37
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MmDbusBackend } from '../../backend/index.js';
|
|
2
|
+
import type { ProviderExecutionContext } from '../contracts.js';
|
|
3
|
+
import { type RuntimeCompositionOperationDeps } from './runtime-composition-operation.js';
|
|
4
|
+
import type { ModemManagerProviderOperations, ModemManagerSnapshotResult } from './types.js';
|
|
5
|
+
export interface GenericOperationsDeps {
|
|
6
|
+
readonly backend: MmDbusBackend;
|
|
7
|
+
readonly runtimeComposition?: RuntimeCompositionOperationDeps;
|
|
8
|
+
readSnapshot(context: ProviderExecutionContext): Promise<ModemManagerSnapshotResult>;
|
|
9
|
+
}
|
|
10
|
+
export declare function createGenericOperations(deps: GenericOperationsDeps): Pick<ModemManagerProviderOperations, 'radio' | 'modes' | 'bands' | 'signal' | 'sim' | 'power' | 'usbComposition'>;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { classifyOperationCompletion, defineOperationDescriptor, runtimePath, } from '../../domain/index.js';
|
|
2
|
+
import { bandWriteReadbackMatches, buildBandWriteDescriptor, buildModeWriteDescriptor, encodeModeSelection, matchAdvertisedCombination, sameSelection, selectionOf, } from '../../radio/index.js';
|
|
3
|
+
import { mapModemManagerError } from './errors.js';
|
|
4
|
+
import { createRuntimeCompositionOperation, } from './runtime-composition-operation.js';
|
|
5
|
+
const PROVIDER_ID = 'modemmanager';
|
|
6
|
+
const PROFILE = 'generic-mm';
|
|
7
|
+
function readDescriptor(id) {
|
|
8
|
+
return defineOperationDescriptor({
|
|
9
|
+
id,
|
|
10
|
+
support: { read: { supported: true }, write: { supported: false, reason: 'read-only' } },
|
|
11
|
+
authority: 'provider',
|
|
12
|
+
provider: PROVIDER_ID,
|
|
13
|
+
constraints: { kind: 'unconstrained' },
|
|
14
|
+
livePreconditions: ['modem-present', 'runtime-interface-present'],
|
|
15
|
+
availability: { state: 'available' },
|
|
16
|
+
mutationImpact: 'read',
|
|
17
|
+
retryClass: 'idempotent-read',
|
|
18
|
+
readback: { required: false },
|
|
19
|
+
rollback: { required: false },
|
|
20
|
+
journal: { required: false },
|
|
21
|
+
admission: { required: false },
|
|
22
|
+
evidence: { profiles: [PROFILE], firmware: [] },
|
|
23
|
+
confidence: 'high',
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function writeDescriptor(id) {
|
|
27
|
+
return defineOperationDescriptor({
|
|
28
|
+
id,
|
|
29
|
+
support: { read: { supported: true }, write: { supported: true } },
|
|
30
|
+
authority: 'provider',
|
|
31
|
+
provider: PROVIDER_ID,
|
|
32
|
+
constraints: { kind: 'unconstrained' },
|
|
33
|
+
livePreconditions: ['modem-present', 'runtime-interface-present'],
|
|
34
|
+
availability: { state: 'available' },
|
|
35
|
+
mutationImpact: 'disruptive',
|
|
36
|
+
retryClass: 'never',
|
|
37
|
+
readback: { required: false },
|
|
38
|
+
rollback: { required: false },
|
|
39
|
+
journal: { required: true, reason: 'disruptive-radio-write' },
|
|
40
|
+
admission: { required: true, reason: 'provider-mutation' },
|
|
41
|
+
evidence: { profiles: [PROFILE], firmware: [] },
|
|
42
|
+
confidence: 'high',
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function applied(context, value) {
|
|
46
|
+
return classifyOperationCompletion({
|
|
47
|
+
operation: 'read',
|
|
48
|
+
completionGeneration: context.generation,
|
|
49
|
+
currentGeneration: context.generation,
|
|
50
|
+
completion: { status: 'applied', value },
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function refused(context, reason) {
|
|
54
|
+
return classifyOperationCompletion({
|
|
55
|
+
operation: 'read',
|
|
56
|
+
completionGeneration: context.generation,
|
|
57
|
+
currentGeneration: context.generation,
|
|
58
|
+
completion: { status: 'refused', reason },
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function receiptReason(reason) {
|
|
62
|
+
const mapped = mapModemManagerError(new Error(reason));
|
|
63
|
+
return mapped.reason === 'failed' ? reason : mapped.reason;
|
|
64
|
+
}
|
|
65
|
+
export function createGenericOperations(deps) {
|
|
66
|
+
const readField = async (context, capability, select, reason) => {
|
|
67
|
+
const snapshot = await deps.readSnapshot(context);
|
|
68
|
+
if (!snapshot.ok)
|
|
69
|
+
return refused(context, snapshot.reason);
|
|
70
|
+
return snapshot.capabilities[capability]
|
|
71
|
+
? applied(context, select(snapshot))
|
|
72
|
+
: refused(context, reason);
|
|
73
|
+
};
|
|
74
|
+
const readBands = async (context) => {
|
|
75
|
+
const snapshot = await deps.readSnapshot(context);
|
|
76
|
+
if (!snapshot.ok)
|
|
77
|
+
return refused(context, snapshot.reason);
|
|
78
|
+
if (!snapshot.capabilities.bandRead)
|
|
79
|
+
return refused(context, 'band-read-unsupported');
|
|
80
|
+
const result = await deps.backend.readBands(runtimePath(snapshot.modemPath));
|
|
81
|
+
return result.ok
|
|
82
|
+
? applied(context, result.bands)
|
|
83
|
+
: refused(context, receiptReason(result.reason));
|
|
84
|
+
};
|
|
85
|
+
const radioDescriptor = writeDescriptor('modemmanager.radio-modes');
|
|
86
|
+
const radio = {
|
|
87
|
+
descriptor: radioDescriptor,
|
|
88
|
+
describe: () => Promise.resolve(radioDescriptor),
|
|
89
|
+
read: (context) => readField(context, 'modeRead', (snapshot) => snapshot.radio, 'mode-read-unsupported'),
|
|
90
|
+
write: async (context, input) => {
|
|
91
|
+
const snapshot = await deps.readSnapshot(context);
|
|
92
|
+
if (!snapshot.ok)
|
|
93
|
+
return refused(context, snapshot.reason);
|
|
94
|
+
if (!snapshot.capabilities.modeWrite)
|
|
95
|
+
return refused(context, 'mode-write-unsupported');
|
|
96
|
+
const receipt = await deps.backend.setRadioModes(runtimePath(snapshot.modemPath), input);
|
|
97
|
+
if (receipt.status !== 'applied')
|
|
98
|
+
return refused(context, receiptReason(receipt.reason));
|
|
99
|
+
return readField(context, 'modeRead', (next) => next.radio, 'mode-read-unsupported');
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
const readModeTruth = async (context) => readField(context, 'modeRead', (snapshot) => snapshot.radio.truth, 'mode-read-unsupported');
|
|
103
|
+
const modes = {
|
|
104
|
+
descriptor: buildModeWriteDescriptor({
|
|
105
|
+
provider: PROVIDER_ID,
|
|
106
|
+
profile: PROFILE,
|
|
107
|
+
truth: {
|
|
108
|
+
current: { state: 'not-reported' },
|
|
109
|
+
supported: { combinations: [], undecodable: [] },
|
|
110
|
+
},
|
|
111
|
+
writeSupported: true,
|
|
112
|
+
}),
|
|
113
|
+
describe: async (context) => {
|
|
114
|
+
const snapshot = await deps.readSnapshot(context);
|
|
115
|
+
return buildModeWriteDescriptor({
|
|
116
|
+
provider: PROVIDER_ID,
|
|
117
|
+
profile: PROFILE,
|
|
118
|
+
truth: snapshot.ok
|
|
119
|
+
? snapshot.radio.truth
|
|
120
|
+
: {
|
|
121
|
+
current: { state: 'not-reported' },
|
|
122
|
+
supported: { combinations: [], undecodable: [] },
|
|
123
|
+
},
|
|
124
|
+
writeSupported: snapshot.ok && snapshot.capabilities.modeWrite,
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
read: readModeTruth,
|
|
128
|
+
write: async (context, selection) => {
|
|
129
|
+
const snapshot = await deps.readSnapshot(context);
|
|
130
|
+
if (!snapshot.ok)
|
|
131
|
+
return refused(context, snapshot.reason);
|
|
132
|
+
if (!snapshot.capabilities.modeWrite)
|
|
133
|
+
return refused(context, 'mode-write-unsupported');
|
|
134
|
+
// A selection the modem never advertised is refused outright — never rounded to
|
|
135
|
+
// the nearest advertised one. Substituting is how "prefer 4G" on a marginal cell
|
|
136
|
+
// silently becomes 5G-first, which `five-g-preference.ts` refuses for the same reason.
|
|
137
|
+
if (matchAdvertisedCombination(snapshot.radio.truth.supported, selection) === undefined) {
|
|
138
|
+
return refused(context, 'mode-combination-not-advertised');
|
|
139
|
+
}
|
|
140
|
+
const encoded = encodeModeSelection(selection);
|
|
141
|
+
if (!encoded.ok)
|
|
142
|
+
return refused(context, 'mode-name-unknown');
|
|
143
|
+
const receipt = await deps.backend.setModeCombination(runtimePath(snapshot.modemPath), encoded.allowedMask, encoded.preferredMask);
|
|
144
|
+
if (receipt.status !== 'applied')
|
|
145
|
+
return refused(context, receiptReason(receipt.reason));
|
|
146
|
+
const readback = await readModeTruth(context);
|
|
147
|
+
if (readback.status !== 'applied')
|
|
148
|
+
return readback;
|
|
149
|
+
// An accepted-but-ignored `SetCurrentModes` is indistinguishable from success at
|
|
150
|
+
// the call site, so the descriptor requires a readback and this enforces it.
|
|
151
|
+
return readback.value.current.state === 'reported' &&
|
|
152
|
+
sameSelection(selectionOf(readback.value.current.combination), selection)
|
|
153
|
+
? readback
|
|
154
|
+
: refused(context, 'mode-write-readback-mismatch');
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
const bandDescriptorFor = (snapshot) => buildBandWriteDescriptor({
|
|
158
|
+
provider: PROVIDER_ID,
|
|
159
|
+
profile: PROFILE,
|
|
160
|
+
certification: snapshot.ok
|
|
161
|
+
? snapshot.bandCertification
|
|
162
|
+
: {
|
|
163
|
+
required: true,
|
|
164
|
+
satisfied: false,
|
|
165
|
+
reason: 'band-certification-required',
|
|
166
|
+
offerable: [],
|
|
167
|
+
},
|
|
168
|
+
readSupported: snapshot.ok && snapshot.capabilities.bandRead,
|
|
169
|
+
});
|
|
170
|
+
const bands = {
|
|
171
|
+
descriptor: bandDescriptorFor({ ok: false, reason: 'not-found' }),
|
|
172
|
+
describe: async (context) => bandDescriptorFor(await deps.readSnapshot(context)),
|
|
173
|
+
read: readBands,
|
|
174
|
+
write: async (context, input) => {
|
|
175
|
+
const snapshot = await deps.readSnapshot(context);
|
|
176
|
+
if (!snapshot.ok)
|
|
177
|
+
return refused(context, snapshot.reason);
|
|
178
|
+
// The certification gate, second of two. `bandWrite` is already false without a
|
|
179
|
+
// catalog entry, and the descriptor already reads `refused` — but a band lock can
|
|
180
|
+
// take a working uplink off the air, so the call path refuses independently
|
|
181
|
+
// rather than trusting a consumer to have read the descriptor.
|
|
182
|
+
if (!snapshot.bandCertification.satisfied || !snapshot.capabilities.bandWrite)
|
|
183
|
+
return refused(context, 'band-write-certification-required');
|
|
184
|
+
const receipt = await deps.backend.setCurrentBands(runtimePath(snapshot.modemPath), input);
|
|
185
|
+
if (receipt.status !== 'applied')
|
|
186
|
+
return refused(context, receiptReason(receipt.reason));
|
|
187
|
+
const readback = await readBands(context);
|
|
188
|
+
if (readback.status !== 'applied')
|
|
189
|
+
return readback;
|
|
190
|
+
return bandWriteReadbackMatches(input, readback.value)
|
|
191
|
+
? readback
|
|
192
|
+
: refused(context, 'band-write-readback-mismatch');
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
const signal = {
|
|
196
|
+
descriptor: readDescriptor('modemmanager.signal'),
|
|
197
|
+
read: (context) => readField(context, 'signalRead', (snapshot) => snapshot.signal, 'signal-read-unsupported'),
|
|
198
|
+
};
|
|
199
|
+
const sim = {
|
|
200
|
+
descriptor: readDescriptor('modemmanager.sim'),
|
|
201
|
+
read: (context) => readField(context, 'simRead', (snapshot) => snapshot.sim, 'sim-read-unsupported'),
|
|
202
|
+
};
|
|
203
|
+
const power = {
|
|
204
|
+
descriptor: readDescriptor('modemmanager.power'),
|
|
205
|
+
read: (context) => readField(context, 'powerRead', (snapshot) => snapshot.power, 'power-read-unsupported'),
|
|
206
|
+
};
|
|
207
|
+
const usbComposition = createRuntimeCompositionOperation(deps.runtimeComposition);
|
|
208
|
+
return { radio, modes, bands, signal, sim, power, usbComposition };
|
|
209
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { MmLocation } from '../../backend/index.js';
|
|
2
|
+
import type { DbusTransport } from '../../transport/index.js';
|
|
3
|
+
import type { MmUssd } from '../../ussd/index.js';
|
|
4
|
+
import type { ProviderExecutionContext } from '../contracts.js';
|
|
5
|
+
import type { ModemManagerProviderOperations, ModemManagerSnapshotResult } from './types.js';
|
|
6
|
+
export interface ModuleOperationsOptions {
|
|
7
|
+
readonly transport: DbusTransport;
|
|
8
|
+
readonly destination: string;
|
|
9
|
+
readonly location: MmLocation;
|
|
10
|
+
readonly ussd: MmUssd;
|
|
11
|
+
readonly now: () => number;
|
|
12
|
+
readSnapshot(context: ProviderExecutionContext): Promise<ModemManagerSnapshotResult>;
|
|
13
|
+
}
|
|
14
|
+
type ModuleOperationSurface = Pick<ModemManagerProviderOperations, 'location' | 'sms' | 'ussd' | 'initiateUssd' | 'respondUssd' | 'cancelUssd' | 'fccCoverage'>;
|
|
15
|
+
export declare class ModemManagerModuleOperations {
|
|
16
|
+
#private;
|
|
17
|
+
readonly operations: ModuleOperationSurface;
|
|
18
|
+
constructor(options: ModuleOperationsOptions);
|
|
19
|
+
stop(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { epochMillis, runtimePath } from '../../domain/index.js';
|
|
2
|
+
import { resolveFccUnlockCoverage } from '../../fcc/index.js';
|
|
3
|
+
import { advanceGnssFixState, GNSS_OFF } from '../../location/index.js';
|
|
4
|
+
import { createDbusSmsPort } from '../../sms/index.js';
|
|
5
|
+
export class ModemManagerModuleOperations {
|
|
6
|
+
operations;
|
|
7
|
+
#options;
|
|
8
|
+
#smsPorts = new Map();
|
|
9
|
+
#locationStates = new Map();
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.#options = options;
|
|
12
|
+
this.operations = {
|
|
13
|
+
location: {
|
|
14
|
+
status: async (context) => {
|
|
15
|
+
const path = await this.#modemPath(context);
|
|
16
|
+
return path === undefined
|
|
17
|
+
? { ok: false, reason: 'modem-not-found' }
|
|
18
|
+
: this.#options.location.getLocationStatus(runtimePath(path));
|
|
19
|
+
},
|
|
20
|
+
enable: async (context, sources) => {
|
|
21
|
+
const path = await this.#modemPath(context);
|
|
22
|
+
if (path === undefined)
|
|
23
|
+
return this.#unsupportedToggle();
|
|
24
|
+
const result = await this.#options.location.enableGnss(runtimePath(path), sources);
|
|
25
|
+
if (result.outcome === 'applied') {
|
|
26
|
+
this.#setLocationState(context, {
|
|
27
|
+
kind: 'gnss-enabled',
|
|
28
|
+
at: epochMillis(this.#options.now()),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
},
|
|
33
|
+
disable: async (context) => {
|
|
34
|
+
const path = await this.#modemPath(context);
|
|
35
|
+
if (path === undefined)
|
|
36
|
+
return this.#unsupportedToggle();
|
|
37
|
+
const result = await this.#options.location.disableGnss(runtimePath(path));
|
|
38
|
+
if (result.outcome === 'applied')
|
|
39
|
+
this.#setLocationState(context, { kind: 'gnss-disabled' });
|
|
40
|
+
return result;
|
|
41
|
+
},
|
|
42
|
+
readFix: async (context) => {
|
|
43
|
+
const path = await this.#modemPath(context);
|
|
44
|
+
if (path === undefined)
|
|
45
|
+
return { outcome: 'unsupported', reason: 'modem-not-found' };
|
|
46
|
+
const read = await this.#options.location.readFix(runtimePath(path));
|
|
47
|
+
this.#setLocationState(context, {
|
|
48
|
+
kind: 'read',
|
|
49
|
+
at: epochMillis(this.#options.now()),
|
|
50
|
+
read,
|
|
51
|
+
});
|
|
52
|
+
return read;
|
|
53
|
+
},
|
|
54
|
+
state: (context) => this.#locationStates.get(String(context.physicalModemId)) ?? GNSS_OFF,
|
|
55
|
+
tick: (context) => this.#setLocationState(context, {
|
|
56
|
+
kind: 'tick',
|
|
57
|
+
at: epochMillis(this.#options.now()),
|
|
58
|
+
}),
|
|
59
|
+
},
|
|
60
|
+
sms: (context) => this.#sms(context),
|
|
61
|
+
ussd: options.ussd,
|
|
62
|
+
initiateUssd: (context, ussdCommand) => this.#ussd(context, (path) => options.ussd.initiate(runtimePath(path), ussdCommand)),
|
|
63
|
+
respondUssd: (context, ussdResponse) => this.#ussd(context, (path) => options.ussd.respond(runtimePath(path), ussdResponse)),
|
|
64
|
+
cancelUssd: (context) => this.#ussd(context, (path) => options.ussd.cancel(runtimePath(path))),
|
|
65
|
+
fccCoverage: resolveFccUnlockCoverage,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
async stop() {
|
|
69
|
+
for (const port of this.#smsPorts.values())
|
|
70
|
+
await port.stop();
|
|
71
|
+
this.#smsPorts.clear();
|
|
72
|
+
this.#options.ussd.stop();
|
|
73
|
+
}
|
|
74
|
+
async #sms(context) {
|
|
75
|
+
const snapshot = await this.#options.readSnapshot(context);
|
|
76
|
+
if (!snapshot.ok)
|
|
77
|
+
return { ok: false, reason: snapshot.reason };
|
|
78
|
+
if (!snapshot.capabilities.sms)
|
|
79
|
+
return { ok: false, reason: 'unsupported' };
|
|
80
|
+
let port = this.#smsPorts.get(snapshot.modemPath);
|
|
81
|
+
if (port === undefined) {
|
|
82
|
+
port = createDbusSmsPort({
|
|
83
|
+
transport: this.#options.transport,
|
|
84
|
+
modemPath: snapshot.modemPath,
|
|
85
|
+
destination: this.#options.destination,
|
|
86
|
+
});
|
|
87
|
+
this.#smsPorts.set(snapshot.modemPath, port);
|
|
88
|
+
}
|
|
89
|
+
return { ok: true, port };
|
|
90
|
+
}
|
|
91
|
+
async #ussd(context, run) {
|
|
92
|
+
const path = await this.#modemPath(context);
|
|
93
|
+
return path === undefined
|
|
94
|
+
? {
|
|
95
|
+
ok: false,
|
|
96
|
+
snapshot: this.#options.ussd.snapshot(runtimePath('/')),
|
|
97
|
+
refusal: 'unsupported',
|
|
98
|
+
}
|
|
99
|
+
: run(path);
|
|
100
|
+
}
|
|
101
|
+
async #modemPath(context) {
|
|
102
|
+
const snapshot = await this.#options.readSnapshot(context);
|
|
103
|
+
return snapshot.ok ? snapshot.modemPath : undefined;
|
|
104
|
+
}
|
|
105
|
+
#setLocationState(context, event) {
|
|
106
|
+
const key = String(context.physicalModemId);
|
|
107
|
+
const next = advanceGnssFixState(this.#locationStates.get(key) ?? GNSS_OFF, event);
|
|
108
|
+
this.#locationStates.set(key, next);
|
|
109
|
+
return next;
|
|
110
|
+
}
|
|
111
|
+
#unsupportedToggle() {
|
|
112
|
+
return {
|
|
113
|
+
outcome: 'unsupported',
|
|
114
|
+
reason: 'modem-not-found',
|
|
115
|
+
enabledSources: new Set(),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type MmDbusBackend, type QuiesceHook } from '../../backend/index.js';
|
|
2
|
+
import { type BandCertificationCatalog, type BandSku } from '../../band/index.js';
|
|
3
|
+
import { type NormalizedModemObservation } from '../../observations/index.js';
|
|
4
|
+
import type { ObservationListener } from '../../ports/index.js';
|
|
5
|
+
import type { DbusTransport } from '../../transport/index.js';
|
|
6
|
+
import type { ProviderDefinition, ProviderExecutionContext } from '../contracts.js';
|
|
7
|
+
import type { RuntimeCompositionOperationDeps } from './runtime-composition-operation.js';
|
|
8
|
+
import type { ModemManagerProviderLifecycle, ModemManagerProviderOperations, ModemManagerSnapshotResult } from './types.js';
|
|
9
|
+
export interface ModemManagerProviderOptions {
|
|
10
|
+
readonly transport: DbusTransport;
|
|
11
|
+
readonly destination?: string;
|
|
12
|
+
readonly quiesce?: QuiesceHook;
|
|
13
|
+
readonly now?: () => number;
|
|
14
|
+
/**
|
|
15
|
+
* The band-lock certification catalog. Defaults to the one shipped in this package,
|
|
16
|
+
* which is EMPTY — so a band write is refused on every device until a human-reviewed
|
|
17
|
+
* commit adds an entry carrying its bench transcript.
|
|
18
|
+
*/
|
|
19
|
+
readonly bandCertificationCatalog?: BandCertificationCatalog;
|
|
20
|
+
/**
|
|
21
|
+
* The device's SKU, if the embedding process can resolve one.
|
|
22
|
+
*
|
|
23
|
+
* ModemManager's `Modem` interface carries `Model` and `Revision` but NO USB
|
|
24
|
+
* `vid:pid`, and `BandSku` is keyed on all three — so this package structurally
|
|
25
|
+
* cannot build one from the bus alone, and a partial match would certify a family
|
|
26
|
+
* no reviewer looked at. With no resolver injected there is no SKU, hence no entry,
|
|
27
|
+
* hence no band write: fail-closed, which is this module's whole stance.
|
|
28
|
+
*/
|
|
29
|
+
readonly bandSku?: (context: ProviderExecutionContext) => BandSku | undefined;
|
|
30
|
+
readonly runtimeComposition?: RuntimeCompositionOperationDeps;
|
|
31
|
+
}
|
|
32
|
+
export declare class ModemManagerProvider implements ModemManagerProviderLifecycle {
|
|
33
|
+
#private;
|
|
34
|
+
readonly definition: ProviderDefinition<NormalizedModemObservation, ModemManagerProviderOperations>;
|
|
35
|
+
constructor(options: ModemManagerProviderOptions);
|
|
36
|
+
start(): ReturnType<MmDbusBackend['start']>;
|
|
37
|
+
observe(listener: ObservationListener): () => void;
|
|
38
|
+
stop(): Promise<void>;
|
|
39
|
+
readSnapshot(context: ProviderExecutionContext): Promise<ModemManagerSnapshotResult>;
|
|
40
|
+
}
|
|
41
|
+
export declare function createModemManagerProvider(options: ModemManagerProviderOptions): ModemManagerProvider;
|