@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,20 @@
|
|
|
1
|
+
import type { DeviceGeneration, OperationDescriptor, OperationResult } from '../../domain/index.js';
|
|
2
|
+
import type { ProviderExecutionContext } from '../contracts.js';
|
|
3
|
+
import { type HilinkProfile } from './provider.js';
|
|
4
|
+
export type ReadValue = Readonly<Record<string, unknown>>;
|
|
5
|
+
export type ReadOperation = {
|
|
6
|
+
readonly descriptor: OperationDescriptor<never, ReadValue>;
|
|
7
|
+
readonly read: (context: ProviderExecutionContext) => Promise<OperationResult<ReadValue>>;
|
|
8
|
+
};
|
|
9
|
+
export type WriteOperation = {
|
|
10
|
+
readonly descriptor: OperationDescriptor<string | boolean, ReadValue>;
|
|
11
|
+
readonly read: ReadOperation['read'];
|
|
12
|
+
readonly write: (context: ProviderExecutionContext, value: string | boolean) => Promise<OperationResult<ReadValue>>;
|
|
13
|
+
};
|
|
14
|
+
export declare function applied(generation: DeviceGeneration, value: ReadValue): OperationResult<ReadValue>;
|
|
15
|
+
export declare function refused(generation: DeviceGeneration, reason: string): OperationResult<ReadValue>;
|
|
16
|
+
export declare function operationDescriptor<I>(id: string, write: boolean, profile: HilinkProfile): OperationDescriptor<I, ReadValue>;
|
|
17
|
+
export declare function requestSucceeded(status: number, body: string): boolean;
|
|
18
|
+
export declare function capabilityPath(kind: 'status' | 'signal' | 'mode' | 'data'): string;
|
|
19
|
+
export declare function writeDocument(kind: 'mode' | 'data', value: string | boolean): string;
|
|
20
|
+
export declare function readbackMatches(kind: 'mode' | 'data', body: string, value: string | boolean): boolean;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { defineOperationDescriptor } from '../../domain/index.js';
|
|
2
|
+
import { parseHilinkDataCapability, parseHilinkXmlValue } from '../../hardware/router-parsers.js';
|
|
3
|
+
import { HILINK_PATHS } from './provider.js';
|
|
4
|
+
export function applied(generation, value) {
|
|
5
|
+
return { status: 'applied', value, generation, requiresReconciliation: false };
|
|
6
|
+
}
|
|
7
|
+
export function refused(generation, reason) {
|
|
8
|
+
return { status: 'refused', reason, generation, requiresReconciliation: false };
|
|
9
|
+
}
|
|
10
|
+
export function operationDescriptor(id, write, profile) {
|
|
11
|
+
return defineOperationDescriptor({
|
|
12
|
+
id,
|
|
13
|
+
support: {
|
|
14
|
+
read: { supported: true },
|
|
15
|
+
write: write ? { supported: true } : { supported: false, reason: 'read-only' },
|
|
16
|
+
},
|
|
17
|
+
authority: 'provider',
|
|
18
|
+
provider: 'huawei-hilink',
|
|
19
|
+
constraints: { kind: 'unconstrained' },
|
|
20
|
+
livePreconditions: ['fresh-session', 'capability-evidence'],
|
|
21
|
+
availability: { state: 'available' },
|
|
22
|
+
mutationImpact: write ? 'write' : 'read',
|
|
23
|
+
retryClass: 'never',
|
|
24
|
+
readback: write
|
|
25
|
+
? { required: true, reason: 'fresh-readback', matches: () => true }
|
|
26
|
+
: { required: false },
|
|
27
|
+
rollback: { required: false },
|
|
28
|
+
journal: { required: false },
|
|
29
|
+
admission: { required: true, reason: 'router-session' },
|
|
30
|
+
evidence: { profiles: [profile.id], firmware: [profile.firmware] },
|
|
31
|
+
confidence: 'high',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export function requestSucceeded(status, body) {
|
|
35
|
+
return status >= 200 && status < 300 && /<response>\s*OK\s*<\/response>/i.test(body);
|
|
36
|
+
}
|
|
37
|
+
export function capabilityPath(kind) {
|
|
38
|
+
return kind === 'status'
|
|
39
|
+
? HILINK_PATHS.status
|
|
40
|
+
: kind === 'signal'
|
|
41
|
+
? HILINK_PATHS.signal
|
|
42
|
+
: kind === 'mode'
|
|
43
|
+
? HILINK_PATHS.modeList
|
|
44
|
+
: HILINK_PATHS.data;
|
|
45
|
+
}
|
|
46
|
+
export function writeDocument(kind, value) {
|
|
47
|
+
return kind === 'mode'
|
|
48
|
+
? `<?xml version="1.0" encoding="UTF-8"?><request><NetworkMode>${String(value)}</NetworkMode></request>`
|
|
49
|
+
: `<?xml version="1.0" encoding="UTF-8"?><request><dataswitch>${value ? 1 : 0}</dataswitch></request>`;
|
|
50
|
+
}
|
|
51
|
+
export function readbackMatches(kind, body, value) {
|
|
52
|
+
if (kind === 'mode')
|
|
53
|
+
return parseHilinkXmlValue(body, 'NetworkMode') === String(value);
|
|
54
|
+
const parsed = parseHilinkDataCapability(body);
|
|
55
|
+
return parsed.state === 'reported' && parsed.enabled === Boolean(value);
|
|
56
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { NormalizedModemObservation } from '../../observations/index.js';
|
|
2
|
+
import type { ResourceOwnershipPort } from '../../ports/resource-ownership.js';
|
|
3
|
+
import type { ProviderDefinition, ProviderOperationsSurface } from '../contracts.js';
|
|
4
|
+
import { HuaweiProvider, type ReadOperation, type WriteOperation } from './runtime.js';
|
|
5
|
+
import type { HilinkTransport } from './transport.js';
|
|
6
|
+
export declare const HILINK_PATHS: {
|
|
7
|
+
readonly session: '/api/webserver/SesTokInfo';
|
|
8
|
+
readonly loginState: '/api/user/state-login';
|
|
9
|
+
readonly login: '/api/user/login';
|
|
10
|
+
readonly status: '/api/monitoring/status';
|
|
11
|
+
readonly signal: '/api/device/signal';
|
|
12
|
+
readonly modeList: '/api/net/net-mode-list';
|
|
13
|
+
readonly mode: '/api/net/net-mode';
|
|
14
|
+
readonly data: '/api/dialup/mobile-dataswitch';
|
|
15
|
+
};
|
|
16
|
+
export type HilinkProfile = {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly firmware: string;
|
|
19
|
+
readonly passwordType: 3 | 4;
|
|
20
|
+
};
|
|
21
|
+
export declare const HILINK_PROFILES: readonly [{
|
|
22
|
+
readonly id: 'e3372h-22.200-password-type-3';
|
|
23
|
+
readonly firmware: '22.200.05.00.1080';
|
|
24
|
+
readonly passwordType: 3;
|
|
25
|
+
}, {
|
|
26
|
+
readonly id: 'e3372h-22.333-password-type-4';
|
|
27
|
+
readonly firmware: '22.333.01.00.00';
|
|
28
|
+
readonly passwordType: 4;
|
|
29
|
+
}];
|
|
30
|
+
export type HilinkCredentials = {
|
|
31
|
+
readonly username: string;
|
|
32
|
+
readonly password: string;
|
|
33
|
+
};
|
|
34
|
+
export type HilinkOptions = {
|
|
35
|
+
readonly interfaceName: string;
|
|
36
|
+
readonly adminUrl: string;
|
|
37
|
+
readonly transport: HilinkTransport;
|
|
38
|
+
readonly ownership: ResourceOwnershipPort;
|
|
39
|
+
readonly credentials: HilinkCredentials;
|
|
40
|
+
readonly now?: () => number;
|
|
41
|
+
};
|
|
42
|
+
export type HilinkObservation = NormalizedModemObservation;
|
|
43
|
+
export type HuaweiOperations = ProviderOperationsSurface & {
|
|
44
|
+
readonly status: ReadOperation;
|
|
45
|
+
readonly signal: ReadOperation;
|
|
46
|
+
readonly mode: WriteOperation;
|
|
47
|
+
readonly data: WriteOperation;
|
|
48
|
+
};
|
|
49
|
+
export declare function hilinkProfile(profileId: string): HilinkProfile | undefined;
|
|
50
|
+
export declare function createHuaweiHiLinkProvider(options: HilinkOptions): HuaweiProvider;
|
|
51
|
+
export declare function createHuaweiHiLinkDefinition(options: HilinkOptions): ProviderDefinition<HilinkObservation, HuaweiOperations>;
|
|
52
|
+
export { HuaweiProvider as HuaweiHiLinkProvider };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { HuaweiProvider } from './runtime.js';
|
|
2
|
+
export const HILINK_PATHS = {
|
|
3
|
+
session: '/api/webserver/SesTokInfo',
|
|
4
|
+
loginState: '/api/user/state-login',
|
|
5
|
+
login: '/api/user/login',
|
|
6
|
+
status: '/api/monitoring/status',
|
|
7
|
+
signal: '/api/device/signal',
|
|
8
|
+
modeList: '/api/net/net-mode-list',
|
|
9
|
+
mode: '/api/net/net-mode',
|
|
10
|
+
data: '/api/dialup/mobile-dataswitch',
|
|
11
|
+
};
|
|
12
|
+
export const HILINK_PROFILES = [
|
|
13
|
+
{
|
|
14
|
+
id: 'e3372h-22.200-password-type-3',
|
|
15
|
+
firmware: '22.200.05.00.1080',
|
|
16
|
+
passwordType: 3,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: 'e3372h-22.333-password-type-4',
|
|
20
|
+
firmware: '22.333.01.00.00',
|
|
21
|
+
passwordType: 4,
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
export function hilinkProfile(profileId) {
|
|
25
|
+
return HILINK_PROFILES.find((profile) => profile.id === profileId);
|
|
26
|
+
}
|
|
27
|
+
export function createHuaweiHiLinkProvider(options) {
|
|
28
|
+
return new HuaweiProvider(options);
|
|
29
|
+
}
|
|
30
|
+
export function createHuaweiHiLinkDefinition(options) {
|
|
31
|
+
const runtime = createHuaweiHiLinkProvider(options);
|
|
32
|
+
return {
|
|
33
|
+
id: 'huawei-hilink',
|
|
34
|
+
profileVersion: '1',
|
|
35
|
+
eligibleTransports: ['network'],
|
|
36
|
+
passiveMatchers: HILINK_PROFILES.map((profile) => ({
|
|
37
|
+
id: `firmware-${profile.firmware}`,
|
|
38
|
+
fact: 'firmware',
|
|
39
|
+
expected: [profile.firmware],
|
|
40
|
+
profiles: [profile.id],
|
|
41
|
+
strength: 'strong',
|
|
42
|
+
required: true,
|
|
43
|
+
})),
|
|
44
|
+
unauthenticatedProbes: [
|
|
45
|
+
{
|
|
46
|
+
id: 'hilink-session-shape',
|
|
47
|
+
run: (request) => runtime.sessionProbe(request),
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
authenticatedProfile: {
|
|
51
|
+
algorithm: 'firmware-selected-hilink-password',
|
|
52
|
+
attemptLimit: 1,
|
|
53
|
+
authenticate: (request, candidates) => runtime.authenticateProfile(request, candidates),
|
|
54
|
+
},
|
|
55
|
+
capabilityReaders: [
|
|
56
|
+
runtime.capability('status'),
|
|
57
|
+
runtime.capability('signal'),
|
|
58
|
+
runtime.capability('mode'),
|
|
59
|
+
runtime.capability('data'),
|
|
60
|
+
],
|
|
61
|
+
observe: (context) => runtime.observe(context),
|
|
62
|
+
operations: (profile) => runtime.operations(profile),
|
|
63
|
+
contractFixtures: HILINK_PROFILES.map((profile) => ({
|
|
64
|
+
profile: profile.id,
|
|
65
|
+
request: {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
path: HILINK_PATHS.login,
|
|
68
|
+
passwordType: profile.passwordType,
|
|
69
|
+
interfaceBound: true,
|
|
70
|
+
redirects: 'disabled',
|
|
71
|
+
},
|
|
72
|
+
response: { status: 'matched', sessionMaterial: '[redacted]' },
|
|
73
|
+
})),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export { HuaweiProvider as HuaweiHiLinkProvider };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ObservationEnvelope } from '../../domain/index.js';
|
|
2
|
+
import type { AuthenticatedProfileResult, CapabilityReader, ProviderExecutionContext, ProviderMatchRequest } from '../contracts.js';
|
|
3
|
+
import { type HilinkObservation, type HuaweiOperations } from './provider.js';
|
|
4
|
+
import { HilinkSessionRuntime } from './session.js';
|
|
5
|
+
export type { ReadOperation, WriteOperation } from './operations.js';
|
|
6
|
+
export declare class HuaweiProvider extends HilinkSessionRuntime {
|
|
7
|
+
sessionProbe(request: ProviderMatchRequest): Promise<{
|
|
8
|
+
signal: "match" | "unknown";
|
|
9
|
+
strength: 'strong';
|
|
10
|
+
profiles: ("e3372h-22.200-password-type-3" | "e3372h-22.333-password-type-4")[];
|
|
11
|
+
detail: string;
|
|
12
|
+
}>;
|
|
13
|
+
authenticateProfile(request: ProviderMatchRequest, candidates: readonly string[]): Promise<AuthenticatedProfileResult>;
|
|
14
|
+
observe(context: ProviderExecutionContext): Promise<readonly ObservationEnvelope<HilinkObservation>[]>;
|
|
15
|
+
capability(kind: 'status' | 'signal' | 'mode' | 'data'): CapabilityReader;
|
|
16
|
+
operations(profileId: string): HuaweiOperations;
|
|
17
|
+
private sessionFor;
|
|
18
|
+
private capabilitySupported;
|
|
19
|
+
private readOperation;
|
|
20
|
+
private writeOperation;
|
|
21
|
+
private write;
|
|
22
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { parseHilinkCapabilities, parseHilinkDataCapability, parseHilinkSession, parseHilinkXmlValue, } from '../../hardware/router-parsers.js';
|
|
2
|
+
import { normalizeHilinkObservation } from '../../observations/sources/hilink.js';
|
|
3
|
+
import { applied, capabilityPath, operationDescriptor, readbackMatches, refused, requestSucceeded, writeDocument, } from './operations.js';
|
|
4
|
+
import { HILINK_PATHS, HILINK_PROFILES, hilinkProfile, } from './provider.js';
|
|
5
|
+
import { HilinkSessionRuntime } from './session.js';
|
|
6
|
+
export class HuaweiProvider extends HilinkSessionRuntime {
|
|
7
|
+
async sessionProbe(request) {
|
|
8
|
+
const profile = HILINK_PROFILES.find((candidate) => candidate.firmware === request.firmware);
|
|
9
|
+
const response = await this.request('GET', HILINK_PATHS.session);
|
|
10
|
+
const matches = response.status === 200 && parseHilinkSession(response.body) !== undefined;
|
|
11
|
+
return {
|
|
12
|
+
signal: matches && profile !== undefined ? 'match' : 'unknown',
|
|
13
|
+
strength: 'strong',
|
|
14
|
+
profiles: profile === undefined ? [] : [profile.id],
|
|
15
|
+
detail: matches ? 'session-token-evidence' : 'session-token-not-proven',
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async authenticateProfile(request, candidates) {
|
|
19
|
+
const candidate = candidates.length === 1 ? hilinkProfile(candidates[0] ?? '') : undefined;
|
|
20
|
+
if (candidate === undefined)
|
|
21
|
+
return { status: 'refused', detail: 'profile-mismatch' };
|
|
22
|
+
const result = await this.authenticate(request, candidate, true);
|
|
23
|
+
if (result.status === 'ready')
|
|
24
|
+
return { status: 'matched', profile: candidate.id, detail: 'login-ok' };
|
|
25
|
+
return {
|
|
26
|
+
status: result.reason === 'unreachable' ? 'unavailable' : 'refused',
|
|
27
|
+
detail: result.reason,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
async observe(context) {
|
|
31
|
+
const ready = await this.sessionFor(context);
|
|
32
|
+
if (ready === undefined)
|
|
33
|
+
return [];
|
|
34
|
+
const [status, signal, modeList, mode] = await Promise.all([
|
|
35
|
+
this.getResponse(HILINK_PATHS.status, ready),
|
|
36
|
+
this.getResponse(HILINK_PATHS.signal, ready),
|
|
37
|
+
this.getResponse(HILINK_PATHS.modeList, ready),
|
|
38
|
+
this.getResponse(HILINK_PATHS.mode, ready),
|
|
39
|
+
]);
|
|
40
|
+
const input = {
|
|
41
|
+
status: status.body,
|
|
42
|
+
signal: signal.body,
|
|
43
|
+
netModeList: modeList.body,
|
|
44
|
+
netMode: mode.body,
|
|
45
|
+
};
|
|
46
|
+
return [
|
|
47
|
+
normalizeHilinkObservation(input, {
|
|
48
|
+
stableKey: `modem:${this.options.interfaceName}`,
|
|
49
|
+
generation: context.generation,
|
|
50
|
+
sourceEpoch: 1,
|
|
51
|
+
observedAt: (this.options.now?.() ?? Date.now()),
|
|
52
|
+
}),
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
capability(kind) {
|
|
56
|
+
return {
|
|
57
|
+
id: `hilink-${kind}`,
|
|
58
|
+
read: async (context) => {
|
|
59
|
+
const session = await this.sessionFor(context);
|
|
60
|
+
if (session === undefined)
|
|
61
|
+
return { signal: 'unknown', strength: 'strong', detail: 'session-unavailable' };
|
|
62
|
+
const path = capabilityPath(kind);
|
|
63
|
+
const response = await this.getResponse(path, session);
|
|
64
|
+
const supported = this.capabilitySupported(kind, response.body) && !this.authExpired(response);
|
|
65
|
+
return {
|
|
66
|
+
signal: supported ? 'match' : 'mismatch',
|
|
67
|
+
strength: 'strong',
|
|
68
|
+
detail: supported ? 'capability-reported' : 'capability-refused',
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
operations(profileId) {
|
|
74
|
+
const profile = hilinkProfile(profileId);
|
|
75
|
+
if (profile === undefined)
|
|
76
|
+
return unsupportedOperations(profileId);
|
|
77
|
+
return {
|
|
78
|
+
access: 'read-write',
|
|
79
|
+
status: this.readOperation('status', HILINK_PATHS.status, profile),
|
|
80
|
+
signal: this.readOperation('signal', HILINK_PATHS.signal, profile),
|
|
81
|
+
mode: this.writeOperation('mode', HILINK_PATHS.mode, profile),
|
|
82
|
+
data: this.writeOperation('data', HILINK_PATHS.data, profile),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
async sessionFor(context) {
|
|
86
|
+
const cached = this.cachedSession(context);
|
|
87
|
+
if (cached !== undefined)
|
|
88
|
+
return cached;
|
|
89
|
+
const profile = hilinkProfile(context.profile);
|
|
90
|
+
if (profile === undefined)
|
|
91
|
+
return undefined;
|
|
92
|
+
const result = await this.authenticate(context, profile, true);
|
|
93
|
+
return result.status === 'ready' ? result.session : undefined;
|
|
94
|
+
}
|
|
95
|
+
capabilitySupported(kind, body) {
|
|
96
|
+
if (kind === 'mode')
|
|
97
|
+
return parseHilinkCapabilities({ netModeList: body }).net_mode.state === 'reported';
|
|
98
|
+
if (kind === 'data')
|
|
99
|
+
return parseHilinkDataCapability(body).state === 'reported';
|
|
100
|
+
return parseHilinkXmlValue(body, 'code') === undefined && /<response>/i.test(body);
|
|
101
|
+
}
|
|
102
|
+
readOperation(id, path, profile) {
|
|
103
|
+
return {
|
|
104
|
+
descriptor: operationDescriptor(id, false, profile),
|
|
105
|
+
read: async (context) => {
|
|
106
|
+
const result = await this.authenticate(context, profile, false);
|
|
107
|
+
if (result.status === 'refused')
|
|
108
|
+
return refused(context.generation, result.reason);
|
|
109
|
+
const response = await this.getResponse(path, result.session);
|
|
110
|
+
if (this.authExpired(response))
|
|
111
|
+
return refused(context.generation, 'auth-expired');
|
|
112
|
+
if (response.status < 200 || response.status >= 300)
|
|
113
|
+
return refused(context.generation, 'http-failure');
|
|
114
|
+
return applied(context.generation, { body: response.body });
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
writeOperation(id, path, profile) {
|
|
119
|
+
return {
|
|
120
|
+
descriptor: operationDescriptor(id, true, profile),
|
|
121
|
+
read: this.readOperation(`${id}-read`, path, profile).read,
|
|
122
|
+
write: (context, value) => this.write(context, profile, id, path, value),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
async write(context, profile, kind, path, value) {
|
|
126
|
+
return this.withDevice(context, async () => {
|
|
127
|
+
const ownership = await this.options.ownership.acquire({ resource: 'router-session' });
|
|
128
|
+
if (ownership.status === 'refused')
|
|
129
|
+
return refused(context.generation, 'busy');
|
|
130
|
+
try {
|
|
131
|
+
const ready = await this.authenticate(context, profile, false);
|
|
132
|
+
if (ready.status === 'refused')
|
|
133
|
+
return refused(context.generation, ready.reason);
|
|
134
|
+
const capabilityPath = kind === 'mode' ? HILINK_PATHS.modeList : HILINK_PATHS.data;
|
|
135
|
+
const capability = await this.getResponse(capabilityPath, ready.session);
|
|
136
|
+
if (this.authExpired(capability))
|
|
137
|
+
return refused(context.generation, 'auth-expired');
|
|
138
|
+
if (!this.capabilitySupported(kind, capability.body))
|
|
139
|
+
return refused(context.generation, 'capability-unavailable');
|
|
140
|
+
if (kind === 'mode') {
|
|
141
|
+
const offered = parseHilinkCapabilities({ netModeList: capability.body }).net_mode;
|
|
142
|
+
if (offered.state !== 'reported' ||
|
|
143
|
+
!offered.modes.some((mode) => mode.id === String(value)))
|
|
144
|
+
return refused(context.generation, 'capability-unavailable');
|
|
145
|
+
}
|
|
146
|
+
const body = writeDocument(kind, value);
|
|
147
|
+
const posted = await this.postResponse(path, body, ready.session);
|
|
148
|
+
if (this.authExpired(posted))
|
|
149
|
+
return refused(context.generation, 'auth-expired');
|
|
150
|
+
if (!requestSucceeded(posted.status, posted.body))
|
|
151
|
+
return refused(context.generation, 'http-failure');
|
|
152
|
+
const fresh = await this.authenticate(context, profile, false);
|
|
153
|
+
if (fresh.status === 'refused')
|
|
154
|
+
return refused(context.generation, fresh.reason);
|
|
155
|
+
const readback = await this.getResponse(path, fresh.session);
|
|
156
|
+
if (this.authExpired(readback))
|
|
157
|
+
return refused(context.generation, 'auth-expired');
|
|
158
|
+
const matches = readbackMatches(kind, readback.body, value);
|
|
159
|
+
return matches
|
|
160
|
+
? applied(context.generation, { body: readback.body })
|
|
161
|
+
: refused(context.generation, 'not-applied');
|
|
162
|
+
}
|
|
163
|
+
finally {
|
|
164
|
+
await ownership.lease.release();
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function unsupportedOperations(_profileId) {
|
|
170
|
+
throw new Error('unsupported-hilink-profile');
|
|
171
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ProviderExecutionContext, ProviderMatchRequest } from '../contracts.js';
|
|
2
|
+
import { type HilinkOptions, type HilinkProfile } from './provider.js';
|
|
3
|
+
import type { HilinkHttpResponse } from './transport.js';
|
|
4
|
+
export type HilinkSession = {
|
|
5
|
+
readonly cookie: string;
|
|
6
|
+
readonly token: string;
|
|
7
|
+
};
|
|
8
|
+
export type HilinkSessionFailure = 'auth-expired' | 'http-failure' | 'malformed' | 'profile-mismatch' | 'unreachable';
|
|
9
|
+
export type HilinkSessionResult = {
|
|
10
|
+
readonly status: 'ready';
|
|
11
|
+
readonly session: HilinkSession;
|
|
12
|
+
} | {
|
|
13
|
+
readonly status: 'refused';
|
|
14
|
+
readonly reason: HilinkSessionFailure;
|
|
15
|
+
};
|
|
16
|
+
export declare class HilinkSessionRuntime {
|
|
17
|
+
#private;
|
|
18
|
+
protected readonly options: HilinkOptions;
|
|
19
|
+
constructor(options: HilinkOptions);
|
|
20
|
+
protected withDevice<T>(context: ProviderExecutionContext, run: () => Promise<T>): Promise<T>;
|
|
21
|
+
protected sessionKey(context: ProviderMatchRequest): string;
|
|
22
|
+
protected cachedSession(context: ProviderMatchRequest): HilinkSession | undefined;
|
|
23
|
+
protected authenticate(context: ProviderMatchRequest, profile: HilinkProfile, cache: boolean): Promise<HilinkSessionResult>;
|
|
24
|
+
protected authExpired(response: HilinkHttpResponse): boolean;
|
|
25
|
+
protected getResponse(path: string, session: HilinkSession): Promise<HilinkHttpResponse>;
|
|
26
|
+
protected postResponse(path: string, body: string, session: HilinkSession): Promise<HilinkHttpResponse>;
|
|
27
|
+
protected request(method: 'GET' | 'POST', path: string, body?: string, headers?: readonly string[]): Promise<HilinkHttpResponse>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { parseHilinkSession, parseHilinkUserState, parseHilinkXmlValue, } from '../../hardware/router-parsers.js';
|
|
3
|
+
import { HILINK_PATHS } from './provider.js';
|
|
4
|
+
function base64(value) {
|
|
5
|
+
return Buffer.from(value).toString('base64');
|
|
6
|
+
}
|
|
7
|
+
function derivePassword(profile, username, password, token) {
|
|
8
|
+
if (profile.passwordType === 3)
|
|
9
|
+
return base64(password);
|
|
10
|
+
const passwordHash = base64(createHash('sha256').update(password).digest('hex'));
|
|
11
|
+
const loginHash = createHash('sha256').update(`${username}${passwordHash}${token}`).digest('hex');
|
|
12
|
+
return base64(loginHash);
|
|
13
|
+
}
|
|
14
|
+
function escapeXml(value) {
|
|
15
|
+
return value
|
|
16
|
+
.replace(/&/g, '&')
|
|
17
|
+
.replace(/</g, '<')
|
|
18
|
+
.replace(/>/g, '>')
|
|
19
|
+
.replace(/"/g, '"')
|
|
20
|
+
.replace(/'/g, ''');
|
|
21
|
+
}
|
|
22
|
+
function loginDocument(profile, username, password, token) {
|
|
23
|
+
return `<?xml version="1.0" encoding="UTF-8"?><request><Username>${escapeXml(username)}</Username><Password>${derivePassword(profile, username, password, token)}</Password><password_type>${profile.passwordType}</password_type></request>`;
|
|
24
|
+
}
|
|
25
|
+
function responseHeader(response, name) {
|
|
26
|
+
const expected = name.toLowerCase();
|
|
27
|
+
return Object.entries(response.headers ?? {}).find(([key]) => key.toLowerCase() === expected)?.[1];
|
|
28
|
+
}
|
|
29
|
+
function loggedInSession(response, fallback) {
|
|
30
|
+
const cookie = responseHeader(response, 'set-cookie')?.split(';', 1)[0]?.trim() ?? fallback.cookie;
|
|
31
|
+
const tokenHeader = responseHeader(response, '__requestverificationtoken') ??
|
|
32
|
+
responseHeader(response, '__requestverificationtokenone');
|
|
33
|
+
const token = tokenHeader?.split('#', 1)[0]?.trim() ?? fallback.token;
|
|
34
|
+
return { cookie, token };
|
|
35
|
+
}
|
|
36
|
+
export class HilinkSessionRuntime {
|
|
37
|
+
options;
|
|
38
|
+
#authenticated = new Map();
|
|
39
|
+
#serial = new Map();
|
|
40
|
+
constructor(options) {
|
|
41
|
+
this.options = options;
|
|
42
|
+
}
|
|
43
|
+
async withDevice(context, run) {
|
|
44
|
+
const key = String(context.physicalModemId);
|
|
45
|
+
const prior = this.#serial.get(key) ?? Promise.resolve();
|
|
46
|
+
let release = () => { };
|
|
47
|
+
const current = new Promise((resolve) => {
|
|
48
|
+
release = resolve;
|
|
49
|
+
});
|
|
50
|
+
const chain = prior.then(() => current);
|
|
51
|
+
this.#serial.set(key, chain);
|
|
52
|
+
await prior;
|
|
53
|
+
try {
|
|
54
|
+
return await run();
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
release();
|
|
58
|
+
if (this.#serial.get(key) === chain)
|
|
59
|
+
this.#serial.delete(key);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
sessionKey(context) {
|
|
63
|
+
return `${context.physicalModemId}:${context.generation}`;
|
|
64
|
+
}
|
|
65
|
+
cachedSession(context) {
|
|
66
|
+
return this.#authenticated.get(this.sessionKey(context));
|
|
67
|
+
}
|
|
68
|
+
async authenticate(context, profile, cache) {
|
|
69
|
+
const sessionResponse = await this.request('GET', HILINK_PATHS.session);
|
|
70
|
+
if (sessionResponse.status === 401 || sessionResponse.status === 403)
|
|
71
|
+
return { status: 'refused', reason: 'auth-expired' };
|
|
72
|
+
if (sessionResponse.status !== 200)
|
|
73
|
+
return { status: 'refused', reason: 'unreachable' };
|
|
74
|
+
const initial = parseHilinkSession(sessionResponse.body);
|
|
75
|
+
if (initial === undefined)
|
|
76
|
+
return { status: 'refused', reason: 'malformed' };
|
|
77
|
+
const stateResponse = await this.getResponse(HILINK_PATHS.loginState, initial);
|
|
78
|
+
if (this.authExpired(stateResponse))
|
|
79
|
+
return { status: 'refused', reason: 'auth-expired' };
|
|
80
|
+
const state = parseHilinkUserState(stateResponse.body);
|
|
81
|
+
if (state === undefined)
|
|
82
|
+
return { status: 'refused', reason: 'malformed' };
|
|
83
|
+
if (state.passwordType !== profile.passwordType)
|
|
84
|
+
return { status: 'refused', reason: 'profile-mismatch' };
|
|
85
|
+
const login = await this.postResponse(HILINK_PATHS.login, loginDocument(profile, this.options.credentials.username, this.options.credentials.password, initial.token), initial);
|
|
86
|
+
if (this.authExpired(login))
|
|
87
|
+
return { status: 'refused', reason: 'auth-expired' };
|
|
88
|
+
if (login.status !== 200 || !/<response>\s*OK\s*<\/response>/i.test(login.body))
|
|
89
|
+
return { status: 'refused', reason: 'http-failure' };
|
|
90
|
+
const session = loggedInSession(login, initial);
|
|
91
|
+
if (cache)
|
|
92
|
+
this.#authenticated.set(this.sessionKey(context), session);
|
|
93
|
+
return { status: 'ready', session };
|
|
94
|
+
}
|
|
95
|
+
authExpired(response) {
|
|
96
|
+
return (response.status === 401 ||
|
|
97
|
+
response.status === 403 ||
|
|
98
|
+
parseHilinkXmlValue(response.body, 'code') === '125002');
|
|
99
|
+
}
|
|
100
|
+
getResponse(path, session) {
|
|
101
|
+
return this.request('GET', path, undefined, [`Cookie: ${session.cookie}`]);
|
|
102
|
+
}
|
|
103
|
+
postResponse(path, body, session) {
|
|
104
|
+
return this.request('POST', path, body, [
|
|
105
|
+
`Cookie: ${session.cookie}`,
|
|
106
|
+
`__RequestVerificationToken: ${session.token}`,
|
|
107
|
+
'Content-Type: application/xml',
|
|
108
|
+
]);
|
|
109
|
+
}
|
|
110
|
+
request(method, path, body, headers = []) {
|
|
111
|
+
return this.options.transport.request({
|
|
112
|
+
method,
|
|
113
|
+
url: `${this.options.adminUrl}${path}`,
|
|
114
|
+
...(body === undefined ? {} : { body }),
|
|
115
|
+
headers,
|
|
116
|
+
interfaceName: this.options.interfaceName,
|
|
117
|
+
redirect: 'error',
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type HilinkHttpRequest = {
|
|
2
|
+
readonly method: 'GET' | 'POST';
|
|
3
|
+
readonly url: string;
|
|
4
|
+
readonly body?: string;
|
|
5
|
+
readonly headers: readonly string[];
|
|
6
|
+
readonly interfaceName: string;
|
|
7
|
+
readonly redirect: 'error';
|
|
8
|
+
};
|
|
9
|
+
export type HilinkHttpResponse = {
|
|
10
|
+
readonly status: number;
|
|
11
|
+
readonly body: string;
|
|
12
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
13
|
+
};
|
|
14
|
+
export interface HilinkTransport {
|
|
15
|
+
request(request: HilinkHttpRequest): Promise<HilinkHttpResponse>;
|
|
16
|
+
}
|
|
17
|
+
export type HilinkReplayExchange = HilinkHttpRequest & {
|
|
18
|
+
readonly response: HilinkHttpResponse;
|
|
19
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './contracts.js';
|
|
2
|
+
export * from './huawei-hilink/index.js';
|
|
3
|
+
export * from './matcher.js';
|
|
4
|
+
export * from './modem-manager/index.js';
|
|
5
|
+
export * from './network-manager/index.js';
|
|
6
|
+
export * from './registry.js';
|
|
7
|
+
export * from './ufi-himi/index.js';
|
|
8
|
+
export * from './zte-goform/index.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './contracts.js';
|
|
2
|
+
export * from './huawei-hilink/index.js';
|
|
3
|
+
export * from './matcher.js';
|
|
4
|
+
export * from './modem-manager/index.js';
|
|
5
|
+
export * from './network-manager/index.js';
|
|
6
|
+
export * from './registry.js';
|
|
7
|
+
export * from './ufi-himi/index.js';
|
|
8
|
+
export * from './zte-goform/index.js';
|