@ceralive/modem-control 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (480) hide show
  1. package/README.md +341 -0
  2. package/dist/backend/at-lease.d.ts +59 -0
  3. package/dist/backend/at-lease.js +118 -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 +101 -0
  61. package/dist/backend/transition-preconditions.js +132 -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 +216 -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 +20 -0
  146. package/dist/index.js +27 -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 +61 -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/operation-ids.d.ts +2 -0
  196. package/dist/operation-ids.js +26 -0
  197. package/dist/operations/contracts.d.ts +72 -0
  198. package/dist/operations/contracts.js +1 -0
  199. package/dist/operations/index.d.ts +1 -0
  200. package/dist/operations/index.js +1 -0
  201. package/dist/operations/operation-engine.d.ts +16 -0
  202. package/dist/operations/operation-engine.js +194 -0
  203. package/dist/ports/index.d.ts +12 -0
  204. package/{src/ports/index.ts → dist/ports/index.js} +12 -8
  205. package/dist/ports/location.d.ts +87 -0
  206. package/dist/ports/location.js +36 -0
  207. package/dist/ports/modem-manager.d.ts +89 -0
  208. package/dist/ports/modem-manager.js +9 -0
  209. package/dist/ports/mutation-admission.d.ts +27 -0
  210. package/dist/ports/mutation-admission.js +9 -0
  211. package/dist/ports/network-manager.d.ts +68 -0
  212. package/dist/ports/network-manager.js +13 -0
  213. package/{src/ports/observation.ts → dist/ports/observation.d.ts} +16 -27
  214. package/dist/ports/observation.js +7 -0
  215. package/dist/ports/ops.d.ts +44 -0
  216. package/dist/ports/ops.js +16 -0
  217. package/{src/ports/receipts.ts → dist/ports/receipts.d.ts} +5 -27
  218. package/dist/ports/receipts.js +9 -0
  219. package/dist/ports/reconcile.d.ts +33 -0
  220. package/dist/ports/reconcile.js +200 -0
  221. package/dist/ports/resource-ownership.d.ts +29 -0
  222. package/dist/ports/resource-ownership.js +1 -0
  223. package/dist/ports/router.d.ts +19 -0
  224. package/dist/ports/router.js +7 -0
  225. package/dist/ports/sms.d.ts +64 -0
  226. package/dist/ports/sms.js +24 -0
  227. package/dist/ports/uhubctl.d.ts +6 -0
  228. package/dist/ports/uhubctl.js +1 -0
  229. package/dist/providers/contracts.d.ts +124 -0
  230. package/dist/providers/contracts.js +10 -0
  231. package/dist/providers/huawei-hilink/index.d.ts +2 -0
  232. package/dist/providers/huawei-hilink/index.js +2 -0
  233. package/dist/providers/huawei-hilink/operations.d.ts +20 -0
  234. package/dist/providers/huawei-hilink/operations.js +56 -0
  235. package/dist/providers/huawei-hilink/provider.d.ts +52 -0
  236. package/dist/providers/huawei-hilink/provider.js +76 -0
  237. package/dist/providers/huawei-hilink/runtime.d.ts +22 -0
  238. package/dist/providers/huawei-hilink/runtime.js +171 -0
  239. package/dist/providers/huawei-hilink/session.d.ts +28 -0
  240. package/dist/providers/huawei-hilink/session.js +120 -0
  241. package/dist/providers/huawei-hilink/transport.d.ts +19 -0
  242. package/dist/providers/huawei-hilink/transport.js +1 -0
  243. package/dist/providers/index.d.ts +8 -0
  244. package/dist/providers/index.js +8 -0
  245. package/dist/providers/matcher.d.ts +3 -0
  246. package/dist/providers/matcher.js +205 -0
  247. package/dist/providers/modem-manager/errors.d.ts +7 -0
  248. package/dist/providers/modem-manager/errors.js +37 -0
  249. package/dist/providers/modem-manager/generic-operations.d.ts +10 -0
  250. package/dist/providers/modem-manager/generic-operations.js +209 -0
  251. package/dist/providers/modem-manager/index.d.ts +4 -0
  252. package/dist/providers/modem-manager/index.js +4 -0
  253. package/dist/providers/modem-manager/module-operations.d.ts +21 -0
  254. package/dist/providers/modem-manager/module-operations.js +118 -0
  255. package/dist/providers/modem-manager/provider.d.ts +41 -0
  256. package/dist/providers/modem-manager/provider.js +155 -0
  257. package/dist/providers/modem-manager/runtime-composition-operation.d.ts +32 -0
  258. package/dist/providers/modem-manager/runtime-composition-operation.js +151 -0
  259. package/dist/providers/modem-manager/snapshot.d.ts +5 -0
  260. package/dist/providers/modem-manager/snapshot.js +204 -0
  261. package/dist/providers/modem-manager/types.d.ts +137 -0
  262. package/dist/providers/modem-manager/types.js +1 -0
  263. package/dist/providers/network-manager/adapter.d.ts +71 -0
  264. package/dist/providers/network-manager/adapter.js +348 -0
  265. package/dist/providers/network-manager/index.d.ts +2 -0
  266. package/dist/providers/network-manager/index.js +2 -0
  267. package/dist/providers/network-manager/types.d.ts +171 -0
  268. package/dist/providers/network-manager/types.js +77 -0
  269. package/dist/providers/registry.d.ts +13 -0
  270. package/dist/providers/registry.js +33 -0
  271. package/dist/providers/ufi-himi/index.d.ts +6 -0
  272. package/dist/providers/ufi-himi/index.js +6 -0
  273. package/dist/providers/ufi-himi/operations.d.ts +41 -0
  274. package/dist/providers/ufi-himi/operations.js +66 -0
  275. package/dist/providers/ufi-himi/prohibitions.d.ts +62 -0
  276. package/dist/providers/ufi-himi/prohibitions.js +88 -0
  277. package/dist/providers/ufi-himi/provider.d.ts +41 -0
  278. package/dist/providers/ufi-himi/provider.js +204 -0
  279. package/dist/providers/ufi-himi/qualcomm-evidence.d.ts +32 -0
  280. package/dist/providers/ufi-himi/qualcomm-evidence.js +51 -0
  281. package/dist/providers/ufi-himi/session.d.ts +46 -0
  282. package/dist/providers/ufi-himi/session.js +92 -0
  283. package/dist/providers/ufi-himi/transport.d.ts +29 -0
  284. package/dist/providers/ufi-himi/transport.js +25 -0
  285. package/dist/providers/zte-goform/index.d.ts +2 -0
  286. package/dist/providers/zte-goform/index.js +2 -0
  287. package/dist/providers/zte-goform/provider.d.ts +56 -0
  288. package/dist/providers/zte-goform/provider.js +101 -0
  289. package/dist/providers/zte-goform/session.d.ts +23 -0
  290. package/dist/providers/zte-goform/session.js +197 -0
  291. package/dist/providers/zte-goform/transport.d.ts +16 -0
  292. package/dist/providers/zte-goform/transport.js +1 -0
  293. package/dist/radio/band-truth.d.ts +50 -0
  294. package/dist/radio/band-truth.js +92 -0
  295. package/dist/radio/index.d.ts +3 -0
  296. package/dist/radio/index.js +10 -0
  297. package/dist/radio/mode-combinations.d.ts +88 -0
  298. package/dist/radio/mode-combinations.js +198 -0
  299. package/dist/radio/mode-truth.d.ts +67 -0
  300. package/dist/radio/mode-truth.js +112 -0
  301. package/dist/redact.d.ts +15 -0
  302. package/dist/redact.js +189 -0
  303. package/dist/safety/composition-root.d.ts +28 -0
  304. package/dist/safety/composition-root.js +65 -0
  305. package/dist/safety/flock-resource-ownership.d.ts +11 -0
  306. package/dist/safety/flock-resource-ownership.js +138 -0
  307. package/dist/safety/index.d.ts +2 -0
  308. package/dist/safety/index.js +2 -0
  309. package/dist/sms/dbus-messaging.d.ts +17 -0
  310. package/dist/sms/dbus-messaging.js +185 -0
  311. package/dist/sms/inbox-store.d.ts +10 -0
  312. package/dist/sms/inbox-store.js +82 -0
  313. package/dist/sms/index.d.ts +4 -0
  314. package/dist/sms/index.js +10 -0
  315. package/dist/sms/mmcli-parse.d.ts +54 -0
  316. package/dist/sms/mmcli-parse.js +224 -0
  317. package/dist/sms/normalize.d.ts +42 -0
  318. package/dist/sms/normalize.js +95 -0
  319. package/dist/testing/domain-fakes.d.ts +58 -0
  320. package/dist/testing/domain-fakes.js +98 -0
  321. package/dist/testing/index.d.ts +2 -0
  322. package/dist/testing/index.js +17 -0
  323. package/dist/testing/provider-fakes.d.ts +45 -0
  324. package/dist/testing/provider-fakes.js +64 -0
  325. package/dist/transport/calls.d.ts +9 -0
  326. package/dist/transport/calls.js +88 -0
  327. package/dist/transport/codec.d.ts +3 -0
  328. package/dist/transport/codec.js +207 -0
  329. package/dist/transport/dbus-native.d.ts +57 -0
  330. package/dist/transport/dbus-native.js +17 -0
  331. package/dist/transport/errors.d.ts +21 -0
  332. package/{src/transport/errors.ts → dist/transport/errors.js} +34 -46
  333. package/dist/transport/index.d.ts +4 -0
  334. package/dist/transport/index.js +9 -0
  335. package/dist/transport/signals.d.ts +15 -0
  336. package/dist/transport/signals.js +123 -0
  337. package/dist/transport/signature.d.ts +7 -0
  338. package/dist/transport/signature.js +94 -0
  339. package/dist/transport/transport.d.ts +2 -0
  340. package/dist/transport/transport.js +202 -0
  341. package/dist/transport/types.d.ts +61 -0
  342. package/dist/transport/types.js +19 -0
  343. package/dist/usb-mode/catalog-schema.d.ts +139 -0
  344. package/dist/usb-mode/catalog-schema.js +97 -0
  345. package/dist/usb-mode/catalog.d.ts +21 -0
  346. package/{src/usb-mode/catalog.ts → dist/usb-mode/catalog.js} +10 -32
  347. package/dist/usb-mode/certified-catalog.json +67 -0
  348. package/dist/usb-mode/index.d.ts +6 -0
  349. package/dist/usb-mode/index.js +16 -0
  350. package/dist/usb-mode/ingestion.d.ts +111 -0
  351. package/dist/usb-mode/ingestion.js +187 -0
  352. package/dist/usb-mode/promotion-review.d.ts +21 -0
  353. package/dist/usb-mode/promotion-review.js +87 -0
  354. package/dist/usb-mode/runtime-capability.d.ts +59 -0
  355. package/dist/usb-mode/runtime-capability.js +157 -0
  356. package/dist/usb-mode/usb-devices-parse.d.ts +36 -0
  357. package/dist/usb-mode/usb-devices-parse.js +157 -0
  358. package/dist/ussd/calls.d.ts +32 -0
  359. package/dist/ussd/calls.js +96 -0
  360. package/dist/ussd/index.d.ts +5 -0
  361. package/dist/ussd/index.js +12 -0
  362. package/dist/ussd/mm-ussd.d.ts +37 -0
  363. package/dist/ussd/mm-ussd.js +205 -0
  364. package/dist/ussd/refusal.d.ts +53 -0
  365. package/dist/ussd/refusal.js +154 -0
  366. package/dist/ussd/registration.d.ts +20 -0
  367. package/dist/ussd/registration.js +101 -0
  368. package/dist/ussd/session.d.ts +106 -0
  369. package/dist/ussd/session.js +163 -0
  370. package/package.json +38 -4
  371. package/src/backend/at-lease.test.ts +0 -106
  372. package/src/backend/at-lease.ts +0 -158
  373. package/src/backend/cell-info.test.ts +0 -154
  374. package/src/backend/cell-info.ts +0 -160
  375. package/src/backend/device-classifier.test.ts +0 -168
  376. package/src/backend/device-classifier.ts +0 -248
  377. package/src/backend/enrichment.ts +0 -96
  378. package/src/backend/features.test.ts +0 -162
  379. package/src/backend/features.ts +0 -179
  380. package/src/backend/identity-ladder.test.ts +0 -117
  381. package/src/backend/identity-ladder.ts +0 -221
  382. package/src/backend/identity-registry.test.ts +0 -89
  383. package/src/backend/identity-registry.ts +0 -151
  384. package/src/backend/index.ts +0 -236
  385. package/src/backend/lifecycle-interlock.ts +0 -38
  386. package/src/backend/managed-objects.ts +0 -108
  387. package/src/backend/mapping.ts +0 -160
  388. package/src/backend/mm-backend.ts +0 -191
  389. package/src/backend/mm-mutations.ts +0 -228
  390. package/src/backend/modem-actor.test.ts +0 -95
  391. package/src/backend/modem-actor.ts +0 -112
  392. package/src/backend/nm-auto-apn.ts +0 -161
  393. package/src/backend/nm-gsm-fields.ts +0 -122
  394. package/src/backend/nmcli-nm-port.ts +0 -228
  395. package/src/backend/nmcli-runner.ts +0 -52
  396. package/src/backend/observer.ts +0 -297
  397. package/src/backend/power-contract.test.ts +0 -40
  398. package/src/backend/power-contract.ts +0 -83
  399. package/src/backend/recovery-attribution.test.ts +0 -102
  400. package/src/backend/recovery-attribution.ts +0 -86
  401. package/src/backend/recovery-budget.test.ts +0 -64
  402. package/src/backend/recovery-budget.ts +0 -84
  403. package/src/backend/recovery-ladder.test.ts +0 -257
  404. package/src/backend/recovery-ladder.ts +0 -249
  405. package/src/backend/router-ethernet.test.ts +0 -71
  406. package/src/backend/router-ethernet.ts +0 -90
  407. package/src/backend/row-store.ts +0 -105
  408. package/src/backend/signal-setup.ts +0 -112
  409. package/src/backend/sim-unlock.ts +0 -193
  410. package/src/backend/transition-preconditions.ts +0 -149
  411. package/src/backend/uhubctl-power-hook.test.ts +0 -274
  412. package/src/backend/uhubctl-power-hook.ts +0 -377
  413. package/src/backend/usage/accounting.test.ts +0 -147
  414. package/src/backend/usage/accounting.ts +0 -123
  415. package/src/backend/usage/billing-cycle.test.ts +0 -62
  416. package/src/backend/usage/billing-cycle.ts +0 -45
  417. package/src/backend/usage/index.ts +0 -60
  418. package/src/backend/usage/policy-store.test.ts +0 -164
  419. package/src/backend/usage/policy-store.ts +0 -216
  420. package/src/backend/usage/policy-write.test.ts +0 -198
  421. package/src/backend/usage/policy-write.ts +0 -207
  422. package/src/backend/usage/proc-net-dev.test.ts +0 -56
  423. package/src/backend/usage/sampler.test.ts +0 -327
  424. package/src/backend/usage/sampler.ts +0 -282
  425. package/src/backend/usage/store.test.ts +0 -148
  426. package/src/backend/usage/store.ts +0 -177
  427. package/src/backend/usb-enumerator.test.ts +0 -87
  428. package/src/backend/usb-enumerator.ts +0 -181
  429. package/src/backend/usb-mode-transition.test.ts +0 -323
  430. package/src/backend/usb-mode-transition.ts +0 -253
  431. package/src/domain/brand.ts +0 -29
  432. package/src/domain/errors.ts +0 -77
  433. package/src/domain/guards.test.ts +0 -218
  434. package/src/domain/guards.ts +0 -144
  435. package/src/domain/identity.test.ts +0 -83
  436. package/src/domain/identity.ts +0 -165
  437. package/src/domain/index.ts +0 -12
  438. package/src/domain/snapshot.test.ts +0 -266
  439. package/src/domain/snapshot.ts +0 -120
  440. package/src/index.test.ts +0 -6
  441. package/src/index.ts +0 -15
  442. package/src/ports/README.md +0 -61
  443. package/src/ports/forbidden-surface.test.ts +0 -241
  444. package/src/ports/modem-manager.ts +0 -72
  445. package/src/ports/network-manager.ts +0 -87
  446. package/src/ports/ops.ts +0 -60
  447. package/src/ports/ops.type-test.ts +0 -39
  448. package/src/ports/receipts.test.ts +0 -153
  449. package/src/ports/reconcile.test.ts +0 -152
  450. package/src/ports/reconcile.ts +0 -338
  451. package/src/ports/router.ts +0 -29
  452. package/src/redact.test.ts +0 -82
  453. package/src/redact.ts +0 -73
  454. package/src/transport/README.md +0 -65
  455. package/src/transport/calls.ts +0 -113
  456. package/src/transport/characterization.test.ts +0 -260
  457. package/src/transport/codec.test.ts +0 -118
  458. package/src/transport/codec.ts +0 -240
  459. package/src/transport/conformance-python.test.ts +0 -152
  460. package/src/transport/conformance-same-lib.test.ts +0 -115
  461. package/src/transport/dbus-native-lib.d.ts +0 -19
  462. package/src/transport/dbus-native.ts +0 -85
  463. package/src/transport/index.ts +0 -30
  464. package/src/transport/no-library-leak.test.ts +0 -61
  465. package/src/transport/reliability.test.ts +0 -173
  466. package/src/transport/signals.ts +0 -150
  467. package/src/transport/signature.ts +0 -110
  468. package/src/transport/test-support/fake-service.ts +0 -168
  469. package/src/transport/test-support/independent-producer.py +0 -110
  470. package/src/transport/test-support/private-bus.ts +0 -66
  471. package/src/transport/transport.ts +0 -250
  472. package/src/transport/types.ts +0 -118
  473. package/src/usb-mode/catalog-schema.test.ts +0 -181
  474. package/src/usb-mode/catalog-schema.ts +0 -113
  475. package/src/usb-mode/certified-catalog.json +0 -67
  476. package/src/usb-mode/index.ts +0 -58
  477. package/src/usb-mode/ingestion.test.ts +0 -268
  478. package/src/usb-mode/ingestion.ts +0 -297
  479. package/src/usb-mode/promotion-review.ts +0 -117
  480. package/src/usb-mode/usb-devices-parse.ts +0 -196
@@ -0,0 +1,155 @@
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
+ ...(options.runtimeComposition === undefined
64
+ ? {}
65
+ : { runtimeComposition: options.runtimeComposition }),
66
+ }),
67
+ ...this.#moduleOperations.operations,
68
+ };
69
+ this.definition = {
70
+ id: 'modemmanager',
71
+ profileVersion: '1',
72
+ eligibleTransports: ['modemmanager'],
73
+ passiveMatchers: [],
74
+ unauthenticatedProbes: [
75
+ { id: 'object-manager-modem', run: (request) => this.#probe(request) },
76
+ ],
77
+ capabilityReaders: [
78
+ { id: 'runtime-modem-interface', read: (context) => this.#capability(context) },
79
+ ],
80
+ observe: (context) => this.#observeNormalized(context),
81
+ operations: () => operations,
82
+ contractFixtures: [],
83
+ };
84
+ }
85
+ start() {
86
+ this.#startPromise ??= this.#backend.start();
87
+ return this.#startPromise;
88
+ }
89
+ observe(listener) {
90
+ return this.#backend.observe(listener);
91
+ }
92
+ async stop() {
93
+ await this.#moduleOperations.stop();
94
+ await this.#backend.stop();
95
+ }
96
+ async readSnapshot(context) {
97
+ try {
98
+ await this.start();
99
+ const tree = await fetchManagedObjects(this.#transport, this.#destination);
100
+ const snapshot = buildModemManagerSnapshot(tree, context, this.#now, this.#epoch, this.#bandCertification(context));
101
+ if (snapshot === undefined)
102
+ return { ok: false, reason: 'not-found' };
103
+ this.#stableKeyByPath.set(snapshot.modemPath, `modem:${context.physicalModemId}`);
104
+ return { ok: true, ...snapshot };
105
+ }
106
+ catch (error) {
107
+ return { ok: false, reason: mapModemManagerError(error).reason };
108
+ }
109
+ }
110
+ #bandCertification(context) {
111
+ const sku = this.#bandSku(context);
112
+ return sku === undefined ? undefined : findBandCertification(this.#bandCatalog, sku);
113
+ }
114
+ async #probe(request) {
115
+ const snapshot = await this.readSnapshot({ ...request, profile: PROFILE });
116
+ return {
117
+ signal: snapshot.ok
118
+ ? 'match'
119
+ : snapshot.reason === 'not-found'
120
+ ? 'mismatch'
121
+ : 'unknown',
122
+ strength: 'strong',
123
+ profiles: [PROFILE],
124
+ detail: snapshot.ok ? 'runtime-modem-interface-present' : snapshot.reason,
125
+ };
126
+ }
127
+ async #capability(context) {
128
+ const snapshot = await this.readSnapshot(context);
129
+ return {
130
+ signal: snapshot.ok
131
+ ? 'match'
132
+ : snapshot.reason === 'not-found'
133
+ ? 'mismatch'
134
+ : 'unknown',
135
+ strength: 'strong',
136
+ detail: snapshot.ok ? 'generic-controls-derived-from-runtime-properties' : snapshot.reason,
137
+ };
138
+ }
139
+ async #observeNormalized(context) {
140
+ const snapshot = await this.readSnapshot(context);
141
+ if (snapshot.ok)
142
+ return [snapshot.observation];
143
+ return [
144
+ unavailableObservation('modemmanager', {
145
+ stableKey: stableKeyFromPhysicalModemId(context.physicalModemId),
146
+ generation: context.generation,
147
+ sourceEpoch: sourceEpoch(this.#epoch),
148
+ observedAt: epochMillis(this.#now()),
149
+ }, snapshot.reason === 'not-found' ? 'device-absent' : 'provider-unavailable'),
150
+ ];
151
+ }
152
+ }
153
+ export function createModemManagerProvider(options) {
154
+ return new ModemManagerProvider(options);
155
+ }
@@ -0,0 +1,32 @@
1
+ import { type AtCommandSender, type UsbModeTransitionOutcome } from '../../backend/index.js';
2
+ import { type RuntimeCompositionCapability, type RuntimeCompositionMode } from '../../usb-mode/index.js';
3
+ import type { ProviderExecutionContext } from '../contracts.js';
4
+ import type { ContextWriteOperation } from './types.js';
5
+ export declare const RUNTIME_COMPOSITION_SUPPRESSIONS: readonly ['unknown-vendor', 'no-return-path', 'blocked-by-state', 'provisioning-disabled'];
6
+ export type RuntimeCompositionSuppression = (typeof RUNTIME_COMPOSITION_SUPPRESSIONS)[number];
7
+ export type RuntimeCompositionState = {
8
+ readonly status: 'available';
9
+ readonly current: RuntimeCompositionMode;
10
+ readonly enumerated: readonly RuntimeCompositionMode[];
11
+ readonly offerable: readonly RuntimeCompositionMode[];
12
+ } | {
13
+ readonly status: 'suppressed';
14
+ readonly reason: RuntimeCompositionSuppression;
15
+ readonly detail: string;
16
+ readonly current: RuntimeCompositionMode | null;
17
+ readonly enumerated: readonly RuntimeCompositionMode[];
18
+ readonly offerable: readonly [];
19
+ };
20
+ export type RuntimeCompositionOperationDeps = {
21
+ readonly vendor: (context: ProviderExecutionContext) => string | Promise<string>;
22
+ readonly provisioningEnabled: (context: ProviderExecutionContext) => boolean | Promise<boolean>;
23
+ readonly blockedReason: (context: ProviderExecutionContext) => string | undefined | Promise<string | undefined>;
24
+ readonly atSender: AtCommandSender;
25
+ readonly transition: (context: ProviderExecutionContext, capability: Extract<RuntimeCompositionCapability, {
26
+ readonly status: 'available';
27
+ }>, target: RuntimeCompositionMode) => Promise<UsbModeTransitionOutcome>;
28
+ };
29
+ export interface RuntimeCompositionOperation extends ContextWriteOperation<RuntimeCompositionMode, RuntimeCompositionState> {
30
+ capability(context: ProviderExecutionContext): Promise<RuntimeCompositionState>;
31
+ }
32
+ export declare function createRuntimeCompositionOperation(deps: RuntimeCompositionOperationDeps | undefined): RuntimeCompositionOperation;
@@ -0,0 +1,151 @@
1
+ import { AtCommandLease, computeAtAllowlist, } from '../../backend/index.js';
2
+ import { classifyOperationCompletion, defineOperationDescriptor, } from '../../domain/index.js';
3
+ import { isRuntimeCompositionVendor, RUNTIME_COMPOSITION_QUERY_REGISTRY, resolveRuntimeCompositionCapability, } from '../../usb-mode/index.js';
4
+ export const RUNTIME_COMPOSITION_SUPPRESSIONS = [
5
+ 'unknown-vendor',
6
+ 'no-return-path',
7
+ 'blocked-by-state',
8
+ 'provisioning-disabled',
9
+ ];
10
+ const OPERATION_ID = 'modemmanager.usb-composition';
11
+ function suppressed(reason, detail, capability) {
12
+ return {
13
+ status: 'suppressed',
14
+ reason,
15
+ detail,
16
+ current: capability?.current ?? null,
17
+ enumerated: capability?.enumerated ?? [],
18
+ offerable: [],
19
+ };
20
+ }
21
+ function descriptorFor(state) {
22
+ const available = state.status === 'available';
23
+ return defineOperationDescriptor({
24
+ id: OPERATION_ID,
25
+ support: {
26
+ read: { supported: true },
27
+ write: available ? { supported: true } : { supported: false, reason: state.reason },
28
+ },
29
+ authority: 'provider',
30
+ provider: 'modemmanager',
31
+ constraints: {
32
+ kind: 'allowed-values',
33
+ values: available ? state.offerable : [],
34
+ },
35
+ livePreconditions: [
36
+ 'modem-present',
37
+ 'runtime-interface-present',
38
+ 'identity-confident',
39
+ 'streaming-interlock-open',
40
+ ],
41
+ availability: available ? { state: 'available' } : { state: 'refused', reason: state.reason },
42
+ mutationImpact: 'disruptive',
43
+ retryClass: 'never',
44
+ readback: {
45
+ required: true,
46
+ reason: 'device-reported-composition',
47
+ matches: (input, value) => value.status === 'available' && Object.is(input, value.current),
48
+ },
49
+ rollback: { required: true, reason: 'restore-previous-composition' },
50
+ journal: { required: true, reason: 'composition-reenumeration' },
51
+ admission: { required: true, reason: 'provider-mutation' },
52
+ evidence: { profiles: ['generic-mm'], firmware: [] },
53
+ confidence: available ? 'high' : 'unknown',
54
+ });
55
+ }
56
+ function result(context, completion) {
57
+ return classifyOperationCompletion({
58
+ operation: 'write',
59
+ completionGeneration: context.generation,
60
+ currentGeneration: context.generation,
61
+ completion,
62
+ });
63
+ }
64
+ function disabledOperation() {
65
+ const state = {
66
+ status: 'suppressed',
67
+ reason: 'provisioning-disabled',
68
+ detail: 'USB composition provisioning is not configured',
69
+ current: null,
70
+ enumerated: [],
71
+ offerable: [],
72
+ };
73
+ const descriptor = descriptorFor(state);
74
+ return {
75
+ descriptor,
76
+ describe: () => Promise.resolve(descriptor),
77
+ capability: () => Promise.resolve(state),
78
+ read: (context) => Promise.resolve(result(context, { status: 'applied', value: state })),
79
+ write: (context) => Promise.resolve(result(context, { status: 'refused', reason: state.reason })),
80
+ };
81
+ }
82
+ export function createRuntimeCompositionOperation(deps) {
83
+ if (deps === undefined)
84
+ return disabledOperation();
85
+ const capability = async (context) => {
86
+ const vendor = (await deps.vendor(context)).trim().toLowerCase();
87
+ if (!isRuntimeCompositionVendor(vendor))
88
+ return suppressed('unknown-vendor', `No reviewed composition query exists for ${vendor}`);
89
+ if (!(await deps.provisioningEnabled(context)))
90
+ return suppressed('provisioning-disabled', 'USB composition provisioning is disabled');
91
+ const blocked = await deps.blockedReason(context);
92
+ if (blocked !== undefined)
93
+ return suppressed('blocked-by-state', blocked);
94
+ const queries = RUNTIME_COMPOSITION_QUERY_REGISTRY[vendor];
95
+ const lease = new AtCommandLease({
96
+ sender: deps.atSender,
97
+ allowlist: computeAtAllowlist([]),
98
+ });
99
+ const current = await lease.run(queries.current);
100
+ const enumeration = await lease.run(queries.enumerate);
101
+ const resolved = resolveRuntimeCompositionCapability({
102
+ vendor,
103
+ currentResponse: current.raw,
104
+ enumerationResponse: enumeration.raw,
105
+ });
106
+ if (resolved.status === 'unknown')
107
+ return suppressed('no-return-path', resolved.reason, resolved);
108
+ if (!resolved.returnPathProven)
109
+ return suppressed('no-return-path', 'The current mode is absent from the device catalog', resolved);
110
+ return {
111
+ status: 'available',
112
+ current: resolved.current,
113
+ enumerated: resolved.enumerated,
114
+ offerable: resolved.offerable.filter((mode) => !Object.is(mode, resolved.current)),
115
+ };
116
+ };
117
+ return {
118
+ descriptor: descriptorFor(suppressed('provisioning-disabled', 'Capability has not been read for this device')),
119
+ describe: async (context) => descriptorFor(await capability(context)),
120
+ capability,
121
+ read: async (context) => result(context, { status: 'applied', value: await capability(context) }),
122
+ write: async (context, target) => {
123
+ const state = await capability(context);
124
+ if (state.status === 'suppressed')
125
+ return result(context, { status: 'refused', reason: state.reason });
126
+ if (!state.offerable.some((candidate) => Object.is(candidate, target)))
127
+ return result(context, { status: 'refused', reason: 'no-return-path' });
128
+ const fullCapability = {
129
+ status: 'available',
130
+ current: state.current,
131
+ enumerated: state.enumerated,
132
+ returnPathProven: true,
133
+ offerable: [state.current, ...state.offerable],
134
+ };
135
+ const outcome = await deps.transition(context, fullCapability, target);
136
+ if (outcome.status === 'refused')
137
+ return result(context, { status: 'refused', reason: 'blocked-by-state' });
138
+ if (outcome.status === 'failed')
139
+ return result(context, { status: 'failed', reason: outcome.reason });
140
+ return result(context, {
141
+ status: 'applied',
142
+ value: {
143
+ status: 'available',
144
+ current: target,
145
+ enumerated: state.enumerated,
146
+ offerable: state.enumerated.filter((mode) => !Object.is(mode, target)),
147
+ },
148
+ });
149
+ },
150
+ };
151
+ }
@@ -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
+ }