@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,101 @@
|
|
|
1
|
+
import type { EpochMillis, IdentityConfidence } from '../domain/index.js';
|
|
2
|
+
import type { ConnectionId, DeviceIfname } from '../ports/index.js';
|
|
3
|
+
import { type CatalogEntry, type CertifiedCatalog, type MmUsbMode, type PermittedTransition, type RuntimeCompositionCapability, type RuntimeCompositionMode, type RuntimeCompositionVendor, type SkuDiscriminator } from '../usb-mode/index.js';
|
|
4
|
+
import type { InterlockTarget, LifecycleInterlock } from './lifecycle-interlock.js';
|
|
5
|
+
/** A held "transition active" interlock — released when the transaction ends. */
|
|
6
|
+
export interface InterlockHold {
|
|
7
|
+
release(): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The transition interlock. `canDisrupt` (from `LifecycleInterlock`) answers "is it
|
|
11
|
+
* safe to disrupt this modem now?"; `hold` marks a transition in progress so a
|
|
12
|
+
* streaming start is blocked for its duration. Both directions, one seam.
|
|
13
|
+
*/
|
|
14
|
+
export interface TransitionInterlock extends LifecycleInterlock {
|
|
15
|
+
hold(target: InterlockTarget): Promise<InterlockHold>;
|
|
16
|
+
}
|
|
17
|
+
/** The Phase-A interlock: always allows, holds nothing. */
|
|
18
|
+
export declare const ALLOW_ALL_TRANSITION_INTERLOCK: TransitionInterlock;
|
|
19
|
+
/** Live, re-evaluable readiness inputs — re-polled at entry AND in-actor. */
|
|
20
|
+
export interface TransitionReadiness {
|
|
21
|
+
/** The identity confidence from A3.2's ladder; `'low'` refuses the transition. */
|
|
22
|
+
readonly identityConfidence: IdentityConfidence;
|
|
23
|
+
}
|
|
24
|
+
type UsbModeTransitionRequestBase = {
|
|
25
|
+
readonly stableKey: string;
|
|
26
|
+
readonly connectionId: ConnectionId;
|
|
27
|
+
readonly deviceIfname: DeviceIfname;
|
|
28
|
+
/** Physical-topology UID captured BEFORE the switch — survives re-enumeration. */
|
|
29
|
+
readonly cachedPhysicalUid: string;
|
|
30
|
+
/** The MM `Device` UID to inhibit by (cached — the modem disappears mid-switch). */
|
|
31
|
+
readonly inhibitUid: string;
|
|
32
|
+
/** Must be `true` (mirrors the CLI `--confirm` flag). */
|
|
33
|
+
readonly confirm: boolean;
|
|
34
|
+
/** Must be `true` (an extra maintenance-mode safety gate). */
|
|
35
|
+
readonly maintenance: boolean;
|
|
36
|
+
readonly now: EpochMillis;
|
|
37
|
+
/** Live readiness, re-polled at entry AND in-actor (TOCTOU-safe). */
|
|
38
|
+
probeReadiness(): Promise<TransitionReadiness>;
|
|
39
|
+
};
|
|
40
|
+
export type CatalogUsbModeTransitionRequest = UsbModeTransitionRequestBase & {
|
|
41
|
+
readonly strategy?: 'catalog';
|
|
42
|
+
readonly sku: SkuDiscriminator;
|
|
43
|
+
readonly fromMode: MmUsbMode;
|
|
44
|
+
readonly toMode: MmUsbMode;
|
|
45
|
+
};
|
|
46
|
+
export type RuntimeUsbModeTransitionRequest = UsbModeTransitionRequestBase & {
|
|
47
|
+
readonly strategy: 'runtime';
|
|
48
|
+
readonly vendor: RuntimeCompositionVendor;
|
|
49
|
+
readonly sku?: SkuDiscriminator;
|
|
50
|
+
readonly fromMode: RuntimeCompositionMode;
|
|
51
|
+
readonly toMode: RuntimeCompositionMode;
|
|
52
|
+
readonly capability: RuntimeCompositionCapability;
|
|
53
|
+
};
|
|
54
|
+
export type UsbModeTransitionRequest = CatalogUsbModeTransitionRequest | RuntimeUsbModeTransitionRequest;
|
|
55
|
+
/** How a transition ended. */
|
|
56
|
+
export type UsbModeTransitionOutcome = {
|
|
57
|
+
readonly status: 'refused';
|
|
58
|
+
readonly stage: 'entry' | 'in-actor';
|
|
59
|
+
readonly reason: string;
|
|
60
|
+
readonly steps: readonly string[];
|
|
61
|
+
} | {
|
|
62
|
+
readonly status: 'succeeded';
|
|
63
|
+
readonly newIfname: DeviceIfname;
|
|
64
|
+
readonly steps: readonly string[];
|
|
65
|
+
} | {
|
|
66
|
+
readonly status: 'failed';
|
|
67
|
+
readonly degraded: boolean;
|
|
68
|
+
readonly reason: string;
|
|
69
|
+
readonly steps: readonly string[];
|
|
70
|
+
};
|
|
71
|
+
export type UsbModeTransitionPlan = {
|
|
72
|
+
readonly atCommand: string;
|
|
73
|
+
readonly applyCommand?: string;
|
|
74
|
+
readonly proof: {
|
|
75
|
+
readonly tier: 'catalog-descriptors';
|
|
76
|
+
readonly transition: PermittedTransition;
|
|
77
|
+
} | {
|
|
78
|
+
readonly tier: 'runtime-requery';
|
|
79
|
+
readonly vendor: RuntimeCompositionVendor;
|
|
80
|
+
readonly target: RuntimeCompositionMode;
|
|
81
|
+
readonly currentQuery: string;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
export type PreconditionResult = {
|
|
85
|
+
readonly ok: true;
|
|
86
|
+
readonly entry?: CatalogEntry;
|
|
87
|
+
readonly plan: UsbModeTransitionPlan;
|
|
88
|
+
readonly allowlistedCommands: readonly string[];
|
|
89
|
+
} | {
|
|
90
|
+
readonly ok: false;
|
|
91
|
+
readonly reason: string;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Check every transition precondition against the LIVE inputs. Called at entry and
|
|
95
|
+
* again in-actor; a request that passed at entry can fail here if the identity
|
|
96
|
+
* confidence dropped or the interlock closed in the meantime. Order is cheap-static
|
|
97
|
+
* checks first (confirm, maintenance, catalog, permitted) then the live probes
|
|
98
|
+
* (identity, interlock), so a refusal touches as little as possible.
|
|
99
|
+
*/
|
|
100
|
+
export declare function checkTransitionPreconditions(request: UsbModeTransitionRequest, catalog: CertifiedCatalog, interlock: TransitionInterlock): Promise<PreconditionResult>;
|
|
101
|
+
export {};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// USB-mode transition preconditions + the transition interlock seam.
|
|
2
|
+
//
|
|
3
|
+
// The preconditions gate a mode switch and are checked TWICE (draft §rounds 5/6
|
|
4
|
+
// TOCTOU): once at transaction entry (so a doomed request never enters the actor) and
|
|
5
|
+
// again INSIDE the actor (so a request that was valid at entry but became invalid
|
|
6
|
+
// while queued is caught before any disruptive call). `checkTransitionPreconditions`
|
|
7
|
+
// re-polls the LIVE inputs (`probeReadiness`, `interlock.canDisrupt`) each call, so
|
|
8
|
+
// the two checks can genuinely disagree — that disagreement is the TIER-B race.
|
|
9
|
+
//
|
|
10
|
+
// The interlock is BIDIRECTIONAL: `canDisrupt` is the streaming→transition gate (may
|
|
11
|
+
// I disrupt now?), inherited from A3.4's `LifecycleInterlock`; `hold` is the
|
|
12
|
+
// transition→streaming gate (mark "transition active" until released). The Phase-A
|
|
13
|
+
// stub allows both; Phase B wires CeraUI's streaming-admission check into the same
|
|
14
|
+
// interface without changing the transaction.
|
|
15
|
+
import { buildRuntimeCompositionSetCommand, findCatalogEntry, findPermittedTransition, RUNTIME_COMPOSITION_QUERY_REGISTRY, } from '../usb-mode/index.js';
|
|
16
|
+
const NO_OP_HOLD = {
|
|
17
|
+
release() {
|
|
18
|
+
return Promise.resolve();
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
/** The Phase-A interlock: always allows, holds nothing. */
|
|
22
|
+
export const ALLOW_ALL_TRANSITION_INTERLOCK = {
|
|
23
|
+
canDisrupt() {
|
|
24
|
+
return Promise.resolve({ allow: true });
|
|
25
|
+
},
|
|
26
|
+
hold() {
|
|
27
|
+
return Promise.resolve(NO_OP_HOLD);
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
function sameRuntimeMode(left, right) {
|
|
31
|
+
return Object.is(left, right);
|
|
32
|
+
}
|
|
33
|
+
function runtimePlan(request, catalog) {
|
|
34
|
+
const capability = request.capability;
|
|
35
|
+
if (capability.status !== 'available')
|
|
36
|
+
return { ok: false, reason: 'runtime capability unknown' };
|
|
37
|
+
if (!sameRuntimeMode(capability.current, request.fromMode))
|
|
38
|
+
return { ok: false, reason: 'runtime current mode changed' };
|
|
39
|
+
if (!capability.returnPathProven ||
|
|
40
|
+
!capability.offerable.some((mode) => sameRuntimeMode(mode, request.toMode)))
|
|
41
|
+
return { ok: false, reason: 'no-return-path' };
|
|
42
|
+
const atCommand = buildRuntimeCompositionSetCommand(request.vendor, request.toMode);
|
|
43
|
+
if (atCommand === undefined)
|
|
44
|
+
return { ok: false, reason: 'runtime target command unavailable' };
|
|
45
|
+
const entry = request.sku === undefined ? undefined : findCatalogEntry(catalog, request.sku);
|
|
46
|
+
if (entry !== undefined) {
|
|
47
|
+
const reviewed = entry.permittedTransitions.find((transition) => transition.atCommand === atCommand);
|
|
48
|
+
if (reviewed !== undefined) {
|
|
49
|
+
return {
|
|
50
|
+
ok: true,
|
|
51
|
+
entry,
|
|
52
|
+
plan: {
|
|
53
|
+
atCommand: reviewed.atCommand,
|
|
54
|
+
...(reviewed.applyCommand === undefined ? {} : { applyCommand: reviewed.applyCommand }),
|
|
55
|
+
proof: { tier: 'catalog-descriptors', transition: reviewed },
|
|
56
|
+
},
|
|
57
|
+
allowlistedCommands: entry.permittedTransitions.flatMap((transition) => transition.applyCommand === undefined
|
|
58
|
+
? [transition.atCommand]
|
|
59
|
+
: [transition.atCommand, transition.applyCommand]),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
ok: true,
|
|
65
|
+
...(entry === undefined ? {} : { entry }),
|
|
66
|
+
plan: {
|
|
67
|
+
atCommand,
|
|
68
|
+
proof: {
|
|
69
|
+
tier: 'runtime-requery',
|
|
70
|
+
vendor: request.vendor,
|
|
71
|
+
target: request.toMode,
|
|
72
|
+
currentQuery: RUNTIME_COMPOSITION_QUERY_REGISTRY[request.vendor].current,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
allowlistedCommands: [atCommand],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Check every transition precondition against the LIVE inputs. Called at entry and
|
|
80
|
+
* again in-actor; a request that passed at entry can fail here if the identity
|
|
81
|
+
* confidence dropped or the interlock closed in the meantime. Order is cheap-static
|
|
82
|
+
* checks first (confirm, maintenance, catalog, permitted) then the live probes
|
|
83
|
+
* (identity, interlock), so a refusal touches as little as possible.
|
|
84
|
+
*/
|
|
85
|
+
export async function checkTransitionPreconditions(request, catalog, interlock) {
|
|
86
|
+
if (!request.confirm) {
|
|
87
|
+
return { ok: false, reason: 'confirm:true is required (missing --confirm)' };
|
|
88
|
+
}
|
|
89
|
+
if (!request.maintenance) {
|
|
90
|
+
return { ok: false, reason: 'maintenance flag is required' };
|
|
91
|
+
}
|
|
92
|
+
let staticResult;
|
|
93
|
+
if (request.strategy === 'runtime') {
|
|
94
|
+
staticResult = runtimePlan(request, catalog);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const entry = findCatalogEntry(catalog, request.sku);
|
|
98
|
+
if (entry === undefined) {
|
|
99
|
+
return { ok: false, reason: `uncertified SKU ${request.sku.vidPid} ${request.sku.model}` };
|
|
100
|
+
}
|
|
101
|
+
const transition = findPermittedTransition(entry, request.fromMode, request.toMode);
|
|
102
|
+
if (transition === undefined) {
|
|
103
|
+
return {
|
|
104
|
+
ok: false,
|
|
105
|
+
reason: `transition ${request.fromMode}->${request.toMode} not permitted for ${request.sku.model}`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
staticResult = {
|
|
109
|
+
ok: true,
|
|
110
|
+
entry,
|
|
111
|
+
plan: {
|
|
112
|
+
atCommand: transition.atCommand,
|
|
113
|
+
...(transition.applyCommand === undefined ? {} : { applyCommand: transition.applyCommand }),
|
|
114
|
+
proof: { tier: 'catalog-descriptors', transition },
|
|
115
|
+
},
|
|
116
|
+
allowlistedCommands: entry.permittedTransitions.flatMap((candidate) => candidate.applyCommand === undefined
|
|
117
|
+
? [candidate.atCommand]
|
|
118
|
+
: [candidate.atCommand, candidate.applyCommand]),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (!staticResult.ok)
|
|
122
|
+
return staticResult;
|
|
123
|
+
const readiness = await request.probeReadiness();
|
|
124
|
+
if (readiness.identityConfidence === 'low') {
|
|
125
|
+
return { ok: false, reason: 'low-confidence identity — refusing to transition' };
|
|
126
|
+
}
|
|
127
|
+
const verdict = await interlock.canDisrupt({ stableKey: request.stableKey });
|
|
128
|
+
if (!verdict.allow) {
|
|
129
|
+
return { ok: false, reason: `interlock held: ${verdict.reason}` };
|
|
130
|
+
}
|
|
131
|
+
return staticResult;
|
|
132
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { IdentityConfidence, LogicalSlotId } from '../../domain/index.js';
|
|
2
|
+
/** The composite baseline key. A change in ANY field is a remap (zero-delta rebase). */
|
|
3
|
+
export interface BaselineKey {
|
|
4
|
+
readonly logicalSlotId: string;
|
|
5
|
+
readonly mappingGeneration: number;
|
|
6
|
+
readonly ifname: string;
|
|
7
|
+
readonly bootId: string;
|
|
8
|
+
}
|
|
9
|
+
/** Per-slot accounting state. `key`/`lastObserved` are unset before the first sample. */
|
|
10
|
+
export interface SlotAccount {
|
|
11
|
+
/** Bytes attributed to this slot within the current cycle. */
|
|
12
|
+
readonly cycleBytes: number;
|
|
13
|
+
/** UTC start of the cycle `cycleBytes` is accruing into. */
|
|
14
|
+
readonly cycleStartMs: number;
|
|
15
|
+
/** True when the last observation was low-confidence and sampling is paused. */
|
|
16
|
+
readonly paused: boolean;
|
|
17
|
+
/** The key the baseline was captured under (undefined = no baseline yet). */
|
|
18
|
+
readonly key?: BaselineKey;
|
|
19
|
+
/** The cumulative counter value at the last accepted sample. */
|
|
20
|
+
readonly lastObserved?: number;
|
|
21
|
+
}
|
|
22
|
+
/** One observation fed to the reducer for a single slot in one sampling pass. */
|
|
23
|
+
export interface SampleInput {
|
|
24
|
+
readonly key: BaselineKey;
|
|
25
|
+
/** The current cumulative rx+tx counter for the slot's interface. */
|
|
26
|
+
readonly current: number;
|
|
27
|
+
readonly confidence: IdentityConfidence;
|
|
28
|
+
/** The UTC start of the slot's active cycle (from `cycleStart`). */
|
|
29
|
+
readonly cycleStartMs: number;
|
|
30
|
+
}
|
|
31
|
+
/** A fresh account for a slot first seen in `cycleStartMs`. */
|
|
32
|
+
export declare function initialAccount(cycleStartMs: number): SlotAccount;
|
|
33
|
+
/**
|
|
34
|
+
* Fold one observation into a slot's account, returning the NEXT account. Pure —
|
|
35
|
+
* no clock, no I/O; the caller supplies `current`, `confidence` and `cycleStartMs`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function applySample(prior: SlotAccount | undefined, input: SampleInput): SlotAccount;
|
|
38
|
+
/** Convenience constructor for a `BaselineKey` from its parts. */
|
|
39
|
+
export declare function baselineKey(logicalSlotId: LogicalSlotId, mappingGeneration: number, ifname: string, bootId: string): BaselineKey;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// The pure usage-accounting reducer — the heart of the sampler.
|
|
2
|
+
//
|
|
3
|
+
// For each logical slot we keep a per-cycle byte total plus a BASELINE: the last
|
|
4
|
+
// observed cumulative counter value under a specific key. The key is the composite
|
|
5
|
+
// `{logicalSlotId, mappingGeneration, ifname, bootId}`. The rules, all encoded here
|
|
6
|
+
// with zero I/O so they can be exhaustively unit-tested:
|
|
7
|
+
//
|
|
8
|
+
// - REMAP (any key field changes — e.g. the ifname-to-slot mapping bumped
|
|
9
|
+
// `mappingGeneration`, or a reboot changed `bootId`): rebaseline with a
|
|
10
|
+
// ZERO-DELTA sample. We start counting fresh from the current cumulative value
|
|
11
|
+
// and attribute nothing for that first sample — never a spurious jump.
|
|
12
|
+
// - SAME-KEY DECREASE (the cumulative counter went DOWN — interface re-created /
|
|
13
|
+
// counter reset without a reported remap): CLAMP the negative delta to zero and
|
|
14
|
+
// REBASE the baseline to the new lower value. Never report negative usage.
|
|
15
|
+
// - LOW CONFIDENCE (ambiguous identity, A3.2 ladder): sampling is PAUSED. We drop
|
|
16
|
+
// the baseline and attribute nothing; on resume the next sample is zero-delta so
|
|
17
|
+
// bytes moved during the ambiguous window are never mis-attributed.
|
|
18
|
+
// - CYCLE ROLLOVER (`now` crossed into a new billing cycle): the per-cycle total
|
|
19
|
+
// resets to zero. The baseline is kept — the kernel counter is continuous across
|
|
20
|
+
// a billing boundary; only OUR accounting window resets.
|
|
21
|
+
/** A fresh account for a slot first seen in `cycleStartMs`. */
|
|
22
|
+
export function initialAccount(cycleStartMs) {
|
|
23
|
+
return { cycleBytes: 0, cycleStartMs, paused: false };
|
|
24
|
+
}
|
|
25
|
+
function sameKey(a, b) {
|
|
26
|
+
return (a.logicalSlotId === b.logicalSlotId &&
|
|
27
|
+
a.mappingGeneration === b.mappingGeneration &&
|
|
28
|
+
a.ifname === b.ifname &&
|
|
29
|
+
a.bootId === b.bootId);
|
|
30
|
+
}
|
|
31
|
+
/** Apply a cycle rollover: if we crossed into a newer cycle, zero the per-cycle total. */
|
|
32
|
+
function rollCycle(account, cycleStartMs) {
|
|
33
|
+
if (cycleStartMs > account.cycleStartMs) {
|
|
34
|
+
return { ...account, cycleBytes: 0, cycleStartMs };
|
|
35
|
+
}
|
|
36
|
+
return account;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Fold one observation into a slot's account, returning the NEXT account. Pure —
|
|
40
|
+
* no clock, no I/O; the caller supplies `current`, `confidence` and `cycleStartMs`.
|
|
41
|
+
*/
|
|
42
|
+
export function applySample(prior, input) {
|
|
43
|
+
const base = rollCycle(prior ?? initialAccount(input.cycleStartMs), input.cycleStartMs);
|
|
44
|
+
// Ambiguous identity → pause: attribute nothing and drop the baseline so the
|
|
45
|
+
// next confident sample re-baselines zero-delta (no back-attribution).
|
|
46
|
+
if (input.confidence === 'low') {
|
|
47
|
+
return { cycleBytes: base.cycleBytes, cycleStartMs: base.cycleStartMs, paused: true };
|
|
48
|
+
}
|
|
49
|
+
// Remap, first-ever sample, or resuming from a pause → zero-delta rebaseline.
|
|
50
|
+
if (base.paused || base.key === undefined || base.lastObserved === undefined) {
|
|
51
|
+
return { ...base, paused: false, key: input.key, lastObserved: input.current };
|
|
52
|
+
}
|
|
53
|
+
if (!sameKey(base.key, input.key)) {
|
|
54
|
+
return { ...base, paused: false, key: input.key, lastObserved: input.current };
|
|
55
|
+
}
|
|
56
|
+
// Same key: a decrease is a counter reset → clamp the negative delta and rebase.
|
|
57
|
+
if (input.current < base.lastObserved) {
|
|
58
|
+
return { ...base, paused: false, key: input.key, lastObserved: input.current };
|
|
59
|
+
}
|
|
60
|
+
// Normal case: attribute the non-negative delta and advance the baseline.
|
|
61
|
+
const delta = input.current - base.lastObserved;
|
|
62
|
+
return {
|
|
63
|
+
...base,
|
|
64
|
+
paused: false,
|
|
65
|
+
key: input.key,
|
|
66
|
+
lastObserved: input.current,
|
|
67
|
+
cycleBytes: base.cycleBytes + delta,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** Convenience constructor for a `BaselineKey` from its parts. */
|
|
71
|
+
export function baselineKey(logicalSlotId, mappingGeneration, ifname, bootId) {
|
|
72
|
+
return { logicalSlotId, mappingGeneration, ifname, bootId };
|
|
73
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EpochMillis } from '../../domain/index.js';
|
|
2
|
+
/** Days in a given UTC month. `month` is 0-based (0 = January). Handles leap Feb. */
|
|
3
|
+
export declare function daysInMonth(year: number, month: number): number;
|
|
4
|
+
/** Clamp a requested cycle day to the last valid day of the given month. */
|
|
5
|
+
export declare function clampCycleDay(cycleDay: number, year: number, month: number): number;
|
|
6
|
+
/**
|
|
7
|
+
* The UTC start (`EpochMillis`) of the cycle that `now` falls in for a given
|
|
8
|
+
* `cycleDay`. If `now` is before this month's (clamped) boundary the active cycle
|
|
9
|
+
* began on the previous month's boundary; otherwise it began on this month's.
|
|
10
|
+
*/
|
|
11
|
+
export declare function cycleStart(now: EpochMillis, cycleDay: number): EpochMillis;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// UTC billing-cycle computation with month-length clamping.
|
|
2
|
+
//
|
|
3
|
+
// A usage cycle resets on a configured day of the month (`cycleDay`, 1–31) in UTC.
|
|
4
|
+
// Months are not all the same length, so a `cycleDay` past the end of a short month
|
|
5
|
+
// must clamp to that month's LAST day rather than rolling into the next month or
|
|
6
|
+
// throwing: `cycleDay: 31` resolves to Feb 28 (or Feb 29 in a leap year), Apr 30,
|
|
7
|
+
// etc. All arithmetic is UTC — the device clock's local zone never shifts a cycle.
|
|
8
|
+
/** Days in a given UTC month. `month` is 0-based (0 = January). Handles leap Feb. */
|
|
9
|
+
export function daysInMonth(year, month) {
|
|
10
|
+
// Day 0 of the next month is the last day of this month; UTC avoids DST drift.
|
|
11
|
+
return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
|
|
12
|
+
}
|
|
13
|
+
/** Clamp a requested cycle day to the last valid day of the given month. */
|
|
14
|
+
export function clampCycleDay(cycleDay, year, month) {
|
|
15
|
+
const floor = Math.max(1, Math.trunc(cycleDay));
|
|
16
|
+
return Math.min(floor, daysInMonth(year, month));
|
|
17
|
+
}
|
|
18
|
+
/** The UTC epoch-ms of the cycle boundary in a specific month (clamped). */
|
|
19
|
+
function boundaryFor(cycleDay, year, month) {
|
|
20
|
+
return Date.UTC(year, month, clampCycleDay(cycleDay, year, month));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The UTC start (`EpochMillis`) of the cycle that `now` falls in for a given
|
|
24
|
+
* `cycleDay`. If `now` is before this month's (clamped) boundary the active cycle
|
|
25
|
+
* began on the previous month's boundary; otherwise it began on this month's.
|
|
26
|
+
*/
|
|
27
|
+
export function cycleStart(now, cycleDay) {
|
|
28
|
+
const at = new Date(now);
|
|
29
|
+
const year = at.getUTCFullYear();
|
|
30
|
+
const month = at.getUTCMonth();
|
|
31
|
+
const thisBoundary = boundaryFor(cycleDay, year, month);
|
|
32
|
+
if (now >= thisBoundary) {
|
|
33
|
+
return thisBoundary;
|
|
34
|
+
}
|
|
35
|
+
// Roll back one month (December wraps to the prior year), re-clamping there.
|
|
36
|
+
const prevMonth = month === 0 ? 11 : month - 1;
|
|
37
|
+
const prevYear = month === 0 ? year - 1 : year;
|
|
38
|
+
return boundaryFor(cycleDay, prevYear, prevMonth);
|
|
39
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the current kernel boot id (a UUID string). The path is injectable for tests;
|
|
3
|
+
* the default is the live `/proc` file. A read failure yields an empty string so the
|
|
4
|
+
* sampler still starts (baselines then simply never match a persisted session).
|
|
5
|
+
*/
|
|
6
|
+
export declare function readBootId(path?: string): Promise<string>;
|
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
// `/proc/sys/kernel/random/boot_id` is a per-boot random UUID string. It changes on
|
|
4
4
|
// every reboot, which is exactly when interface byte counters reset to zero, so the
|
|
5
5
|
// sampler keys its baselines on it and re-baselines when it changes.
|
|
6
|
-
|
|
7
6
|
/**
|
|
8
7
|
* Read the current kernel boot id (a UUID string). The path is injectable for tests;
|
|
9
8
|
* the default is the live `/proc` file. A read failure yields an empty string so the
|
|
10
9
|
* sampler still starts (baselines then simply never match a persisted session).
|
|
11
10
|
*/
|
|
12
|
-
export async function readBootId(path = '/proc/sys/kernel/random/boot_id')
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
export async function readBootId(path = '/proc/sys/kernel/random/boot_id') {
|
|
12
|
+
try {
|
|
13
|
+
return (await Bun.file(path).text()).trim();
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return '';
|
|
17
|
+
}
|
|
18
18
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { applySample, type BaselineKey, baselineKey, initialAccount, type SampleInput, type SlotAccount, } from './accounting.js';
|
|
2
|
+
export { clampCycleDay, cycleStart, daysInMonth } from './billing-cycle.js';
|
|
3
|
+
export { readBootId } from './boot-id.js';
|
|
4
|
+
export { createUsagePolicyFileStore, isValidCycleDay, isValidThresholdBytes, type PersistedUsagePolicy, type PersistedUsagePolicySlot, selectUsagePolicy, USAGE_POLICY_SCHEMA_VERSION, type UsagePolicyFileStoreOptions, type UsagePolicyLogEvent, type UsagePolicyLogger, type UsagePolicyStore, } from './policy-store.js';
|
|
5
|
+
export { getUsagePolicy, type SetUsagePolicyDeps, type SetUsagePolicyRejection, type SetUsagePolicyRequest, type SetUsagePolicyResult, setUsagePolicy, type UsagePolicyApplication, type UsagePolicyTarget, } from './policy-write.js';
|
|
6
|
+
export { type CounterSource, parseProcNetDev, procNetDevCounterSource, } from './proc-net-dev.js';
|
|
7
|
+
export { createUsageSampler, type SlotUsageSnapshot, type UsageObservation, UsageSampler, type UsageSamplerOptions, type UsageSnapshot, } from './sampler.js';
|
|
8
|
+
export { createUsageFileStore, type PersistedSlot, type PersistedUsage, USAGE_SCHEMA_VERSION, type UsageFileStoreOptions, type UsageLogEvent, type UsageLogger, type UsageStore, } from './store.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Data-usage sampler — internal all-interface sampler over `/proc/net/dev`
|
|
2
|
+
// CUMULATIVE counters (A4.3). Public surface consumed by the A6.1 bench CLI `usage`
|
|
3
|
+
// command (via `UsageSnapshot`) and the composition root that wires the sampler.
|
|
4
|
+
export { applySample, baselineKey, initialAccount, } from './accounting.js';
|
|
5
|
+
export { clampCycleDay, cycleStart, daysInMonth } from './billing-cycle.js';
|
|
6
|
+
export { readBootId } from './boot-id.js';
|
|
7
|
+
export { createUsagePolicyFileStore, isValidCycleDay, isValidThresholdBytes, selectUsagePolicy, USAGE_POLICY_SCHEMA_VERSION, } from './policy-store.js';
|
|
8
|
+
export { getUsagePolicy, setUsagePolicy, } from './policy-write.js';
|
|
9
|
+
export { parseProcNetDev, procNetDevCounterSource, } from './proc-net-dev.js';
|
|
10
|
+
export { createUsageSampler, UsageSampler, } from './sampler.js';
|
|
11
|
+
export { createUsageFileStore, USAGE_SCHEMA_VERSION, } from './store.js';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { DesiredUsage } from '../../domain/index.js';
|
|
2
|
+
/** The current on-disk schema version. Bump when the persisted shape changes. */
|
|
3
|
+
export declare const USAGE_POLICY_SCHEMA_VERSION = 1;
|
|
4
|
+
/** One slot's persisted usage policy. Both fields are absent when unset. */
|
|
5
|
+
export interface PersistedUsagePolicySlot {
|
|
6
|
+
readonly logicalSlotId: string;
|
|
7
|
+
/** Day of month (1–31) the cycle resets; UTC, month-length clamped (A4.3). */
|
|
8
|
+
readonly cycleDay?: number;
|
|
9
|
+
/** Advisory threshold in bytes; crossing it raises an advisory, never gates. */
|
|
10
|
+
readonly thresholdBytes?: number;
|
|
11
|
+
}
|
|
12
|
+
/** The full persisted policy document. */
|
|
13
|
+
export interface PersistedUsagePolicy {
|
|
14
|
+
readonly schemaVersion: typeof USAGE_POLICY_SCHEMA_VERSION;
|
|
15
|
+
readonly savedAtMs: number;
|
|
16
|
+
readonly slots: readonly PersistedUsagePolicySlot[];
|
|
17
|
+
}
|
|
18
|
+
/** A metadata-only log event. Corruption NEVER carries the raw file content. */
|
|
19
|
+
export type UsagePolicyLogEvent = {
|
|
20
|
+
readonly kind: 'corrupt-policy';
|
|
21
|
+
readonly bytes: number;
|
|
22
|
+
readonly reason: string;
|
|
23
|
+
};
|
|
24
|
+
/** Sink for policy-store log events. Defaults to a metadata-only `console.warn`. */
|
|
25
|
+
export type UsagePolicyLogger = (event: UsagePolicyLogEvent) => void;
|
|
26
|
+
/** The persistence seam `setUsagePolicy` drives. */
|
|
27
|
+
export interface UsagePolicyStore {
|
|
28
|
+
/** Load persisted policy; recreate a fresh 0600 file if absent or corrupt. */
|
|
29
|
+
load(nowMs: number): Promise<PersistedUsagePolicy>;
|
|
30
|
+
/** Atomically write policy with mode 0600 (temp → chmod → rename). */
|
|
31
|
+
save(state: PersistedUsagePolicy): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
export interface UsagePolicyFileStoreOptions {
|
|
34
|
+
readonly path: string;
|
|
35
|
+
readonly logger?: UsagePolicyLogger;
|
|
36
|
+
}
|
|
37
|
+
/** True for a value that is a legal cycle day (integer 1–31). */
|
|
38
|
+
export declare function isValidCycleDay(value: unknown): value is number;
|
|
39
|
+
/** True for a value that is a legal advisory threshold (non-negative integer). */
|
|
40
|
+
export declare function isValidThresholdBytes(value: unknown): value is number;
|
|
41
|
+
/**
|
|
42
|
+
* Read one slot's policy out of a loaded document.
|
|
43
|
+
*
|
|
44
|
+
* This is the read half the composition root uses to build each slot's
|
|
45
|
+
* `UsageObservation.usage`, so the persisted file — not an in-memory guess — is
|
|
46
|
+
* what the sampler accounts against. An unknown slot answers `{}`, i.e. "no
|
|
47
|
+
* policy set", which is exactly what `defaultCellularPolicy` starts from.
|
|
48
|
+
*/
|
|
49
|
+
export declare function selectUsagePolicy(state: PersistedUsagePolicy, logicalSlotId: string): DesiredUsage;
|
|
50
|
+
export declare function createUsagePolicyFileStore(options: UsagePolicyFileStoreOptions): UsagePolicyStore;
|