@ceralive/modem-control 0.2.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (464) hide show
  1. package/README.md +317 -0
  2. package/dist/backend/at-lease.d.ts +57 -0
  3. package/dist/backend/at-lease.js +109 -0
  4. package/dist/backend/cell-info.d.ts +46 -0
  5. package/dist/backend/cell-info.js +124 -0
  6. package/dist/backend/constants.d.ts +24 -0
  7. package/{src/backend/constants.ts → dist/backend/constants.js} +4 -9
  8. package/dist/backend/device-classifier.d.ts +46 -0
  9. package/dist/backend/device-classifier.js +258 -0
  10. package/dist/backend/enrichment.d.ts +28 -0
  11. package/dist/backend/enrichment.js +59 -0
  12. package/dist/backend/features.d.ts +61 -0
  13. package/dist/backend/features.js +109 -0
  14. package/dist/backend/identity-ladder.d.ts +57 -0
  15. package/dist/backend/identity-ladder.js +158 -0
  16. package/dist/backend/identity-registry.d.ts +51 -0
  17. package/dist/backend/identity-registry.js +101 -0
  18. package/dist/backend/index.d.ts +30 -0
  19. package/dist/backend/index.js +35 -0
  20. package/dist/backend/lifecycle-interlock.d.ts +25 -0
  21. package/dist/backend/lifecycle-interlock.js +18 -0
  22. package/dist/backend/managed-objects.d.ts +39 -0
  23. package/dist/backend/managed-objects.js +82 -0
  24. package/dist/backend/mapping.d.ts +11 -0
  25. package/dist/backend/mapping.js +148 -0
  26. package/dist/backend/mm-backend.d.ts +44 -0
  27. package/dist/backend/mm-backend.js +154 -0
  28. package/dist/backend/mm-location.d.ts +21 -0
  29. package/dist/backend/mm-location.js +237 -0
  30. package/dist/backend/mm-mutations.d.ts +42 -0
  31. package/dist/backend/mm-mutations.js +259 -0
  32. package/dist/backend/modem-actor.d.ts +41 -0
  33. package/dist/backend/modem-actor.js +78 -0
  34. package/dist/backend/nm-auto-apn.d.ts +54 -0
  35. package/dist/backend/nm-auto-apn.js +124 -0
  36. package/dist/backend/nm-gsm-fields.d.ts +23 -0
  37. package/dist/backend/nm-gsm-fields.js +107 -0
  38. package/dist/backend/nmcli-nm-port.d.ts +28 -0
  39. package/dist/backend/nmcli-nm-port.js +173 -0
  40. package/dist/backend/nmcli-runner.d.ts +24 -0
  41. package/dist/backend/nmcli-runner.js +35 -0
  42. package/dist/backend/observer.d.ts +37 -0
  43. package/dist/backend/observer.js +219 -0
  44. package/dist/backend/power-contract.d.ts +49 -0
  45. package/dist/backend/power-contract.js +34 -0
  46. package/dist/backend/recovery-attribution.d.ts +32 -0
  47. package/dist/backend/recovery-attribution.js +57 -0
  48. package/dist/backend/recovery-budget.d.ts +45 -0
  49. package/dist/backend/recovery-budget.js +44 -0
  50. package/dist/backend/recovery-ladder.d.ts +94 -0
  51. package/dist/backend/recovery-ladder.js +116 -0
  52. package/dist/backend/router-ethernet.d.ts +19 -0
  53. package/dist/backend/router-ethernet.js +66 -0
  54. package/dist/backend/row-store.d.ts +13 -0
  55. package/dist/backend/row-store.js +82 -0
  56. package/dist/backend/signal-setup.d.ts +30 -0
  57. package/dist/backend/signal-setup.js +92 -0
  58. package/dist/backend/sim-unlock.d.ts +13 -0
  59. package/dist/backend/sim-unlock.js +153 -0
  60. package/dist/backend/transition-preconditions.d.ts +76 -0
  61. package/dist/backend/transition-preconditions.js +64 -0
  62. package/dist/backend/usage/accounting.d.ts +39 -0
  63. package/dist/backend/usage/accounting.js +73 -0
  64. package/dist/backend/usage/billing-cycle.d.ts +11 -0
  65. package/dist/backend/usage/billing-cycle.js +39 -0
  66. package/dist/backend/usage/boot-id.d.ts +6 -0
  67. package/{src/backend/usage/boot-id.ts → dist/backend/usage/boot-id.js} +7 -7
  68. package/dist/backend/usage/index.d.ts +8 -0
  69. package/dist/backend/usage/index.js +11 -0
  70. package/dist/backend/usage/policy-store.d.ts +50 -0
  71. package/dist/backend/usage/policy-store.js +161 -0
  72. package/dist/backend/usage/policy-write.d.ts +65 -0
  73. package/dist/backend/usage/policy-write.js +112 -0
  74. package/dist/backend/usage/proc-net-dev.d.ts +18 -0
  75. package/{src/backend/usage/proc-net-dev.ts → dist/backend/usage/proc-net-dev.js} +39 -47
  76. package/dist/backend/usage/sampler.d.ts +75 -0
  77. package/dist/backend/usage/sampler.js +211 -0
  78. package/dist/backend/usage/store.d.ts +38 -0
  79. package/dist/backend/usage/store.js +126 -0
  80. package/dist/backend/usb-device-snapshot.d.ts +31 -0
  81. package/dist/backend/usb-device-snapshot.js +1 -0
  82. package/dist/backend/usb-enumerator.d.ts +21 -0
  83. package/dist/backend/usb-enumerator.js +153 -0
  84. package/dist/backend/usb-mode-transition.d.ts +29 -0
  85. package/dist/backend/usb-mode-transition.js +196 -0
  86. package/dist/band/band-names.d.ts +42 -0
  87. package/dist/band/band-names.js +150 -0
  88. package/dist/band/certification.d.ts +84 -0
  89. package/dist/band/certification.js +127 -0
  90. package/dist/band/certified-bands.json +4 -0
  91. package/dist/band/index.d.ts +2 -0
  92. package/dist/band/index.js +8 -0
  93. package/dist/capability/detect.d.ts +52 -0
  94. package/dist/capability/detect.js +86 -0
  95. package/dist/capability/five-g-preference.d.ts +104 -0
  96. package/dist/capability/five-g-preference.js +171 -0
  97. package/dist/capability/index.d.ts +3 -0
  98. package/dist/capability/index.js +10 -0
  99. package/dist/capability/support-claim.d.ts +39 -0
  100. package/dist/capability/support-claim.js +81 -0
  101. package/dist/domain/brand.d.ts +10 -0
  102. package/dist/domain/brand.js +21 -0
  103. package/dist/domain/errors.d.ts +32 -0
  104. package/dist/domain/errors.js +48 -0
  105. package/dist/domain/generation.d.ts +8 -0
  106. package/dist/domain/generation.js +12 -0
  107. package/dist/domain/guards.d.ts +6 -0
  108. package/dist/domain/guards.js +127 -0
  109. package/dist/domain/identity.d.ts +109 -0
  110. package/dist/domain/identity.js +86 -0
  111. package/dist/domain/index.d.ts +14 -0
  112. package/dist/domain/index.js +18 -0
  113. package/dist/domain/mm-enums.d.ts +12 -0
  114. package/dist/domain/mm-enums.js +139 -0
  115. package/dist/domain/modem-presentation.d.ts +10 -0
  116. package/dist/domain/modem-presentation.js +39 -0
  117. package/dist/domain/observation.d.ts +39 -0
  118. package/dist/domain/observation.js +4 -0
  119. package/dist/domain/operation.d.ts +118 -0
  120. package/dist/domain/operation.js +85 -0
  121. package/dist/domain/physical-identity.d.ts +43 -0
  122. package/dist/domain/physical-identity.js +113 -0
  123. package/{src/domain/policy.ts → dist/domain/policy.d.ts} +34 -69
  124. package/dist/domain/policy.js +38 -0
  125. package/dist/domain/shadow-divergence.d.ts +27 -0
  126. package/dist/domain/shadow-divergence.js +70 -0
  127. package/dist/domain/snapshot.d.ts +53 -0
  128. package/dist/domain/snapshot.js +75 -0
  129. package/{src/domain/state.ts → dist/domain/state.d.ts} +23 -122
  130. package/dist/domain/state.js +38 -0
  131. package/dist/fcc/coverage.d.ts +63 -0
  132. package/dist/fcc/coverage.js +102 -0
  133. package/dist/fcc/index.d.ts +3 -0
  134. package/dist/fcc/index.js +12 -0
  135. package/dist/fcc/policy-store.d.ts +40 -0
  136. package/dist/fcc/policy-store.js +146 -0
  137. package/dist/fcc/policy-write.d.ts +32 -0
  138. package/dist/fcc/policy-write.js +37 -0
  139. package/dist/hardware/hilink-protocol.d.ts +39 -0
  140. package/dist/hardware/hilink-protocol.js +58 -0
  141. package/dist/hardware/index.d.ts +3 -0
  142. package/dist/hardware/index.js +15 -0
  143. package/dist/hardware/router-parsers.d.ts +89 -0
  144. package/dist/hardware/router-parsers.js +234 -0
  145. package/dist/index.d.ts +19 -0
  146. package/dist/index.js +26 -0
  147. package/dist/journal/codec.d.ts +44 -0
  148. package/dist/journal/codec.js +198 -0
  149. package/dist/journal/engine.d.ts +28 -0
  150. package/dist/journal/engine.js +68 -0
  151. package/dist/journal/entry.d.ts +74 -0
  152. package/dist/journal/entry.js +56 -0
  153. package/dist/journal/index.d.ts +6 -0
  154. package/dist/journal/index.js +6 -0
  155. package/dist/journal/legacy-ceraui.d.ts +73 -0
  156. package/dist/journal/legacy-ceraui.js +227 -0
  157. package/dist/journal/recovery.d.ts +58 -0
  158. package/dist/journal/recovery.js +117 -0
  159. package/dist/journal/store.d.ts +55 -0
  160. package/dist/journal/store.js +150 -0
  161. package/dist/location/fix-state.d.ts +53 -0
  162. package/dist/location/fix-state.js +75 -0
  163. package/dist/location/index.d.ts +2 -0
  164. package/dist/location/index.js +8 -0
  165. package/dist/location/nmea.d.ts +10 -0
  166. package/dist/location/nmea.js +89 -0
  167. package/dist/observations/envelope.d.ts +63 -0
  168. package/dist/observations/envelope.js +77 -0
  169. package/dist/observations/freshness.d.ts +28 -0
  170. package/dist/observations/freshness.js +76 -0
  171. package/dist/observations/index.d.ts +13 -0
  172. package/dist/observations/index.js +24 -0
  173. package/dist/observations/metric.d.ts +47 -0
  174. package/dist/observations/metric.js +73 -0
  175. package/dist/observations/model.d.ts +80 -0
  176. package/dist/observations/model.js +11 -0
  177. package/dist/observations/provenance.d.ts +94 -0
  178. package/dist/observations/provenance.js +67 -0
  179. package/dist/observations/raw.d.ts +42 -0
  180. package/dist/observations/raw.js +146 -0
  181. package/dist/observations/reading.d.ts +51 -0
  182. package/dist/observations/reading.js +65 -0
  183. package/dist/observations/sources/hilink.d.ts +10 -0
  184. package/dist/observations/sources/hilink.js +84 -0
  185. package/dist/observations/sources/modemmanager.d.ts +18 -0
  186. package/dist/observations/sources/modemmanager.js +239 -0
  187. package/dist/observations/sources/router-shared.d.ts +29 -0
  188. package/dist/observations/sources/router-shared.js +68 -0
  189. package/dist/observations/sources/ufi.d.ts +10 -0
  190. package/dist/observations/sources/ufi.js +111 -0
  191. package/dist/observations/sources/zte.d.ts +7 -0
  192. package/dist/observations/sources/zte.js +71 -0
  193. package/dist/observations/state-separation.d.ts +64 -0
  194. package/dist/observations/state-separation.js +52 -0
  195. package/dist/operations/contracts.d.ts +72 -0
  196. package/dist/operations/contracts.js +1 -0
  197. package/dist/operations/index.d.ts +1 -0
  198. package/dist/operations/index.js +1 -0
  199. package/dist/operations/operation-engine.d.ts +16 -0
  200. package/dist/operations/operation-engine.js +194 -0
  201. package/dist/ports/index.d.ts +12 -0
  202. package/{src/ports/index.ts → dist/ports/index.js} +12 -8
  203. package/dist/ports/location.d.ts +87 -0
  204. package/dist/ports/location.js +36 -0
  205. package/dist/ports/modem-manager.d.ts +89 -0
  206. package/dist/ports/modem-manager.js +9 -0
  207. package/dist/ports/mutation-admission.d.ts +27 -0
  208. package/dist/ports/mutation-admission.js +9 -0
  209. package/dist/ports/network-manager.d.ts +68 -0
  210. package/dist/ports/network-manager.js +13 -0
  211. package/{src/ports/observation.ts → dist/ports/observation.d.ts} +16 -27
  212. package/dist/ports/observation.js +7 -0
  213. package/dist/ports/ops.d.ts +44 -0
  214. package/dist/ports/ops.js +16 -0
  215. package/{src/ports/receipts.ts → dist/ports/receipts.d.ts} +5 -27
  216. package/dist/ports/receipts.js +9 -0
  217. package/dist/ports/reconcile.d.ts +33 -0
  218. package/dist/ports/reconcile.js +200 -0
  219. package/dist/ports/resource-ownership.d.ts +29 -0
  220. package/dist/ports/resource-ownership.js +1 -0
  221. package/dist/ports/router.d.ts +19 -0
  222. package/dist/ports/router.js +7 -0
  223. package/dist/ports/sms.d.ts +64 -0
  224. package/dist/ports/sms.js +24 -0
  225. package/dist/ports/uhubctl.d.ts +6 -0
  226. package/dist/ports/uhubctl.js +1 -0
  227. package/dist/providers/contracts.d.ts +124 -0
  228. package/dist/providers/contracts.js +10 -0
  229. package/dist/providers/huawei-hilink/index.d.ts +2 -0
  230. package/dist/providers/huawei-hilink/index.js +2 -0
  231. package/dist/providers/huawei-hilink/operations.d.ts +20 -0
  232. package/dist/providers/huawei-hilink/operations.js +56 -0
  233. package/dist/providers/huawei-hilink/provider.d.ts +52 -0
  234. package/dist/providers/huawei-hilink/provider.js +76 -0
  235. package/dist/providers/huawei-hilink/runtime.d.ts +22 -0
  236. package/dist/providers/huawei-hilink/runtime.js +171 -0
  237. package/dist/providers/huawei-hilink/session.d.ts +28 -0
  238. package/dist/providers/huawei-hilink/session.js +120 -0
  239. package/dist/providers/huawei-hilink/transport.d.ts +19 -0
  240. package/dist/providers/huawei-hilink/transport.js +1 -0
  241. package/dist/providers/index.d.ts +8 -0
  242. package/dist/providers/index.js +8 -0
  243. package/dist/providers/matcher.d.ts +3 -0
  244. package/dist/providers/matcher.js +205 -0
  245. package/dist/providers/modem-manager/errors.d.ts +7 -0
  246. package/dist/providers/modem-manager/errors.js +37 -0
  247. package/dist/providers/modem-manager/generic-operations.d.ts +8 -0
  248. package/dist/providers/modem-manager/generic-operations.js +207 -0
  249. package/dist/providers/modem-manager/index.d.ts +3 -0
  250. package/dist/providers/modem-manager/index.js +3 -0
  251. package/dist/providers/modem-manager/module-operations.d.ts +21 -0
  252. package/dist/providers/modem-manager/module-operations.js +118 -0
  253. package/dist/providers/modem-manager/provider.d.ts +39 -0
  254. package/dist/providers/modem-manager/provider.js +152 -0
  255. package/dist/providers/modem-manager/snapshot.d.ts +5 -0
  256. package/dist/providers/modem-manager/snapshot.js +204 -0
  257. package/dist/providers/modem-manager/types.d.ts +135 -0
  258. package/dist/providers/modem-manager/types.js +1 -0
  259. package/dist/providers/network-manager/adapter.d.ts +71 -0
  260. package/dist/providers/network-manager/adapter.js +348 -0
  261. package/dist/providers/network-manager/index.d.ts +2 -0
  262. package/dist/providers/network-manager/index.js +2 -0
  263. package/dist/providers/network-manager/types.d.ts +163 -0
  264. package/dist/providers/network-manager/types.js +77 -0
  265. package/dist/providers/registry.d.ts +13 -0
  266. package/dist/providers/registry.js +33 -0
  267. package/dist/providers/ufi-himi/index.d.ts +6 -0
  268. package/dist/providers/ufi-himi/index.js +6 -0
  269. package/dist/providers/ufi-himi/operations.d.ts +41 -0
  270. package/dist/providers/ufi-himi/operations.js +66 -0
  271. package/dist/providers/ufi-himi/prohibitions.d.ts +62 -0
  272. package/dist/providers/ufi-himi/prohibitions.js +88 -0
  273. package/dist/providers/ufi-himi/provider.d.ts +41 -0
  274. package/dist/providers/ufi-himi/provider.js +204 -0
  275. package/dist/providers/ufi-himi/qualcomm-evidence.d.ts +32 -0
  276. package/dist/providers/ufi-himi/qualcomm-evidence.js +51 -0
  277. package/dist/providers/ufi-himi/session.d.ts +46 -0
  278. package/dist/providers/ufi-himi/session.js +92 -0
  279. package/dist/providers/ufi-himi/transport.d.ts +29 -0
  280. package/dist/providers/ufi-himi/transport.js +25 -0
  281. package/dist/providers/zte-goform/index.d.ts +2 -0
  282. package/dist/providers/zte-goform/index.js +2 -0
  283. package/dist/providers/zte-goform/provider.d.ts +51 -0
  284. package/dist/providers/zte-goform/provider.js +101 -0
  285. package/dist/providers/zte-goform/session.d.ts +17 -0
  286. package/dist/providers/zte-goform/session.js +133 -0
  287. package/dist/providers/zte-goform/transport.d.ts +16 -0
  288. package/dist/providers/zte-goform/transport.js +1 -0
  289. package/dist/radio/band-truth.d.ts +50 -0
  290. package/dist/radio/band-truth.js +92 -0
  291. package/dist/radio/index.d.ts +3 -0
  292. package/dist/radio/index.js +10 -0
  293. package/dist/radio/mode-combinations.d.ts +78 -0
  294. package/dist/radio/mode-combinations.js +198 -0
  295. package/dist/radio/mode-truth.d.ts +67 -0
  296. package/dist/radio/mode-truth.js +112 -0
  297. package/dist/redact.d.ts +15 -0
  298. package/dist/redact.js +189 -0
  299. package/dist/safety/composition-root.d.ts +28 -0
  300. package/dist/safety/composition-root.js +65 -0
  301. package/dist/safety/flock-resource-ownership.d.ts +11 -0
  302. package/dist/safety/flock-resource-ownership.js +151 -0
  303. package/dist/safety/index.d.ts +2 -0
  304. package/dist/safety/index.js +2 -0
  305. package/dist/sms/dbus-messaging.d.ts +17 -0
  306. package/dist/sms/dbus-messaging.js +185 -0
  307. package/dist/sms/inbox-store.d.ts +10 -0
  308. package/dist/sms/inbox-store.js +82 -0
  309. package/dist/sms/index.d.ts +4 -0
  310. package/dist/sms/index.js +10 -0
  311. package/dist/sms/mmcli-parse.d.ts +54 -0
  312. package/dist/sms/mmcli-parse.js +224 -0
  313. package/dist/sms/normalize.d.ts +42 -0
  314. package/dist/sms/normalize.js +95 -0
  315. package/dist/testing/domain-fakes.d.ts +58 -0
  316. package/dist/testing/domain-fakes.js +98 -0
  317. package/dist/testing/index.d.ts +2 -0
  318. package/dist/testing/index.js +17 -0
  319. package/dist/testing/provider-fakes.d.ts +45 -0
  320. package/dist/testing/provider-fakes.js +64 -0
  321. package/dist/transport/calls.d.ts +9 -0
  322. package/dist/transport/calls.js +88 -0
  323. package/dist/transport/codec.d.ts +3 -0
  324. package/dist/transport/codec.js +207 -0
  325. package/dist/transport/dbus-native.d.ts +57 -0
  326. package/dist/transport/dbus-native.js +17 -0
  327. package/dist/transport/errors.d.ts +21 -0
  328. package/{src/transport/errors.ts → dist/transport/errors.js} +34 -46
  329. package/dist/transport/index.d.ts +4 -0
  330. package/dist/transport/index.js +9 -0
  331. package/dist/transport/signals.d.ts +15 -0
  332. package/dist/transport/signals.js +123 -0
  333. package/dist/transport/signature.d.ts +7 -0
  334. package/dist/transport/signature.js +94 -0
  335. package/dist/transport/transport.d.ts +2 -0
  336. package/dist/transport/transport.js +202 -0
  337. package/dist/transport/types.d.ts +61 -0
  338. package/dist/transport/types.js +19 -0
  339. package/dist/usb-mode/catalog-schema.d.ts +139 -0
  340. package/dist/usb-mode/catalog-schema.js +97 -0
  341. package/dist/usb-mode/catalog.d.ts +21 -0
  342. package/{src/usb-mode/catalog.ts → dist/usb-mode/catalog.js} +10 -32
  343. package/dist/usb-mode/certified-catalog.json +67 -0
  344. package/dist/usb-mode/index.d.ts +5 -0
  345. package/dist/usb-mode/index.js +15 -0
  346. package/dist/usb-mode/ingestion.d.ts +111 -0
  347. package/dist/usb-mode/ingestion.js +187 -0
  348. package/dist/usb-mode/promotion-review.d.ts +21 -0
  349. package/dist/usb-mode/promotion-review.js +87 -0
  350. package/dist/usb-mode/usb-devices-parse.d.ts +36 -0
  351. package/dist/usb-mode/usb-devices-parse.js +157 -0
  352. package/dist/ussd/calls.d.ts +32 -0
  353. package/dist/ussd/calls.js +96 -0
  354. package/dist/ussd/index.d.ts +5 -0
  355. package/dist/ussd/index.js +12 -0
  356. package/dist/ussd/mm-ussd.d.ts +37 -0
  357. package/dist/ussd/mm-ussd.js +205 -0
  358. package/dist/ussd/refusal.d.ts +53 -0
  359. package/dist/ussd/refusal.js +154 -0
  360. package/dist/ussd/registration.d.ts +20 -0
  361. package/dist/ussd/registration.js +101 -0
  362. package/dist/ussd/session.d.ts +84 -0
  363. package/dist/ussd/session.js +163 -0
  364. package/package.json +38 -4
  365. package/src/backend/at-lease.test.ts +0 -106
  366. package/src/backend/at-lease.ts +0 -158
  367. package/src/backend/cell-info.test.ts +0 -154
  368. package/src/backend/cell-info.ts +0 -160
  369. package/src/backend/device-classifier.test.ts +0 -168
  370. package/src/backend/device-classifier.ts +0 -248
  371. package/src/backend/enrichment.ts +0 -96
  372. package/src/backend/features.test.ts +0 -162
  373. package/src/backend/features.ts +0 -179
  374. package/src/backend/identity-ladder.test.ts +0 -117
  375. package/src/backend/identity-ladder.ts +0 -221
  376. package/src/backend/identity-registry.test.ts +0 -89
  377. package/src/backend/identity-registry.ts +0 -151
  378. package/src/backend/index.ts +0 -221
  379. package/src/backend/lifecycle-interlock.ts +0 -38
  380. package/src/backend/managed-objects.ts +0 -108
  381. package/src/backend/mapping.ts +0 -160
  382. package/src/backend/mm-backend.ts +0 -191
  383. package/src/backend/mm-mutations.ts +0 -228
  384. package/src/backend/modem-actor.test.ts +0 -95
  385. package/src/backend/modem-actor.ts +0 -112
  386. package/src/backend/nm-auto-apn.ts +0 -161
  387. package/src/backend/nm-gsm-fields.ts +0 -122
  388. package/src/backend/nmcli-nm-port.ts +0 -228
  389. package/src/backend/nmcli-runner.ts +0 -52
  390. package/src/backend/observer.ts +0 -297
  391. package/src/backend/power-contract.test.ts +0 -40
  392. package/src/backend/power-contract.ts +0 -83
  393. package/src/backend/recovery-attribution.test.ts +0 -102
  394. package/src/backend/recovery-attribution.ts +0 -86
  395. package/src/backend/recovery-budget.test.ts +0 -64
  396. package/src/backend/recovery-budget.ts +0 -84
  397. package/src/backend/recovery-ladder.test.ts +0 -257
  398. package/src/backend/recovery-ladder.ts +0 -249
  399. package/src/backend/router-ethernet.test.ts +0 -71
  400. package/src/backend/router-ethernet.ts +0 -90
  401. package/src/backend/row-store.ts +0 -105
  402. package/src/backend/signal-setup.ts +0 -112
  403. package/src/backend/sim-unlock.ts +0 -193
  404. package/src/backend/transition-preconditions.ts +0 -149
  405. package/src/backend/usage/accounting.test.ts +0 -147
  406. package/src/backend/usage/accounting.ts +0 -123
  407. package/src/backend/usage/billing-cycle.test.ts +0 -62
  408. package/src/backend/usage/billing-cycle.ts +0 -45
  409. package/src/backend/usage/index.ts +0 -37
  410. package/src/backend/usage/proc-net-dev.test.ts +0 -56
  411. package/src/backend/usage/sampler.test.ts +0 -219
  412. package/src/backend/usage/sampler.ts +0 -228
  413. package/src/backend/usage/store.test.ts +0 -148
  414. package/src/backend/usage/store.ts +0 -177
  415. package/src/backend/usb-enumerator.test.ts +0 -87
  416. package/src/backend/usb-enumerator.ts +0 -181
  417. package/src/backend/usb-mode-transition.test.ts +0 -323
  418. package/src/backend/usb-mode-transition.ts +0 -253
  419. package/src/domain/brand.ts +0 -29
  420. package/src/domain/errors.ts +0 -77
  421. package/src/domain/guards.test.ts +0 -218
  422. package/src/domain/guards.ts +0 -144
  423. package/src/domain/identity.test.ts +0 -83
  424. package/src/domain/identity.ts +0 -165
  425. package/src/domain/index.ts +0 -12
  426. package/src/domain/snapshot.test.ts +0 -266
  427. package/src/domain/snapshot.ts +0 -120
  428. package/src/index.test.ts +0 -6
  429. package/src/index.ts +0 -15
  430. package/src/ports/README.md +0 -61
  431. package/src/ports/forbidden-surface.test.ts +0 -241
  432. package/src/ports/modem-manager.ts +0 -72
  433. package/src/ports/network-manager.ts +0 -87
  434. package/src/ports/ops.ts +0 -60
  435. package/src/ports/ops.type-test.ts +0 -39
  436. package/src/ports/receipts.test.ts +0 -153
  437. package/src/ports/reconcile.test.ts +0 -152
  438. package/src/ports/reconcile.ts +0 -338
  439. package/src/ports/router.ts +0 -29
  440. package/src/redact.test.ts +0 -82
  441. package/src/redact.ts +0 -73
  442. package/src/transport/README.md +0 -65
  443. package/src/transport/calls.ts +0 -113
  444. package/src/transport/characterization.test.ts +0 -260
  445. package/src/transport/codec.test.ts +0 -118
  446. package/src/transport/codec.ts +0 -240
  447. package/src/transport/conformance-python.test.ts +0 -152
  448. package/src/transport/conformance-same-lib.test.ts +0 -115
  449. package/src/transport/dbus-native-lib.d.ts +0 -19
  450. package/src/transport/dbus-native.ts +0 -85
  451. package/src/transport/index.ts +0 -30
  452. package/src/transport/no-library-leak.test.ts +0 -61
  453. package/src/transport/reliability.test.ts +0 -173
  454. package/src/transport/signals.ts +0 -150
  455. package/src/transport/signature.ts +0 -110
  456. package/src/transport/test-support/fake-service.ts +0 -168
  457. package/src/transport/test-support/independent-producer.py +0 -110
  458. package/src/transport/test-support/private-bus.ts +0 -66
  459. package/src/transport/transport.ts +0 -250
  460. package/src/transport/types.ts +0 -118
  461. package/src/usb-mode/catalog-schema.test.ts +0 -181
  462. package/src/usb-mode/catalog-schema.ts +0 -113
  463. package/src/usb-mode/certified-catalog.json +0 -67
  464. package/src/usb-mode/index.ts +0 -27
@@ -0,0 +1,148 @@
1
+ // Pure mapping: a decoded ModemManager object tree → a `CellularSnapshot`.
2
+ //
3
+ // A3.1 is lifecycle-only. This mapper is DELIBERATELY conservative: it captures
4
+ // identity, presence, source health, radio power, and MM state — the facts the
5
+ // observer needs to order and reconcile modems — but leaves the richer 3GPP
6
+ // registration + access-technology set as `unknown`/empty. Faithful registration and
7
+ // cell-info normalisation are A3.2 (identity ladder) and A3.3 (Signal.Setup / cell
8
+ // info); mapping them here would either be a guess or trip the domain guards. The
9
+ // snapshot this produces is always guard-valid by construction.
10
+ import { imeiEquipmentId, subscriberNumber as makeSubscriberNumber, subscriptionId as makeSubscriptionId, runtimePath, } from '../domain/index.js';
11
+ import { MODEM_IFACE, MODEM3GPP_IFACE, SIM_IFACE } from './constants.js';
12
+ import { findInterface, followObjectPath, numberProp, pathsWithInterface, stringArrayProp, stringProp, } from './managed-objects.js';
13
+ /** MMModemState (`Modem.State`, signed) → the domain `MmState`. */
14
+ function mapMmState(state) {
15
+ switch (state) {
16
+ case -1:
17
+ return 'failed';
18
+ case 1:
19
+ return 'initializing';
20
+ case 2:
21
+ return 'locked';
22
+ case 3:
23
+ return 'disabled';
24
+ case 4:
25
+ return 'disabling';
26
+ case 5:
27
+ return 'enabling';
28
+ case 6:
29
+ return 'enabled';
30
+ case 7:
31
+ return 'searching';
32
+ case 8:
33
+ return 'registered';
34
+ case 9:
35
+ return 'disconnecting';
36
+ case 10:
37
+ return 'connecting';
38
+ case 11:
39
+ return 'connected';
40
+ default:
41
+ return 'unknown';
42
+ }
43
+ }
44
+ /** MMModemPowerState (`Modem.PowerState`) → the domain `RadioPower`. */
45
+ function mapRadioPower(power) {
46
+ switch (power) {
47
+ case 1:
48
+ return 'off';
49
+ case 2:
50
+ return 'low';
51
+ case 3:
52
+ return 'on';
53
+ default:
54
+ return 'unknown';
55
+ }
56
+ }
57
+ /** Locked / failed modems keep the radio off; every other state we cannot see as
58
+ * off (the guards forbid an active MM state with the radio off), so an unknown
59
+ * power reading is treated as `on` when the modem is clearly on the air. */
60
+ function reconcilePower(power, state) {
61
+ const onTheAir = state === 'enabled' ||
62
+ state === 'searching' ||
63
+ state === 'registered' ||
64
+ state === 'connecting' ||
65
+ state === 'connected' ||
66
+ state === 'disconnecting';
67
+ if (onTheAir && (power === 'unknown' || power === 'off')) {
68
+ return 'on';
69
+ }
70
+ return power;
71
+ }
72
+ /** Read a modem's subscription id (ICCID) from its active SIM object, if any. */
73
+ function readSubscriptionId(tree, modemPath) {
74
+ const modem = findInterface(tree, modemPath, MODEM_IFACE);
75
+ const sim = followObjectPath(tree, modem, 'Sim', SIM_IFACE);
76
+ const iccid = stringProp(sim, 'SimIdentifier');
77
+ return iccid !== undefined && iccid.length > 0 ? makeSubscriptionId(iccid) : undefined;
78
+ }
79
+ /**
80
+ * The SIM's own number(s) from `Modem.OwnNumbers`, or `undefined` when the
81
+ * carrier published none. Most SIMs carry no MSISDN at all, so an absent or
82
+ * empty property is the ordinary case and must read as "not reported" rather
83
+ * than as an empty list an operator could mistake for a failed read.
84
+ */
85
+ function readOwnNumbers(modem) {
86
+ const raw = stringArrayProp(modem, 'OwnNumbers');
87
+ if (raw === undefined) {
88
+ return undefined;
89
+ }
90
+ const numbers = raw
91
+ .map((value) => value.trim())
92
+ .filter((value) => value.length > 0)
93
+ .map((value) => makeSubscriberNumber(value));
94
+ return numbers.length > 0 ? numbers : undefined;
95
+ }
96
+ /** Map ONE modem object to its dimensions. The modem must expose `Modem`. */
97
+ export function mapModem(tree, modemPath) {
98
+ const modem = findInterface(tree, modemPath, MODEM_IFACE);
99
+ const modem3gpp = findInterface(tree, modemPath, MODEM3GPP_IFACE);
100
+ const equipment = stringProp(modem, 'EquipmentIdentifier') ?? stringProp(modem3gpp, 'Imei') ?? '';
101
+ const mmState = mapMmState(numberProp(modem, 'State'));
102
+ const radioPower = reconcilePower(mapRadioPower(numberProp(modem, 'PowerState')), mmState);
103
+ const sub = readSubscriptionId(tree, modemPath);
104
+ const ownNumbers = readOwnNumbers(modem);
105
+ return {
106
+ identity: {
107
+ equipmentId: imeiEquipmentId(equipment),
108
+ runtimePath: runtimePath(modemPath),
109
+ ...(sub !== undefined ? { subscriptionId: sub } : {}),
110
+ ...(ownNumbers !== undefined ? { ownNumbers } : {}),
111
+ },
112
+ presence: 'present',
113
+ sourceHealth: 'live',
114
+ simSlots: [],
115
+ radioPower,
116
+ mmState,
117
+ // Conservative: 3GPP registration + RAT set are A3.3's job (see file header).
118
+ registration: { status: 'unknown', activeRats: new Set() },
119
+ nmActivation: 'unavailable',
120
+ dataInterface: { present: false },
121
+ reconcileStatus: 'pending',
122
+ recoveryState: { stage: 'idle', attempts: 0 },
123
+ };
124
+ }
125
+ /** Every modem path in the tree (objects exposing the `Modem` interface), in order. */
126
+ export function modemPaths(tree) {
127
+ return pathsWithInterface(tree, MODEM_IFACE);
128
+ }
129
+ /** A stable fingerprint of a mapped modem's meaningful dimensions (ignores revision
130
+ * and source health), used to suppress redundant revision bumps on identical reads. */
131
+ export function fingerprint(mapped) {
132
+ return JSON.stringify({
133
+ id: mapped.identity.equipmentId,
134
+ sub: mapped.identity.subscriptionId ?? null,
135
+ own: mapped.identity.ownNumbers ?? null,
136
+ path: mapped.identity.runtimePath,
137
+ presence: mapped.presence,
138
+ radioPower: mapped.radioPower,
139
+ mmState: mapped.mmState,
140
+ registration: {
141
+ status: mapped.registration.status,
142
+ rats: [...mapped.registration.activeRats].sort(),
143
+ },
144
+ nmActivation: mapped.nmActivation,
145
+ dataInterface: mapped.dataInterface,
146
+ reconcileStatus: mapped.reconcileStatus,
147
+ });
148
+ }
@@ -0,0 +1,44 @@
1
+ import type { DesiredRadio } from '../domain/index.js';
2
+ import type { BandReadResult, InhibitLease, ModemManagerPort, ModemRef, NetworkScanResult, ObservationList, ObservationListener, Receipt, SimPukUnlockResult, SimUnlockResult, Unsubscribe } from '../ports/index.js';
3
+ import type { DbusTransport } from '../transport/index.js';
4
+ import { type CellReading } from './cell-info.js';
5
+ import { type ModemEnrichment } from './enrichment.js';
6
+ import { ModemActor, type QuiesceHook } from './modem-actor.js';
7
+ import { type SignalCadence } from './signal-setup.js';
8
+ export interface MmDbusBackendOptions {
9
+ readonly transport: DbusTransport;
10
+ readonly destination?: string;
11
+ readonly actor?: ModemActor;
12
+ /** NM quiesce hook for disruptive mode/slot changes (A3.3 default: no-op). */
13
+ readonly quiesce?: QuiesceHook;
14
+ /** Signal.Setup reporting interval in seconds. */
15
+ readonly signalIntervalSeconds?: number;
16
+ readonly scanTimeoutMs?: number;
17
+ readonly now?: () => number;
18
+ }
19
+ /** The real D-Bus ModemManager backend: observation + mutations + Signal.Setup. */
20
+ export declare class MmDbusBackend implements ModemManagerPort {
21
+ #private;
22
+ constructor(options: MmDbusBackendOptions);
23
+ start(): Promise<ObservationList>;
24
+ observe(listener: ObservationListener): Unsubscribe;
25
+ stop(): Promise<void>;
26
+ setRadioModes(modem: ModemRef, preference: DesiredRadio): Promise<Receipt>;
27
+ setModeCombination(modem: ModemRef, allowed: number, preferred: number): Promise<Receipt>;
28
+ setPrimarySimSlot(modem: ModemRef, slotIndex: number): Promise<Receipt>;
29
+ readBands(modem: ModemRef): Promise<BandReadResult>;
30
+ setCurrentBands(modem: ModemRef, bands: readonly string[]): Promise<Receipt>;
31
+ sendPin(modem: ModemRef, pin: string): Promise<SimUnlockResult>;
32
+ sendPuk(modem: ModemRef, puk: string, newPin: string): Promise<SimPukUnlockResult>;
33
+ scanNetworks(modem: ModemRef): Promise<NetworkScanResult>;
34
+ inhibit(uid: string): Promise<InhibitLease>;
35
+ uninhibit(lease: InhibitLease): Promise<void>;
36
+ /** Whether periodic signal reporting is configured for a modem (Signal.Setup). */
37
+ signalCadence(modem: ModemRef): SignalCadence;
38
+ /** Read the normalized visible-cell list for a modem via `Modem.GetCellInfo`. */
39
+ readCellInfo(modem: ModemRef): Promise<readonly CellReading[]>;
40
+ /** Assemble the full read-only enrichment (revision, eSIM, cadence, cell info). */
41
+ readEnrichment(modem: ModemRef): Promise<ModemEnrichment>;
42
+ }
43
+ /** Construct the real D-Bus ModemManager backend over an A2.4 transport. */
44
+ export declare function createMmDbusBackend(options: MmDbusBackendOptions): MmDbusBackend;
@@ -0,0 +1,154 @@
1
+ // MmDbusBackend — the real D-Bus `ModemManagerPort`, composed from the A3 parts.
2
+ //
3
+ // It fulfils the whole port: the read side delegates to A3.1's epoch-scoped observer;
4
+ // the mutations run through `MmMutations` (each serialized on the shared per-modem
5
+ // `ModemActor`, keyed on A3.2's STABLE key so serialization survives replug); the
6
+ // Signal.Setup lifecycle is driven off the observer's `onEpochRefresh` hook; and the
7
+ // read-only enrichment (firmware revision, eSIM, signal cadence, normalized cell info)
8
+ // is assembled on demand. One transport is shared by all of them.
9
+ //
10
+ // The `onEpochRefresh` hook is the single integration seam: on every current-epoch
11
+ // authoritative snapshot it (1) rebuilds the live path → stable-key map (so a mutation
12
+ // keys the actor by the durable identity, not the transient path) and (2) re-drives
13
+ // Signal.Setup for that epoch — applying to new modems, re-applying to survivors after
14
+ // an owner change, and never firing for a superseded epoch.
15
+ import { epochMillis } from '../domain/index.js';
16
+ import { normalizeCellInfo } from './cell-info.js';
17
+ import { MM_BUS_NAME, MODEM_IFACE } from './constants.js';
18
+ import { buildEnrichment } from './enrichment.js';
19
+ import { modemIdentityFactsFromTree, resolveModemIdentities } from './identity-ladder.js';
20
+ import { fetchManagedObjects, pathsWithInterface } from './managed-objects.js';
21
+ import { MmMutations } from './mm-mutations.js';
22
+ import { ModemActor } from './modem-actor.js';
23
+ import { createMmDbusObserver } from './observer.js';
24
+ import { SignalSetupManager } from './signal-setup.js';
25
+ /** The real D-Bus ModemManager backend: observation + mutations + Signal.Setup. */
26
+ export class MmDbusBackend {
27
+ #transport;
28
+ #destination;
29
+ #now;
30
+ #observer;
31
+ #mutations;
32
+ #signalSetup;
33
+ // Live modem path → durable stable key; rebuilt on every epoch snapshot.
34
+ #stableKeyByPath = new Map();
35
+ constructor(options) {
36
+ this.#transport = options.transport;
37
+ this.#destination = options.destination ?? MM_BUS_NAME;
38
+ this.#now = options.now ?? Date.now;
39
+ const actor = options.actor ?? new ModemActor(options.quiesce);
40
+ this.#signalSetup = new SignalSetupManager({
41
+ transport: this.#transport,
42
+ destination: this.#destination,
43
+ ...(options.signalIntervalSeconds !== undefined
44
+ ? { intervalSeconds: options.signalIntervalSeconds }
45
+ : {}),
46
+ });
47
+ this.#observer = createMmDbusObserver({
48
+ transport: this.#transport,
49
+ destination: this.#destination,
50
+ onEpochRefresh: (event) => this.#onEpochRefresh(event),
51
+ });
52
+ this.#mutations = new MmMutations({
53
+ transport: this.#transport,
54
+ actor,
55
+ destination: this.#destination,
56
+ resolveStableKey: (modem) => this.#stableKeyByPath.get(modem) ?? modem,
57
+ ...(options.scanTimeoutMs !== undefined ? { scanTimeoutMs: options.scanTimeoutMs } : {}),
58
+ now: this.#now,
59
+ });
60
+ }
61
+ // ── observation (delegated to the A3.1 observer) ────────────────────────────────
62
+ start() {
63
+ return this.#observer.start();
64
+ }
65
+ observe(listener) {
66
+ return this.#observer.observe(listener);
67
+ }
68
+ stop() {
69
+ return this.#observer.stop();
70
+ }
71
+ // ── mutations (serialized per modem through the shared actor) ────────────────────
72
+ setRadioModes(modem, preference) {
73
+ return this.#mutations.setRadioModes(modem, preference);
74
+ }
75
+ setModeCombination(modem, allowed, preferred) {
76
+ return this.#mutations.setModeCombination(modem, allowed, preferred);
77
+ }
78
+ setPrimarySimSlot(modem, slotIndex) {
79
+ return this.#mutations.setPrimarySimSlot(modem, slotIndex);
80
+ }
81
+ readBands(modem) {
82
+ return this.#mutations.readBands(modem);
83
+ }
84
+ setCurrentBands(modem, bands) {
85
+ return this.#mutations.setCurrentBands(modem, bands);
86
+ }
87
+ sendPin(modem, pin) {
88
+ return this.#mutations.sendPin(modem, pin);
89
+ }
90
+ sendPuk(modem, puk, newPin) {
91
+ return this.#mutations.sendPuk(modem, puk, newPin);
92
+ }
93
+ scanNetworks(modem) {
94
+ return this.#mutations.scanNetworks(modem);
95
+ }
96
+ inhibit(uid) {
97
+ return this.#mutations.inhibit(uid);
98
+ }
99
+ uninhibit(lease) {
100
+ return this.#mutations.uninhibit(lease);
101
+ }
102
+ // ── enrichment (additive, read-only, never gating) ──────────────────────────────
103
+ /** Whether periodic signal reporting is configured for a modem (Signal.Setup). */
104
+ signalCadence(modem) {
105
+ return this.#signalSetup.cadenceFor(modem);
106
+ }
107
+ /** Read the normalized visible-cell list for a modem via `Modem.GetCellInfo`. */
108
+ async readCellInfo(modem) {
109
+ const provenance = { source: modem, observedAt: epochMillis(this.#now()) };
110
+ try {
111
+ const reply = await this.#transport.callMethod({
112
+ destination: this.#destination,
113
+ path: modem,
114
+ interface: MODEM_IFACE,
115
+ member: 'GetCellInfo',
116
+ });
117
+ const cells = Array.isArray(reply.body[0])
118
+ ? reply.body[0]
119
+ : [];
120
+ return normalizeCellInfo(cells, provenance);
121
+ }
122
+ catch {
123
+ return [];
124
+ }
125
+ }
126
+ /** Assemble the full read-only enrichment (revision, eSIM, cadence, cell info). */
127
+ async readEnrichment(modem) {
128
+ const [tree, cellInfo] = await Promise.all([
129
+ fetchManagedObjects(this.#transport, this.#destination),
130
+ this.readCellInfo(modem),
131
+ ]);
132
+ return buildEnrichment(tree, modem, this.#signalSetup.cadenceFor(modem), cellInfo);
133
+ }
134
+ #onEpochRefresh(event) {
135
+ this.#refreshStableKeys(event);
136
+ this.#signalSetup.applyForEpoch(event.epoch, event.tree);
137
+ }
138
+ #refreshStableKeys(event) {
139
+ const paths = pathsWithInterface(event.tree, MODEM_IFACE);
140
+ const facts = paths.map((path) => modemIdentityFactsFromTree(event.tree, path));
141
+ const resolved = resolveModemIdentities(facts);
142
+ this.#stableKeyByPath.clear();
143
+ paths.forEach((path, index) => {
144
+ const entry = resolved[index];
145
+ if (entry !== undefined) {
146
+ this.#stableKeyByPath.set(path, entry.stableKey);
147
+ }
148
+ });
149
+ }
150
+ }
151
+ /** Construct the real D-Bus ModemManager backend over an A2.4 transport. */
152
+ export function createMmDbusBackend(options) {
153
+ return new MmDbusBackend(options);
154
+ }
@@ -0,0 +1,21 @@
1
+ import type { FixRead, GnssSource, LocationStatusResult, LocationToggleResult, ModemLocationPort, ModemRef } from '../ports/index.js';
2
+ import type { DbusTransport } from '../transport/index.js';
3
+ import type { ModemActor } from './modem-actor.js';
4
+ /** Decode an MMModemLocationSource bitmask into source names. */
5
+ export declare function decodeLocationSources(mask: number): ReadonlySet<string>;
6
+ export declare function encodeLocationSources(sources: Iterable<string>): number;
7
+ export interface MmLocationDeps {
8
+ readonly transport: DbusTransport;
9
+ readonly actor: ModemActor;
10
+ readonly destination?: string;
11
+ readonly resolveStableKey: (modem: ModemRef) => string;
12
+ readonly now?: () => number;
13
+ }
14
+ export declare class MmLocation implements ModemLocationPort {
15
+ #private;
16
+ constructor(deps: MmLocationDeps);
17
+ getLocationStatus(modem: ModemRef): Promise<LocationStatusResult>;
18
+ enableGnss(modem: ModemRef, sources: readonly GnssSource[]): Promise<LocationToggleResult>;
19
+ disableGnss(modem: ModemRef): Promise<LocationToggleResult>;
20
+ readFix(modem: ModemRef): Promise<FixRead>;
21
+ }
@@ -0,0 +1,237 @@
1
+ // The `Modem.Location` adapter — GNSS status, enable/disable, and the current fix.
2
+ //
3
+ // Three decisions here are load-bearing and easy to undo by accident:
4
+ //
5
+ // 1. `Setup`'s `signal_location` argument is ALWAYS false. Passing true makes MM
6
+ // broadcast the `Location` property over `PropertiesChanged`, which would put
7
+ // the operator's coordinates on the system bus for every listener — including
8
+ // this package's own observer, whose snapshots are logged. The fix is fetched
9
+ // by an explicit `GetLocation` call instead, so a coordinate only ever exists
10
+ // where someone asked for it.
11
+ // 2. GNSS runs through `actor.run`, NOT `actor.runQuiesced`. Quiescing exists to
12
+ // stop NetworkManager racing a disruptive change, and it costs a bearer
13
+ // deactivation — on a bonded device, dropping a link to switch a GPS receiver
14
+ // on would be an absurd trade. `Modem.Location.Setup` touches no bearer, so
15
+ // serialization per modem is all that is needed.
16
+ // 3. Disable CLEARS only the GNSS bits. `3gpp-lac-ci` is the cell-info module's
17
+ // source; blanking the whole mask would silently switch off a neighbouring
18
+ // feature the operator never touched.
19
+ import { epochMillis } from '../domain/index.js';
20
+ import { parseNmeaFix } from '../location/nmea.js';
21
+ import { hasGnssSource } from '../ports/index.js';
22
+ import { MM_BUS_NAME, MODEM_LOCATION_IFACE } from './constants.js';
23
+ import { fetchManagedObjects, findInterface, propValue, } from './managed-objects.js';
24
+ /** MMModemLocationSource bits, by decoded source name. */
25
+ const SOURCE_BIT = {
26
+ '3gpp-lac-ci': 1 << 0,
27
+ 'gps-raw': 1 << 1,
28
+ 'gps-nmea': 1 << 2,
29
+ 'cdma-bs': 1 << 3,
30
+ 'gps-unmanaged': 1 << 4,
31
+ 'agps-msa': 1 << 5,
32
+ 'agps-msb': 1 << 6,
33
+ };
34
+ const GPS_RAW_BIT = SOURCE_BIT['gps-raw'];
35
+ const GPS_NMEA_BIT = SOURCE_BIT['gps-nmea'];
36
+ const GNSS_MASK = GPS_RAW_BIT |
37
+ GPS_NMEA_BIT |
38
+ SOURCE_BIT['gps-unmanaged'] |
39
+ SOURCE_BIT['agps-msa'] |
40
+ SOURCE_BIT['agps-msb'];
41
+ /** Decode an MMModemLocationSource bitmask into source names. */
42
+ export function decodeLocationSources(mask) {
43
+ const sources = new Set();
44
+ for (const [name, bit] of Object.entries(SOURCE_BIT)) {
45
+ if ((mask & bit) !== 0) {
46
+ sources.add(name);
47
+ }
48
+ }
49
+ return sources;
50
+ }
51
+ export function encodeLocationSources(sources) {
52
+ let mask = 0;
53
+ for (const source of sources) {
54
+ mask |= SOURCE_BIT[source] ?? 0;
55
+ }
56
+ return mask;
57
+ }
58
+ export class MmLocation {
59
+ #transport;
60
+ #actor;
61
+ #destination;
62
+ #resolveStableKey;
63
+ #now;
64
+ constructor(deps) {
65
+ this.#transport = deps.transport;
66
+ this.#actor = deps.actor;
67
+ this.#destination = deps.destination ?? MM_BUS_NAME;
68
+ this.#resolveStableKey = deps.resolveStableKey;
69
+ this.#now = deps.now ?? Date.now;
70
+ }
71
+ async getLocationStatus(modem) {
72
+ const props = await this.#readLocationProps(modem);
73
+ if (props === undefined) {
74
+ return { ok: false, reason: 'the modem does not expose a Location interface' };
75
+ }
76
+ return { ok: true, status: statusOf(props) };
77
+ }
78
+ enableGnss(modem, sources) {
79
+ return this.#actor.run(this.#resolveStableKey(modem), async () => {
80
+ const props = await this.#readLocationProps(modem);
81
+ if (props === undefined) {
82
+ return refused('the modem does not expose a Location interface');
83
+ }
84
+ const status = statusOf(props);
85
+ if (!status.gnssCapable) {
86
+ return refused('the modem advertises no GNSS source', status.enabledSources);
87
+ }
88
+ const requested = encodeLocationSources(sources) & maskOfNames(status.capabilities);
89
+ if (requested === 0) {
90
+ return refused('none of the requested GNSS sources is advertised by this modem', status.enabledSources);
91
+ }
92
+ const target = maskOfNames(status.enabledSources) | requested;
93
+ return this.#setup(modem, target, 'GNSS enabled');
94
+ });
95
+ }
96
+ disableGnss(modem) {
97
+ return this.#actor.run(this.#resolveStableKey(modem), async () => {
98
+ const props = await this.#readLocationProps(modem);
99
+ if (props === undefined) {
100
+ return refused('the modem does not expose a Location interface');
101
+ }
102
+ const status = statusOf(props);
103
+ const target = maskOfNames(status.enabledSources) & ~GNSS_MASK;
104
+ return this.#setup(modem, target, 'GNSS disabled');
105
+ });
106
+ }
107
+ async readFix(modem) {
108
+ const props = await this.#readLocationProps(modem);
109
+ if (props === undefined) {
110
+ return { outcome: 'unsupported', reason: 'the modem does not expose a Location interface' };
111
+ }
112
+ const status = statusOf(props);
113
+ if (!status.gnssCapable) {
114
+ return { outcome: 'unsupported', reason: 'the modem advertises no GNSS source' };
115
+ }
116
+ if (!status.gnssEnabled) {
117
+ return { outcome: 'disabled', reason: 'GNSS is switched off on this modem' };
118
+ }
119
+ let reply;
120
+ try {
121
+ reply = await this.#transport.callMethod({
122
+ destination: this.#destination,
123
+ path: modem,
124
+ interface: MODEM_LOCATION_IFACE,
125
+ member: 'GetLocation',
126
+ });
127
+ }
128
+ catch (error) {
129
+ return { outcome: 'error', reason: `GetLocation failed: ${describe(error)}` };
130
+ }
131
+ const fix = decodeFix(reply.body[0], epochMillis(this.#now()));
132
+ return fix === undefined
133
+ ? { outcome: 'no-fix', reason: 'the receiver has not acquired a position' }
134
+ : { outcome: 'fix', fix };
135
+ }
136
+ async #setup(modem, mask, appliedReason) {
137
+ try {
138
+ await this.#transport.callMethod({
139
+ destination: this.#destination,
140
+ path: modem,
141
+ interface: MODEM_LOCATION_IFACE,
142
+ member: 'Setup',
143
+ signature: 'ub',
144
+ args: [mask, false],
145
+ });
146
+ }
147
+ catch (error) {
148
+ return {
149
+ outcome: 'failed',
150
+ reason: `Location.Setup failed: ${describe(error)}`,
151
+ enabledSources: new Set(),
152
+ };
153
+ }
154
+ return {
155
+ outcome: 'applied',
156
+ reason: appliedReason,
157
+ enabledSources: decodeLocationSources(mask),
158
+ };
159
+ }
160
+ async #readLocationProps(modem) {
161
+ try {
162
+ const tree = await fetchManagedObjects(this.#transport, this.#destination);
163
+ return findInterface(tree, modem, MODEM_LOCATION_IFACE);
164
+ }
165
+ catch {
166
+ return undefined;
167
+ }
168
+ }
169
+ }
170
+ function maskOfNames(names) {
171
+ return encodeLocationSources(names);
172
+ }
173
+ function refused(reason, enabled = new Set()) {
174
+ return { outcome: 'unsupported', reason, enabledSources: enabled };
175
+ }
176
+ function statusOf(props) {
177
+ const capabilities = decodeLocationSources(maskProp(props, 'Capabilities'));
178
+ const enabledSources = decodeLocationSources(maskProp(props, 'Enabled'));
179
+ return {
180
+ capabilities,
181
+ enabledSources,
182
+ gnssCapable: hasGnssSource(capabilities),
183
+ gnssEnabled: hasGnssSource(enabledSources),
184
+ };
185
+ }
186
+ function maskProp(props, name) {
187
+ const value = propValue(props, name);
188
+ return typeof value === 'number' && Number.isFinite(value) ? value : 0;
189
+ }
190
+ function entryValue(payload, bit) {
191
+ if (!Array.isArray(payload)) {
192
+ return undefined;
193
+ }
194
+ for (const entry of payload) {
195
+ if (Number(entry[0]) === bit) {
196
+ return entry[1]?.value;
197
+ }
198
+ }
199
+ return undefined;
200
+ }
201
+ function numberField(props, name) {
202
+ const value = propValue(props, name);
203
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
204
+ }
205
+ /**
206
+ * Prefer MM's pre-decoded `gps-raw` dict; fall back to parsing the `gps-nmea`
207
+ * sentences. A raw entry that is present but carries no usable coordinate pair is
208
+ * NOT treated as a fix — MM populates the key as soon as the source is on.
209
+ */
210
+ function decodeFix(payload, observedAt) {
211
+ const raw = entryValue(payload, GPS_RAW_BIT);
212
+ if (Array.isArray(raw)) {
213
+ const props = raw;
214
+ const latitude = numberField(props, 'latitude');
215
+ const longitude = numberField(props, 'longitude');
216
+ if (latitude !== undefined && longitude !== undefined) {
217
+ const altitude = numberField(props, 'altitude');
218
+ const utcTime = propValue(props, 'utc-time');
219
+ return {
220
+ latitude,
221
+ longitude,
222
+ ...(altitude === undefined ? {} : { altitude }),
223
+ ...(typeof utcTime === 'string' ? { utcTime } : {}),
224
+ observedAt,
225
+ };
226
+ }
227
+ }
228
+ const nmea = entryValue(payload, GPS_NMEA_BIT);
229
+ if (typeof nmea !== 'string') {
230
+ return undefined;
231
+ }
232
+ const parsed = parseNmeaFix(nmea);
233
+ return parsed === undefined ? undefined : { ...parsed, observedAt };
234
+ }
235
+ function describe(error) {
236
+ return error instanceof Error ? error.message : String(error);
237
+ }
@@ -0,0 +1,42 @@
1
+ import type { DesiredRadio } from '../domain/index.js';
2
+ import type { BandReadResult, InhibitLease, ModemRef, NetworkScanResult, Receipt, SimPukUnlockResult, SimUnlockResult } from '../ports/index.js';
3
+ import type { DbusTransport } from '../transport/index.js';
4
+ import type { ModemActor } from './modem-actor.js';
5
+ export interface MmMutationsDeps {
6
+ readonly transport: DbusTransport;
7
+ readonly actor: ModemActor;
8
+ readonly destination?: string;
9
+ /** Map a live modem path to its stable actor key (survives replug). */
10
+ readonly resolveStableKey: (modem: ModemRef) => string;
11
+ readonly scanTimeoutMs?: number;
12
+ readonly now?: () => number;
13
+ }
14
+ /** The disruptive + SIM mutations of `ModemManagerPort`, serialized per modem. */
15
+ export declare class MmMutations {
16
+ #private;
17
+ constructor(deps: MmMutationsDeps);
18
+ setRadioModes(modem: ModemRef, preference: DesiredRadio): Promise<Receipt>;
19
+ /**
20
+ * `SetCurrentModes` over the RAW `(uu)` masks the modem itself advertised.
21
+ *
22
+ * `setRadioModes` above speaks the reconciler's vocabulary — an ordered RAT
23
+ * preference — and structurally cannot express `MM_MODEM_MODE_NONE`: its preferred
24
+ * mask is derived from `preferenceOrdered[0]`, so "allow this set and prefer
25
+ * nothing within it" has no spelling. That is the combination the bench FM350-GL
26
+ * actually advertises, so a caller selecting an advertised combination verbatim
27
+ * needs this entry point. It quiesces for the same reason `setRadioModes` does: a
28
+ * mode change re-registers the radio and drops the bearer underneath NM.
29
+ *
30
+ * An `allowed` mask of 0 is refused — that is not a selection, it is a radio with
31
+ * nothing switched on — while a `preferred` mask of 0 is passed through untouched.
32
+ */
33
+ setModeCombination(modem: ModemRef, allowed: number, preferred: number): Promise<Receipt>;
34
+ readBands(modem: ModemRef): Promise<BandReadResult>;
35
+ setCurrentBands(modem: ModemRef, bands: readonly string[]): Promise<Receipt>;
36
+ setPrimarySimSlot(modem: ModemRef, slotIndex: number): Promise<Receipt>;
37
+ sendPin(modem: ModemRef, pin: string): Promise<SimUnlockResult>;
38
+ sendPuk(modem: ModemRef, puk: string, newPin: string): Promise<SimPukUnlockResult>;
39
+ scanNetworks(modem: ModemRef): Promise<NetworkScanResult>;
40
+ inhibit(uid: string): Promise<InhibitLease>;
41
+ uninhibit(lease: InhibitLease): Promise<void>;
42
+ }