@ceralive/modem-control 1.0.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 (474) 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 -236
  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/uhubctl-power-hook.test.ts +0 -274
  406. package/src/backend/uhubctl-power-hook.ts +0 -377
  407. package/src/backend/usage/accounting.test.ts +0 -147
  408. package/src/backend/usage/accounting.ts +0 -123
  409. package/src/backend/usage/billing-cycle.test.ts +0 -62
  410. package/src/backend/usage/billing-cycle.ts +0 -45
  411. package/src/backend/usage/index.ts +0 -60
  412. package/src/backend/usage/policy-store.test.ts +0 -164
  413. package/src/backend/usage/policy-store.ts +0 -216
  414. package/src/backend/usage/policy-write.test.ts +0 -198
  415. package/src/backend/usage/policy-write.ts +0 -207
  416. package/src/backend/usage/proc-net-dev.test.ts +0 -56
  417. package/src/backend/usage/sampler.test.ts +0 -327
  418. package/src/backend/usage/sampler.ts +0 -282
  419. package/src/backend/usage/store.test.ts +0 -148
  420. package/src/backend/usage/store.ts +0 -177
  421. package/src/backend/usb-enumerator.test.ts +0 -87
  422. package/src/backend/usb-enumerator.ts +0 -181
  423. package/src/backend/usb-mode-transition.test.ts +0 -323
  424. package/src/backend/usb-mode-transition.ts +0 -253
  425. package/src/domain/brand.ts +0 -29
  426. package/src/domain/errors.ts +0 -77
  427. package/src/domain/guards.test.ts +0 -218
  428. package/src/domain/guards.ts +0 -144
  429. package/src/domain/identity.test.ts +0 -83
  430. package/src/domain/identity.ts +0 -165
  431. package/src/domain/index.ts +0 -12
  432. package/src/domain/snapshot.test.ts +0 -266
  433. package/src/domain/snapshot.ts +0 -120
  434. package/src/index.test.ts +0 -6
  435. package/src/index.ts +0 -15
  436. package/src/ports/README.md +0 -61
  437. package/src/ports/forbidden-surface.test.ts +0 -241
  438. package/src/ports/modem-manager.ts +0 -72
  439. package/src/ports/network-manager.ts +0 -87
  440. package/src/ports/ops.ts +0 -60
  441. package/src/ports/ops.type-test.ts +0 -39
  442. package/src/ports/receipts.test.ts +0 -153
  443. package/src/ports/reconcile.test.ts +0 -152
  444. package/src/ports/reconcile.ts +0 -338
  445. package/src/ports/router.ts +0 -29
  446. package/src/redact.test.ts +0 -82
  447. package/src/redact.ts +0 -73
  448. package/src/transport/README.md +0 -65
  449. package/src/transport/calls.ts +0 -113
  450. package/src/transport/characterization.test.ts +0 -260
  451. package/src/transport/codec.test.ts +0 -118
  452. package/src/transport/codec.ts +0 -240
  453. package/src/transport/conformance-python.test.ts +0 -152
  454. package/src/transport/conformance-same-lib.test.ts +0 -115
  455. package/src/transport/dbus-native-lib.d.ts +0 -19
  456. package/src/transport/dbus-native.ts +0 -85
  457. package/src/transport/index.ts +0 -30
  458. package/src/transport/no-library-leak.test.ts +0 -61
  459. package/src/transport/reliability.test.ts +0 -173
  460. package/src/transport/signals.ts +0 -150
  461. package/src/transport/signature.ts +0 -110
  462. package/src/transport/test-support/fake-service.ts +0 -168
  463. package/src/transport/test-support/independent-producer.py +0 -110
  464. package/src/transport/test-support/private-bus.ts +0 -66
  465. package/src/transport/transport.ts +0 -250
  466. package/src/transport/types.ts +0 -118
  467. package/src/usb-mode/catalog-schema.test.ts +0 -181
  468. package/src/usb-mode/catalog-schema.ts +0 -113
  469. package/src/usb-mode/certified-catalog.json +0 -67
  470. package/src/usb-mode/index.ts +0 -58
  471. package/src/usb-mode/ingestion.test.ts +0 -268
  472. package/src/usb-mode/ingestion.ts +0 -297
  473. package/src/usb-mode/promotion-review.ts +0 -117
  474. package/src/usb-mode/usb-devices-parse.ts +0 -196
@@ -0,0 +1,118 @@
1
+ import { epochMillis, runtimePath } from '../../domain/index.js';
2
+ import { resolveFccUnlockCoverage } from '../../fcc/index.js';
3
+ import { advanceGnssFixState, GNSS_OFF } from '../../location/index.js';
4
+ import { createDbusSmsPort } from '../../sms/index.js';
5
+ export class ModemManagerModuleOperations {
6
+ operations;
7
+ #options;
8
+ #smsPorts = new Map();
9
+ #locationStates = new Map();
10
+ constructor(options) {
11
+ this.#options = options;
12
+ this.operations = {
13
+ location: {
14
+ status: async (context) => {
15
+ const path = await this.#modemPath(context);
16
+ return path === undefined
17
+ ? { ok: false, reason: 'modem-not-found' }
18
+ : this.#options.location.getLocationStatus(runtimePath(path));
19
+ },
20
+ enable: async (context, sources) => {
21
+ const path = await this.#modemPath(context);
22
+ if (path === undefined)
23
+ return this.#unsupportedToggle();
24
+ const result = await this.#options.location.enableGnss(runtimePath(path), sources);
25
+ if (result.outcome === 'applied') {
26
+ this.#setLocationState(context, {
27
+ kind: 'gnss-enabled',
28
+ at: epochMillis(this.#options.now()),
29
+ });
30
+ }
31
+ return result;
32
+ },
33
+ disable: async (context) => {
34
+ const path = await this.#modemPath(context);
35
+ if (path === undefined)
36
+ return this.#unsupportedToggle();
37
+ const result = await this.#options.location.disableGnss(runtimePath(path));
38
+ if (result.outcome === 'applied')
39
+ this.#setLocationState(context, { kind: 'gnss-disabled' });
40
+ return result;
41
+ },
42
+ readFix: async (context) => {
43
+ const path = await this.#modemPath(context);
44
+ if (path === undefined)
45
+ return { outcome: 'unsupported', reason: 'modem-not-found' };
46
+ const read = await this.#options.location.readFix(runtimePath(path));
47
+ this.#setLocationState(context, {
48
+ kind: 'read',
49
+ at: epochMillis(this.#options.now()),
50
+ read,
51
+ });
52
+ return read;
53
+ },
54
+ state: (context) => this.#locationStates.get(String(context.physicalModemId)) ?? GNSS_OFF,
55
+ tick: (context) => this.#setLocationState(context, {
56
+ kind: 'tick',
57
+ at: epochMillis(this.#options.now()),
58
+ }),
59
+ },
60
+ sms: (context) => this.#sms(context),
61
+ ussd: options.ussd,
62
+ initiateUssd: (context, ussdCommand) => this.#ussd(context, (path) => options.ussd.initiate(runtimePath(path), ussdCommand)),
63
+ respondUssd: (context, ussdResponse) => this.#ussd(context, (path) => options.ussd.respond(runtimePath(path), ussdResponse)),
64
+ cancelUssd: (context) => this.#ussd(context, (path) => options.ussd.cancel(runtimePath(path))),
65
+ fccCoverage: resolveFccUnlockCoverage,
66
+ };
67
+ }
68
+ async stop() {
69
+ for (const port of this.#smsPorts.values())
70
+ await port.stop();
71
+ this.#smsPorts.clear();
72
+ this.#options.ussd.stop();
73
+ }
74
+ async #sms(context) {
75
+ const snapshot = await this.#options.readSnapshot(context);
76
+ if (!snapshot.ok)
77
+ return { ok: false, reason: snapshot.reason };
78
+ if (!snapshot.capabilities.sms)
79
+ return { ok: false, reason: 'unsupported' };
80
+ let port = this.#smsPorts.get(snapshot.modemPath);
81
+ if (port === undefined) {
82
+ port = createDbusSmsPort({
83
+ transport: this.#options.transport,
84
+ modemPath: snapshot.modemPath,
85
+ destination: this.#options.destination,
86
+ });
87
+ this.#smsPorts.set(snapshot.modemPath, port);
88
+ }
89
+ return { ok: true, port };
90
+ }
91
+ async #ussd(context, run) {
92
+ const path = await this.#modemPath(context);
93
+ return path === undefined
94
+ ? {
95
+ ok: false,
96
+ snapshot: this.#options.ussd.snapshot(runtimePath('/')),
97
+ refusal: 'unsupported',
98
+ }
99
+ : run(path);
100
+ }
101
+ async #modemPath(context) {
102
+ const snapshot = await this.#options.readSnapshot(context);
103
+ return snapshot.ok ? snapshot.modemPath : undefined;
104
+ }
105
+ #setLocationState(context, event) {
106
+ const key = String(context.physicalModemId);
107
+ const next = advanceGnssFixState(this.#locationStates.get(key) ?? GNSS_OFF, event);
108
+ this.#locationStates.set(key, next);
109
+ return next;
110
+ }
111
+ #unsupportedToggle() {
112
+ return {
113
+ outcome: 'unsupported',
114
+ reason: 'modem-not-found',
115
+ enabledSources: new Set(),
116
+ };
117
+ }
118
+ }
@@ -0,0 +1,39 @@
1
+ import { type MmDbusBackend, type QuiesceHook } from '../../backend/index.js';
2
+ import { type BandCertificationCatalog, type BandSku } from '../../band/index.js';
3
+ import { type NormalizedModemObservation } from '../../observations/index.js';
4
+ import type { ObservationListener } from '../../ports/index.js';
5
+ import type { DbusTransport } from '../../transport/index.js';
6
+ import type { ProviderDefinition, ProviderExecutionContext } from '../contracts.js';
7
+ import type { ModemManagerProviderLifecycle, ModemManagerProviderOperations, ModemManagerSnapshotResult } from './types.js';
8
+ export interface ModemManagerProviderOptions {
9
+ readonly transport: DbusTransport;
10
+ readonly destination?: string;
11
+ readonly quiesce?: QuiesceHook;
12
+ readonly now?: () => number;
13
+ /**
14
+ * The band-lock certification catalog. Defaults to the one shipped in this package,
15
+ * which is EMPTY — so a band write is refused on every device until a human-reviewed
16
+ * commit adds an entry carrying its bench transcript.
17
+ */
18
+ readonly bandCertificationCatalog?: BandCertificationCatalog;
19
+ /**
20
+ * The device's SKU, if the embedding process can resolve one.
21
+ *
22
+ * ModemManager's `Modem` interface carries `Model` and `Revision` but NO USB
23
+ * `vid:pid`, and `BandSku` is keyed on all three — so this package structurally
24
+ * cannot build one from the bus alone, and a partial match would certify a family
25
+ * no reviewer looked at. With no resolver injected there is no SKU, hence no entry,
26
+ * hence no band write: fail-closed, which is this module's whole stance.
27
+ */
28
+ readonly bandSku?: (context: ProviderExecutionContext) => BandSku | undefined;
29
+ }
30
+ export declare class ModemManagerProvider implements ModemManagerProviderLifecycle {
31
+ #private;
32
+ readonly definition: ProviderDefinition<NormalizedModemObservation, ModemManagerProviderOperations>;
33
+ constructor(options: ModemManagerProviderOptions);
34
+ start(): ReturnType<MmDbusBackend['start']>;
35
+ observe(listener: ObservationListener): () => void;
36
+ stop(): Promise<void>;
37
+ readSnapshot(context: ProviderExecutionContext): Promise<ModemManagerSnapshotResult>;
38
+ }
39
+ export declare function createModemManagerProvider(options: ModemManagerProviderOptions): ModemManagerProvider;
@@ -0,0 +1,152 @@
1
+ import { createMmDbusBackend, fetchManagedObjects, MM_BUS_NAME, MmLocation, ModemActor, } from '../../backend/index.js';
2
+ import { BAND_CERTIFICATION_CATALOG, findBandCertification, } from '../../band/index.js';
3
+ import { epochMillis, sourceEpoch, stableKeyFromPhysicalModemId, } from '../../domain/index.js';
4
+ import { unavailableObservation } from '../../observations/index.js';
5
+ import { MmUssd } from '../../ussd/index.js';
6
+ import { mapModemManagerError } from './errors.js';
7
+ import { createGenericOperations } from './generic-operations.js';
8
+ import { ModemManagerModuleOperations } from './module-operations.js';
9
+ import { buildModemManagerSnapshot } from './snapshot.js';
10
+ const PROFILE = 'generic-mm';
11
+ export class ModemManagerProvider {
12
+ definition;
13
+ #transport;
14
+ #destination;
15
+ #now;
16
+ #backend;
17
+ #bandCatalog;
18
+ #bandSku;
19
+ #stableKeyByPath = new Map();
20
+ #moduleOperations;
21
+ #startPromise;
22
+ #epoch = 1;
23
+ constructor(options) {
24
+ this.#transport = options.transport;
25
+ this.#destination = options.destination ?? MM_BUS_NAME;
26
+ this.#now = options.now ?? Date.now;
27
+ this.#bandCatalog = options.bandCertificationCatalog ?? BAND_CERTIFICATION_CATALOG;
28
+ this.#bandSku = options.bandSku ?? (() => undefined);
29
+ const actor = new ModemActor(options.quiesce);
30
+ const resolveStableKey = (modem) => this.#stableKeyByPath.get(modem) ?? modem;
31
+ this.#backend = createMmDbusBackend({
32
+ transport: this.#transport,
33
+ destination: this.#destination,
34
+ actor,
35
+ now: this.#now,
36
+ });
37
+ const location = new MmLocation({
38
+ transport: this.#transport,
39
+ actor,
40
+ destination: this.#destination,
41
+ resolveStableKey,
42
+ now: this.#now,
43
+ });
44
+ const ussd = new MmUssd({
45
+ transport: this.#transport,
46
+ actor,
47
+ destination: this.#destination,
48
+ resolveStableKey,
49
+ });
50
+ this.#moduleOperations = new ModemManagerModuleOperations({
51
+ transport: this.#transport,
52
+ destination: this.#destination,
53
+ location,
54
+ ussd,
55
+ now: this.#now,
56
+ readSnapshot: (context) => this.readSnapshot(context),
57
+ });
58
+ const operations = {
59
+ access: 'read-write',
60
+ ...createGenericOperations({
61
+ backend: this.#backend,
62
+ readSnapshot: (context) => this.readSnapshot(context),
63
+ }),
64
+ ...this.#moduleOperations.operations,
65
+ };
66
+ this.definition = {
67
+ id: 'modemmanager',
68
+ profileVersion: '1',
69
+ eligibleTransports: ['modemmanager'],
70
+ passiveMatchers: [],
71
+ unauthenticatedProbes: [
72
+ { id: 'object-manager-modem', run: (request) => this.#probe(request) },
73
+ ],
74
+ capabilityReaders: [
75
+ { id: 'runtime-modem-interface', read: (context) => this.#capability(context) },
76
+ ],
77
+ observe: (context) => this.#observeNormalized(context),
78
+ operations: () => operations,
79
+ contractFixtures: [],
80
+ };
81
+ }
82
+ start() {
83
+ this.#startPromise ??= this.#backend.start();
84
+ return this.#startPromise;
85
+ }
86
+ observe(listener) {
87
+ return this.#backend.observe(listener);
88
+ }
89
+ async stop() {
90
+ await this.#moduleOperations.stop();
91
+ await this.#backend.stop();
92
+ }
93
+ async readSnapshot(context) {
94
+ try {
95
+ await this.start();
96
+ const tree = await fetchManagedObjects(this.#transport, this.#destination);
97
+ const snapshot = buildModemManagerSnapshot(tree, context, this.#now, this.#epoch, this.#bandCertification(context));
98
+ if (snapshot === undefined)
99
+ return { ok: false, reason: 'not-found' };
100
+ this.#stableKeyByPath.set(snapshot.modemPath, `modem:${context.physicalModemId}`);
101
+ return { ok: true, ...snapshot };
102
+ }
103
+ catch (error) {
104
+ return { ok: false, reason: mapModemManagerError(error).reason };
105
+ }
106
+ }
107
+ #bandCertification(context) {
108
+ const sku = this.#bandSku(context);
109
+ return sku === undefined ? undefined : findBandCertification(this.#bandCatalog, sku);
110
+ }
111
+ async #probe(request) {
112
+ const snapshot = await this.readSnapshot({ ...request, profile: PROFILE });
113
+ return {
114
+ signal: snapshot.ok
115
+ ? 'match'
116
+ : snapshot.reason === 'not-found'
117
+ ? 'mismatch'
118
+ : 'unknown',
119
+ strength: 'strong',
120
+ profiles: [PROFILE],
121
+ detail: snapshot.ok ? 'runtime-modem-interface-present' : snapshot.reason,
122
+ };
123
+ }
124
+ async #capability(context) {
125
+ const snapshot = await this.readSnapshot(context);
126
+ return {
127
+ signal: snapshot.ok
128
+ ? 'match'
129
+ : snapshot.reason === 'not-found'
130
+ ? 'mismatch'
131
+ : 'unknown',
132
+ strength: 'strong',
133
+ detail: snapshot.ok ? 'generic-controls-derived-from-runtime-properties' : snapshot.reason,
134
+ };
135
+ }
136
+ async #observeNormalized(context) {
137
+ const snapshot = await this.readSnapshot(context);
138
+ if (snapshot.ok)
139
+ return [snapshot.observation];
140
+ return [
141
+ unavailableObservation('modemmanager', {
142
+ stableKey: stableKeyFromPhysicalModemId(context.physicalModemId),
143
+ generation: context.generation,
144
+ sourceEpoch: sourceEpoch(this.#epoch),
145
+ observedAt: epochMillis(this.#now()),
146
+ }, snapshot.reason === 'not-found' ? 'device-absent' : 'provider-unavailable'),
147
+ ];
148
+ }
149
+ }
150
+ export function createModemManagerProvider(options) {
151
+ return new ModemManagerProvider(options);
152
+ }
@@ -0,0 +1,5 @@
1
+ import { type DecodedManagedObjects } from '../../backend/managed-objects.js';
2
+ import { type BandCertificationEntry } from '../../band/index.js';
3
+ import type { ProviderExecutionContext } from '../contracts.js';
4
+ import type { ModemManagerProviderSnapshot } from './types.js';
5
+ export declare function buildModemManagerSnapshot(tree: DecodedManagedObjects, context: ProviderExecutionContext, now: () => number, epoch: number, bandCertificationEntry: BandCertificationEntry | undefined): ModemManagerProviderSnapshot | undefined;
@@ -0,0 +1,204 @@
1
+ import { MODEM_IFACE, MODEM_LOCATION_IFACE, MODEM3GPP_IFACE, MODEM3GPP_USSD_IFACE, SIM_IFACE, } from '../../backend/constants.js';
2
+ import { findInterface, pathsWithInterface, propValue, } from '../../backend/managed-objects.js';
3
+ import { decodeBandList } from '../../band/index.js';
4
+ import { MESSAGING_IFACE } from '../../capability/index.js';
5
+ import { decodeStateFailedReason, epochMillis, sourceEpoch, stableKeyFromPhysicalModemId, } from '../../domain/index.js';
6
+ import { readSimPresence } from '../../hardware/router-parsers.js';
7
+ import { normalizeModemManagerObservation } from '../../observations/index.js';
8
+ import { describeBandWriteCertification, readRadioModeTruth } from '../../radio/index.js';
9
+ const SIGNAL_IFACE = `${MODEM_IFACE}.Signal`;
10
+ function rawValue(value) {
11
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean')
12
+ return value;
13
+ if (Array.isArray(value)) {
14
+ const result = [];
15
+ for (const item of value) {
16
+ const decoded = rawValue(item);
17
+ if (decoded !== undefined)
18
+ result.push(decoded);
19
+ }
20
+ return result;
21
+ }
22
+ return undefined;
23
+ }
24
+ function propsRecord(props) {
25
+ if (props === undefined)
26
+ return undefined;
27
+ const record = {};
28
+ for (const [name, wrapped] of props) {
29
+ const decoded = rawValue(wrapped.value);
30
+ if (decoded !== undefined)
31
+ record[name] = decoded;
32
+ }
33
+ return record;
34
+ }
35
+ function tuple(value) {
36
+ if (!Array.isArray(value))
37
+ return [0, 0];
38
+ const allowed = value[0];
39
+ const preferred = value[1];
40
+ return [typeof allowed === 'number' ? allowed : 0, typeof preferred === 'number' ? preferred : 0];
41
+ }
42
+ function modePairs(value) {
43
+ if (!Array.isArray(value))
44
+ return [];
45
+ const result = [];
46
+ for (const candidate of value) {
47
+ const [allowed, preferred] = tuple(candidate);
48
+ if (allowed > 0)
49
+ result.push({ allowed, preferred });
50
+ }
51
+ return result;
52
+ }
53
+ /**
54
+ * A `DbusValue` as the retention layer keeps it.
55
+ *
56
+ * `rawValue` already produces exactly this for a struct (an array of its members), so
57
+ * `SupportedModes` and `CurrentModes` reach the verbatim decoder in the same shape the
58
+ * diagnostics block retains — one representation, so the two cannot disagree.
59
+ */
60
+ function structValue(value) {
61
+ return value === undefined ? undefined : rawValue(value);
62
+ }
63
+ function signalState(modem, signal) {
64
+ const quality = tuple(propValue(modem, 'SignalQuality'));
65
+ return {
66
+ ...(quality[0] >= 0 ? { quality: quality[0] } : {}),
67
+ ...(Array.isArray(propValue(modem, 'SignalQuality')) ? { recent: quality[1] !== 0 } : {}),
68
+ extendedAvailable: signal !== undefined,
69
+ };
70
+ }
71
+ function simState(tree, modem) {
72
+ const activePath = propValue(modem, 'Sim');
73
+ const slots = propValue(modem, 'SimSlots');
74
+ const paths = Array.isArray(slots)
75
+ ? slots.filter((value) => typeof value === 'string' && value !== '/')
76
+ : [];
77
+ const active = typeof activePath === 'string' ? findInterface(tree, activePath, SIM_IFACE) : undefined;
78
+ const lockRequired = propValue(modem, 'UnlockRequired');
79
+ const rawFailedReason = propValue(modem, 'StateFailedReason');
80
+ const failedReason = typeof rawFailedReason === 'number'
81
+ ? decodeStateFailedReason(rawFailedReason)
82
+ : typeof rawFailedReason === 'string'
83
+ ? rawFailedReason
84
+ : undefined;
85
+ const primarySlot = propValue(modem, 'PrimarySimSlot');
86
+ const simType = propValue(active, 'SimType');
87
+ const esimStatus = propValue(active, 'EsimStatus');
88
+ const reading = readSimPresence({
89
+ ...(typeof activePath === 'string' ? { sim: activePath } : {}),
90
+ ...(paths.length > 0 ? { simSlots: paths } : {}),
91
+ ...(failedReason === undefined ? {} : { failedReason }),
92
+ });
93
+ return {
94
+ // POSITIVE evidence only: an exported SIM object path proves a SIM. Its ABSENCE
95
+ // proves nothing — `presence` below is the answer to "is there a SIM", and it
96
+ // says `unknown` unless the modem stated `sim-missing` itself.
97
+ present: typeof activePath === 'string' && activePath !== '/',
98
+ presence: reading.presence,
99
+ presenceEvidence: reading.evidence,
100
+ slotCount: paths.length,
101
+ primarySlot: typeof primarySlot === 'number' ? primarySlot : 0,
102
+ ...(typeof lockRequired === 'number' ? { lockRequired } : {}),
103
+ ...(typeof simType === 'number' ? { simType } : {}),
104
+ ...(typeof esimStatus === 'number' ? { esimStatus } : {}),
105
+ };
106
+ }
107
+ function radioPower(value) {
108
+ switch (value) {
109
+ case 1:
110
+ return 'off';
111
+ case 2:
112
+ return 'low';
113
+ case 3:
114
+ return 'on';
115
+ default:
116
+ return 'unknown';
117
+ }
118
+ }
119
+ function hasProperty(props, name) {
120
+ return props.some(([property]) => property === name);
121
+ }
122
+ function findModemPath(tree, context) {
123
+ const [kind, ...parts] = String(context.physicalModemId).split(':');
124
+ const expected = parts.join(':');
125
+ for (const path of pathsWithInterface(tree, MODEM_IFACE)) {
126
+ const modem = findInterface(tree, path, MODEM_IFACE);
127
+ const candidates = kind === 'serial'
128
+ ? [propValue(modem, 'DeviceIdentifier')]
129
+ : [propValue(modem, 'Device'), propValue(modem, 'Physdev')];
130
+ if (candidates.some((candidate) => candidate === expected))
131
+ return path;
132
+ }
133
+ return undefined;
134
+ }
135
+ export function buildModemManagerSnapshot(tree, context, now, epoch, bandCertificationEntry) {
136
+ const modemPath = findModemPath(tree, context);
137
+ if (modemPath === undefined)
138
+ return undefined;
139
+ const modem = findInterface(tree, modemPath, MODEM_IFACE);
140
+ if (modem === undefined)
141
+ return undefined;
142
+ const modem3gpp = findInterface(tree, modemPath, MODEM3GPP_IFACE);
143
+ const signal = findInterface(tree, modemPath, SIGNAL_IFACE);
144
+ const activePath = propValue(modem, 'Sim');
145
+ const sim = typeof activePath === 'string' ? findInterface(tree, activePath, SIM_IFACE) : undefined;
146
+ const interfaces = tree.find(([path]) => path === modemPath)?.[1] ?? [];
147
+ const interfaceNames = new Set(interfaces.map(([name]) => name));
148
+ const slots = propValue(modem, 'SimSlots');
149
+ const bandCertification = describeBandWriteCertification({
150
+ entry: bandCertificationEntry,
151
+ supported: decodeBandList(propValue(modem, 'SupportedBands')),
152
+ });
153
+ const capabilities = {
154
+ modeRead: hasProperty(modem, 'CurrentModes'),
155
+ modeWrite: hasProperty(modem, 'CurrentModes'),
156
+ bandRead: hasProperty(modem, 'SupportedBands'),
157
+ bandWrite: hasProperty(modem, 'SupportedBands') &&
158
+ bandCertification.satisfied &&
159
+ bandCertification.offerable.length > 0,
160
+ signalRead: hasProperty(modem, 'SignalQuality') || signal !== undefined,
161
+ simRead: hasProperty(modem, 'Sim') || hasProperty(modem, 'SimSlots'),
162
+ multiSim: Array.isArray(slots) && slots.filter((entry) => entry !== '/').length > 1,
163
+ location: interfaceNames.has(MODEM_LOCATION_IFACE),
164
+ sms: interfaceNames.has(MESSAGING_IFACE),
165
+ ussd: interfaceNames.has(MODEM3GPP_USSD_IFACE),
166
+ powerRead: hasProperty(modem, 'PowerState'),
167
+ };
168
+ const [allowed, preferred] = tuple(propValue(modem, 'CurrentModes'));
169
+ const modeTruth = readRadioModeTruth({
170
+ currentModes: structValue(propValue(modem, 'CurrentModes')),
171
+ supportedModes: structValue(propValue(modem, 'SupportedModes')),
172
+ });
173
+ const normalizationContext = {
174
+ stableKey: stableKeyFromPhysicalModemId(context.physicalModemId),
175
+ generation: context.generation,
176
+ sourceEpoch: sourceEpoch(epoch),
177
+ observedAt: epochMillis(now()),
178
+ };
179
+ // The struct properties are handed to normalization VERBATIM — `CurrentModes` as its
180
+ // `(uu)` pair, `SignalQuality` as its `(ub)` pair. Flattening them here (which this
181
+ // did) kept the first member and dropped the preferred mode and the recency flag
182
+ // before the diagnostics block ever saw them, which is a drop no downstream layer
183
+ // can undo. The normalizer reads either shape.
184
+ const modemRaw = propsRecord(modem) ?? {};
185
+ return {
186
+ modemPath,
187
+ capabilities,
188
+ bandCertification,
189
+ radio: {
190
+ current: { allowed, preferred },
191
+ supported: modePairs(propValue(modem, 'SupportedModes')),
192
+ truth: modeTruth,
193
+ },
194
+ signal: signalState(modem, signal),
195
+ sim: simState(tree, modem),
196
+ power: radioPower(propValue(modem, 'PowerState')),
197
+ observation: normalizeModemManagerObservation({
198
+ modem: modemRaw,
199
+ ...(modem3gpp === undefined ? {} : { modem3gpp: propsRecord(modem3gpp) ?? {} }),
200
+ ...(sim === undefined ? {} : { sim: propsRecord(sim) ?? {} }),
201
+ ...(signal === undefined ? {} : { signal: propsRecord(signal) ?? {} }),
202
+ }, normalizationContext),
203
+ };
204
+ }
@@ -0,0 +1,135 @@
1
+ import type { DesiredRadio, ObservationEnvelope, OperationDescriptor, OperationResult, RadioPower } from '../../domain/index.js';
2
+ import type { SimPresence, SimPresenceEvidence } from '../../hardware/router-parsers.js';
3
+ import type { GnssFixState } from '../../location/index.js';
4
+ import type { NormalizedModemObservation } from '../../observations/index.js';
5
+ import type { FixRead, GnssSource, LocationStatusResult, LocationToggleResult, ModemBands, ObservationList, ObservationListener, SmsObservationPort } from '../../ports/index.js';
6
+ import type { BandWriteCertification, ModeSelection, RadioModeTruth } from '../../radio/index.js';
7
+ import type { MmUssd, UssdVerbResult } from '../../ussd/index.js';
8
+ import type { ProviderExecutionContext, ProviderOperationsSurface } from '../contracts.js';
9
+ import type { ModemManagerRefusalReason } from './errors.js';
10
+ export type ModemManagerCapabilities = {
11
+ readonly modeRead: boolean;
12
+ readonly modeWrite: boolean;
13
+ readonly bandRead: boolean;
14
+ readonly bandWrite: boolean;
15
+ readonly signalRead: boolean;
16
+ readonly simRead: boolean;
17
+ readonly multiSim: boolean;
18
+ readonly location: boolean;
19
+ readonly sms: boolean;
20
+ readonly ussd: boolean;
21
+ readonly powerRead: boolean;
22
+ };
23
+ export type ModemManagerRadioState = {
24
+ readonly current: {
25
+ readonly allowed: number;
26
+ readonly preferred: number;
27
+ };
28
+ readonly supported: readonly {
29
+ readonly allowed: number;
30
+ readonly preferred: number;
31
+ }[];
32
+ /**
33
+ * The SAME properties decoded without loss — `preferred: 'none'` stays `none`, an
34
+ * unfamiliar mode bit stays offerable as `unknown-combination`, and a member that
35
+ * is not a `(uu)` lands in `undecodable` rather than vanishing. `supported` above
36
+ * is the pre-existing raw-mask view and DROPS a zero-allowed member; this one does
37
+ * not, which is the whole point of carrying both.
38
+ */
39
+ readonly truth: RadioModeTruth;
40
+ };
41
+ export type ModemManagerSignalState = {
42
+ readonly quality?: number;
43
+ readonly recent?: boolean;
44
+ readonly extendedAvailable: boolean;
45
+ };
46
+ export type ModemManagerSimState = {
47
+ /**
48
+ * The modem exports an ACTIVE SIM object path. `true` is positive evidence of a
49
+ * SIM; `false` is NOT evidence of absence — read `presence` for that.
50
+ */
51
+ readonly present: boolean;
52
+ /** `absent` only ever comes from `StateFailedReason: sim-missing`. */
53
+ readonly presence: SimPresence;
54
+ readonly presenceEvidence: SimPresenceEvidence;
55
+ readonly slotCount: number;
56
+ readonly primarySlot: number;
57
+ readonly lockRequired?: number;
58
+ readonly simType?: number;
59
+ readonly esimStatus?: number;
60
+ };
61
+ export type ModemManagerProviderSnapshot = {
62
+ readonly modemPath: string;
63
+ readonly capabilities: ModemManagerCapabilities;
64
+ readonly radio: ModemManagerRadioState;
65
+ /** The band-lock gate for THIS device, resolved from the shipped certification catalog. */
66
+ readonly bandCertification: BandWriteCertification;
67
+ readonly signal: ModemManagerSignalState;
68
+ readonly sim: ModemManagerSimState;
69
+ readonly power: RadioPower;
70
+ readonly observation: ObservationEnvelope<NormalizedModemObservation>;
71
+ };
72
+ export type ModemManagerSnapshotResult = ({
73
+ readonly ok: true;
74
+ } & ModemManagerProviderSnapshot) | {
75
+ readonly ok: false;
76
+ readonly reason: ModemManagerRefusalReason;
77
+ };
78
+ export interface ContextReadOperation<O> {
79
+ readonly descriptor: OperationDescriptor<never, O>;
80
+ read(context: ProviderExecutionContext): Promise<OperationResult<O>>;
81
+ }
82
+ export interface ContextWriteOperation<I, O> {
83
+ /** The shape-only descriptor: what this operation is, before any device answered. */
84
+ readonly descriptor: OperationDescriptor<I, O>;
85
+ /**
86
+ * The descriptor as the LIVE device entitles it.
87
+ *
88
+ * A static descriptor cannot carry a device's own catalog, so it cannot say which
89
+ * mode combinations this modem advertises or whether this SKU's band lock is
90
+ * certified. Those are exactly the facts a consumer needs before it offers a
91
+ * control, so they are read here instead of being inferred from a capability flag.
92
+ */
93
+ describe(context: ProviderExecutionContext): Promise<OperationDescriptor<I, O>>;
94
+ read(context: ProviderExecutionContext): Promise<OperationResult<O>>;
95
+ write(context: ProviderExecutionContext, input: I): Promise<OperationResult<O>>;
96
+ }
97
+ export type SmsPortResult = {
98
+ readonly ok: true;
99
+ readonly port: SmsObservationPort;
100
+ } | {
101
+ readonly ok: false;
102
+ readonly reason: ModemManagerRefusalReason;
103
+ };
104
+ export interface ModemManagerProviderOperations extends ProviderOperationsSurface {
105
+ readonly radio: ContextWriteOperation<DesiredRadio, ModemManagerRadioState>;
106
+ /**
107
+ * The modem's OWN mode vocabulary — an advertised `(allowed, preferred)` pair,
108
+ * selected verbatim. Separate from `radio` because `DesiredRadio` is an ordered RAT
109
+ * preference and cannot express `preferred: none`, which real hardware advertises.
110
+ */
111
+ readonly modes: ContextWriteOperation<ModeSelection, RadioModeTruth>;
112
+ readonly bands: ContextWriteOperation<readonly string[], ModemBands>;
113
+ readonly signal: ContextReadOperation<ModemManagerSignalState>;
114
+ readonly sim: ContextReadOperation<ModemManagerSimState>;
115
+ readonly power: ContextReadOperation<RadioPower>;
116
+ readonly location: {
117
+ status(context: ProviderExecutionContext): Promise<LocationStatusResult>;
118
+ enable(context: ProviderExecutionContext, sources: readonly GnssSource[]): Promise<LocationToggleResult>;
119
+ disable(context: ProviderExecutionContext): Promise<LocationToggleResult>;
120
+ readFix(context: ProviderExecutionContext): Promise<FixRead>;
121
+ state(context: ProviderExecutionContext): GnssFixState;
122
+ tick(context: ProviderExecutionContext): GnssFixState;
123
+ };
124
+ sms(context: ProviderExecutionContext): Promise<SmsPortResult>;
125
+ readonly ussd: MmUssd;
126
+ initiateUssd(context: ProviderExecutionContext, ussdCommand: string): Promise<UssdVerbResult>;
127
+ respondUssd(context: ProviderExecutionContext, ussdResponse: string): Promise<UssdVerbResult>;
128
+ cancelUssd(context: ProviderExecutionContext): Promise<UssdVerbResult>;
129
+ readonly fccCoverage: (vid: string | undefined, pid: string | undefined) => 'present' | 'absent' | 'unknown';
130
+ }
131
+ export interface ModemManagerProviderLifecycle {
132
+ start(): Promise<ObservationList>;
133
+ observe(listener: ObservationListener): () => void;
134
+ stop(): Promise<void>;
135
+ }
@@ -0,0 +1 @@
1
+ export {};