@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,120 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { parseHilinkSession, parseHilinkUserState, parseHilinkXmlValue, } from '../../hardware/router-parsers.js';
3
+ import { HILINK_PATHS } from './provider.js';
4
+ function base64(value) {
5
+ return Buffer.from(value).toString('base64');
6
+ }
7
+ function derivePassword(profile, username, password, token) {
8
+ if (profile.passwordType === 3)
9
+ return base64(password);
10
+ const passwordHash = base64(createHash('sha256').update(password).digest('hex'));
11
+ const loginHash = createHash('sha256').update(`${username}${passwordHash}${token}`).digest('hex');
12
+ return base64(loginHash);
13
+ }
14
+ function escapeXml(value) {
15
+ return value
16
+ .replace(/&/g, '&')
17
+ .replace(/</g, '&lt;')
18
+ .replace(/>/g, '&gt;')
19
+ .replace(/"/g, '&quot;')
20
+ .replace(/'/g, '&apos;');
21
+ }
22
+ function loginDocument(profile, username, password, token) {
23
+ return `<?xml version="1.0" encoding="UTF-8"?><request><Username>${escapeXml(username)}</Username><Password>${derivePassword(profile, username, password, token)}</Password><password_type>${profile.passwordType}</password_type></request>`;
24
+ }
25
+ function responseHeader(response, name) {
26
+ const expected = name.toLowerCase();
27
+ return Object.entries(response.headers ?? {}).find(([key]) => key.toLowerCase() === expected)?.[1];
28
+ }
29
+ function loggedInSession(response, fallback) {
30
+ const cookie = responseHeader(response, 'set-cookie')?.split(';', 1)[0]?.trim() ?? fallback.cookie;
31
+ const tokenHeader = responseHeader(response, '__requestverificationtoken') ??
32
+ responseHeader(response, '__requestverificationtokenone');
33
+ const token = tokenHeader?.split('#', 1)[0]?.trim() ?? fallback.token;
34
+ return { cookie, token };
35
+ }
36
+ export class HilinkSessionRuntime {
37
+ options;
38
+ #authenticated = new Map();
39
+ #serial = new Map();
40
+ constructor(options) {
41
+ this.options = options;
42
+ }
43
+ async withDevice(context, run) {
44
+ const key = String(context.physicalModemId);
45
+ const prior = this.#serial.get(key) ?? Promise.resolve();
46
+ let release = () => { };
47
+ const current = new Promise((resolve) => {
48
+ release = resolve;
49
+ });
50
+ const chain = prior.then(() => current);
51
+ this.#serial.set(key, chain);
52
+ await prior;
53
+ try {
54
+ return await run();
55
+ }
56
+ finally {
57
+ release();
58
+ if (this.#serial.get(key) === chain)
59
+ this.#serial.delete(key);
60
+ }
61
+ }
62
+ sessionKey(context) {
63
+ return `${context.physicalModemId}:${context.generation}`;
64
+ }
65
+ cachedSession(context) {
66
+ return this.#authenticated.get(this.sessionKey(context));
67
+ }
68
+ async authenticate(context, profile, cache) {
69
+ const sessionResponse = await this.request('GET', HILINK_PATHS.session);
70
+ if (sessionResponse.status === 401 || sessionResponse.status === 403)
71
+ return { status: 'refused', reason: 'auth-expired' };
72
+ if (sessionResponse.status !== 200)
73
+ return { status: 'refused', reason: 'unreachable' };
74
+ const initial = parseHilinkSession(sessionResponse.body);
75
+ if (initial === undefined)
76
+ return { status: 'refused', reason: 'malformed' };
77
+ const stateResponse = await this.getResponse(HILINK_PATHS.loginState, initial);
78
+ if (this.authExpired(stateResponse))
79
+ return { status: 'refused', reason: 'auth-expired' };
80
+ const state = parseHilinkUserState(stateResponse.body);
81
+ if (state === undefined)
82
+ return { status: 'refused', reason: 'malformed' };
83
+ if (state.passwordType !== profile.passwordType)
84
+ return { status: 'refused', reason: 'profile-mismatch' };
85
+ const login = await this.postResponse(HILINK_PATHS.login, loginDocument(profile, this.options.credentials.username, this.options.credentials.password, initial.token), initial);
86
+ if (this.authExpired(login))
87
+ return { status: 'refused', reason: 'auth-expired' };
88
+ if (login.status !== 200 || !/<response>\s*OK\s*<\/response>/i.test(login.body))
89
+ return { status: 'refused', reason: 'http-failure' };
90
+ const session = loggedInSession(login, initial);
91
+ if (cache)
92
+ this.#authenticated.set(this.sessionKey(context), session);
93
+ return { status: 'ready', session };
94
+ }
95
+ authExpired(response) {
96
+ return (response.status === 401 ||
97
+ response.status === 403 ||
98
+ parseHilinkXmlValue(response.body, 'code') === '125002');
99
+ }
100
+ getResponse(path, session) {
101
+ return this.request('GET', path, undefined, [`Cookie: ${session.cookie}`]);
102
+ }
103
+ postResponse(path, body, session) {
104
+ return this.request('POST', path, body, [
105
+ `Cookie: ${session.cookie}`,
106
+ `__RequestVerificationToken: ${session.token}`,
107
+ 'Content-Type: application/xml',
108
+ ]);
109
+ }
110
+ request(method, path, body, headers = []) {
111
+ return this.options.transport.request({
112
+ method,
113
+ url: `${this.options.adminUrl}${path}`,
114
+ ...(body === undefined ? {} : { body }),
115
+ headers,
116
+ interfaceName: this.options.interfaceName,
117
+ redirect: 'error',
118
+ });
119
+ }
120
+ }
@@ -0,0 +1,19 @@
1
+ export type HilinkHttpRequest = {
2
+ readonly method: 'GET' | 'POST';
3
+ readonly url: string;
4
+ readonly body?: string;
5
+ readonly headers: readonly string[];
6
+ readonly interfaceName: string;
7
+ readonly redirect: 'error';
8
+ };
9
+ export type HilinkHttpResponse = {
10
+ readonly status: number;
11
+ readonly body: string;
12
+ readonly headers?: Readonly<Record<string, string>>;
13
+ };
14
+ export interface HilinkTransport {
15
+ request(request: HilinkHttpRequest): Promise<HilinkHttpResponse>;
16
+ }
17
+ export type HilinkReplayExchange = HilinkHttpRequest & {
18
+ readonly response: HilinkHttpResponse;
19
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ export * from './contracts.js';
2
+ export * from './huawei-hilink/index.js';
3
+ export * from './matcher.js';
4
+ export * from './modem-manager/index.js';
5
+ export * from './network-manager/index.js';
6
+ export * from './registry.js';
7
+ export * from './ufi-himi/index.js';
8
+ export * from './zte-goform/index.js';
@@ -0,0 +1,8 @@
1
+ export * from './contracts.js';
2
+ export * from './huawei-hilink/index.js';
3
+ export * from './matcher.js';
4
+ export * from './modem-manager/index.js';
5
+ export * from './network-manager/index.js';
6
+ export * from './registry.js';
7
+ export * from './ufi-himi/index.js';
8
+ export * from './zte-goform/index.js';
@@ -0,0 +1,3 @@
1
+ import type { ProviderMatcher } from './contracts.js';
2
+ import type { ProviderRegistry } from './registry.js';
3
+ export declare function createProviderMatcher(registry: ProviderRegistry): ProviderMatcher;
@@ -0,0 +1,205 @@
1
+ const STRENGTH_POINTS = {
2
+ none: 0,
3
+ weak: 1,
4
+ moderate: 2,
5
+ strong: 3,
6
+ };
7
+ function scoreOf(points) {
8
+ if (points <= 0)
9
+ return 'unsupported';
10
+ if (points === 1)
11
+ return 'maybe';
12
+ if (points === 2)
13
+ return 'likely';
14
+ return 'supported';
15
+ }
16
+ function confidenceOf(points) {
17
+ return Math.min(1, points / 3);
18
+ }
19
+ function cacheSignature(request, registryRevision) {
20
+ return JSON.stringify({
21
+ registryRevision,
22
+ generation: request.generation,
23
+ transport: request.transport,
24
+ passiveFacts: request.passiveFacts,
25
+ composition: request.composition,
26
+ firmware: request.firmware ?? null,
27
+ });
28
+ }
29
+ function addCandidate(candidates, provider, profile, strength) {
30
+ const key = `${provider.id}\u0000${profile}`;
31
+ const existing = candidates.get(key);
32
+ if (existing === undefined) {
33
+ candidates.set(key, { provider, profile, points: STRENGTH_POINTS[strength] });
34
+ return;
35
+ }
36
+ existing.points += STRENGTH_POINTS[strength];
37
+ }
38
+ function unresolvedResult(status, request, points, evidence, conflicts) {
39
+ return {
40
+ status,
41
+ provider: null,
42
+ profile: null,
43
+ score: scoreOf(points),
44
+ confidence: confidenceOf(points),
45
+ evidence,
46
+ conflicts,
47
+ operations: null,
48
+ writable: false,
49
+ generation: request.generation,
50
+ physicalModemId: request.physicalModemId,
51
+ };
52
+ }
53
+ async function evaluate(registry, request) {
54
+ const evidence = [];
55
+ const conflicts = [];
56
+ const candidates = new Map();
57
+ const eligible = [];
58
+ for (const provider of registry.list()) {
59
+ const transportMatches = provider.eligibleTransports.includes(request.transport);
60
+ evidence.push({
61
+ provider: provider.id,
62
+ profile: null,
63
+ stage: 'transport-eligibility',
64
+ source: request.transport,
65
+ signal: transportMatches ? 'match' : 'mismatch',
66
+ strength: 'none',
67
+ detail: transportMatches ? 'eligible-transport' : 'ineligible-transport',
68
+ });
69
+ if (transportMatches)
70
+ eligible.push(provider);
71
+ }
72
+ for (const provider of eligible) {
73
+ for (const matcher of provider.passiveMatchers) {
74
+ const actual = request.passiveFacts
75
+ .filter((fact) => fact.kind === matcher.fact)
76
+ .map((fact) => fact.value);
77
+ const signal = actual.length === 0
78
+ ? 'unknown'
79
+ : actual.some((value) => matcher.expected.includes(value))
80
+ ? 'match'
81
+ : 'mismatch';
82
+ const item = {
83
+ provider: provider.id,
84
+ profile: matcher.profiles.length === 1 ? (matcher.profiles[0] ?? null) : null,
85
+ stage: 'passive-facts',
86
+ source: matcher.id,
87
+ signal,
88
+ strength: matcher.strength,
89
+ detail: signal === 'match' ? 'expected-fact-present' : 'expected-fact-not-proven',
90
+ };
91
+ evidence.push(item);
92
+ if (signal === 'mismatch' && matcher.required)
93
+ conflicts.push(item);
94
+ if (signal === 'match') {
95
+ for (const profile of matcher.profiles) {
96
+ addCandidate(candidates, provider, profile, matcher.strength);
97
+ }
98
+ }
99
+ }
100
+ }
101
+ for (const provider of eligible) {
102
+ for (const probe of provider.unauthenticatedProbes) {
103
+ const result = await probe.run(request);
104
+ const item = {
105
+ provider: provider.id,
106
+ profile: result.profiles.length === 1 ? (result.profiles[0] ?? null) : null,
107
+ stage: 'unauthenticated-fingerprint',
108
+ source: probe.id,
109
+ signal: result.signal,
110
+ strength: result.strength,
111
+ detail: result.detail,
112
+ };
113
+ evidence.push(item);
114
+ if (result.signal === 'mismatch')
115
+ conflicts.push(item);
116
+ if (result.signal === 'match') {
117
+ for (const profile of result.profiles) {
118
+ addCandidate(candidates, provider, profile, result.strength);
119
+ }
120
+ }
121
+ }
122
+ }
123
+ const ranked = [...candidates.values()].sort((left, right) => right.points - left.points);
124
+ const top = ranked[0];
125
+ if (top === undefined)
126
+ return unresolvedResult('unsupported', request, 0, evidence, conflicts);
127
+ if (ranked[1]?.points === top.points) {
128
+ return unresolvedResult('ambiguous', request, top.points, evidence, conflicts);
129
+ }
130
+ if (top.provider.authenticatedProfile !== undefined) {
131
+ const auth = await top.provider.authenticatedProfile.authenticate(request, [top.profile]);
132
+ const matched = auth.status === 'matched' && auth.profile === top.profile;
133
+ const item = {
134
+ provider: top.provider.id,
135
+ profile: top.profile,
136
+ stage: 'authenticated-profile',
137
+ source: top.provider.authenticatedProfile.algorithm,
138
+ signal: matched ? 'match' : auth.status === 'unavailable' ? 'unknown' : 'mismatch',
139
+ strength: 'strong',
140
+ detail: auth.detail,
141
+ };
142
+ evidence.push(item);
143
+ if (!matched) {
144
+ conflicts.push(item);
145
+ return unresolvedResult('ambiguous', request, top.points, evidence, conflicts);
146
+ }
147
+ top.points += STRENGTH_POINTS.strong;
148
+ }
149
+ const context = { ...request, profile: top.profile };
150
+ for (const reader of top.provider.capabilityReaders) {
151
+ const result = await reader.read(context);
152
+ const item = {
153
+ provider: top.provider.id,
154
+ profile: top.profile,
155
+ stage: 'capability-read',
156
+ source: reader.id,
157
+ signal: result.signal,
158
+ strength: result.strength,
159
+ detail: result.detail,
160
+ };
161
+ evidence.push(item);
162
+ if (result.signal === 'match')
163
+ top.points += STRENGTH_POINTS[result.strength];
164
+ if (result.signal === 'mismatch')
165
+ conflicts.push(item);
166
+ }
167
+ if (scoreOf(top.points) !== 'supported') {
168
+ return unresolvedResult('ambiguous', request, top.points, evidence, conflicts);
169
+ }
170
+ const operations = top.provider.operations(top.profile);
171
+ return {
172
+ status: 'selected',
173
+ provider: top.provider.id,
174
+ profile: top.profile,
175
+ score: 'supported',
176
+ confidence: confidenceOf(top.points),
177
+ evidence,
178
+ conflicts,
179
+ operations,
180
+ writable: operations.access === 'read-write',
181
+ generation: request.generation,
182
+ physicalModemId: request.physicalModemId,
183
+ };
184
+ }
185
+ class GenerationScopedProviderMatcher {
186
+ registry;
187
+ /** Cache mutation is scoped by physical modem, generation, firmware and composition. */
188
+ #cache = new Map();
189
+ constructor(registry) {
190
+ this.registry = registry;
191
+ }
192
+ async match(request) {
193
+ const key = request.physicalModemId;
194
+ const signature = cacheSignature(request, this.registry.revision);
195
+ const cached = this.#cache.get(key);
196
+ if (cached?.signature === signature)
197
+ return cached.result;
198
+ const result = await evaluate(this.registry, request);
199
+ this.#cache.set(key, { signature, result });
200
+ return result;
201
+ }
202
+ }
203
+ export function createProviderMatcher(registry) {
204
+ return new GenerationScopedProviderMatcher(registry);
205
+ }
@@ -0,0 +1,7 @@
1
+ export declare const MODEM_MANAGER_REFUSAL_REASONS: readonly ["unauthorized", "unsupported", "wrong-state", "busy", "not-found", "timed-out", "disconnected", "failed"];
2
+ export type ModemManagerRefusalReason = (typeof MODEM_MANAGER_REFUSAL_REASONS)[number];
3
+ export type ModemManagerRefusal = {
4
+ readonly reason: ModemManagerRefusalReason;
5
+ readonly retryable: boolean;
6
+ };
7
+ export declare function mapModemManagerError(error: unknown): ModemManagerRefusal;
@@ -0,0 +1,37 @@
1
+ import { DisconnectedError, TransportError } from '../../transport/index.js';
2
+ export const MODEM_MANAGER_REFUSAL_REASONS = [
3
+ 'unauthorized',
4
+ 'unsupported',
5
+ 'wrong-state',
6
+ 'busy',
7
+ 'not-found',
8
+ 'timed-out',
9
+ 'disconnected',
10
+ 'failed',
11
+ ];
12
+ function errorIdentity(error) {
13
+ if (!(error instanceof Error))
14
+ return String(error);
15
+ const dbusName = Reflect.get(error, 'dbusName');
16
+ return `${typeof dbusName === 'string' ? dbusName : error.name} ${error.message}`;
17
+ }
18
+ export function mapModemManagerError(error) {
19
+ if (error instanceof DisconnectedError)
20
+ return { reason: 'disconnected', retryable: true };
21
+ const identity = errorIdentity(error);
22
+ if (/Unauthorized|AccessDenied/i.test(identity))
23
+ return { reason: 'unauthorized', retryable: false };
24
+ if (/Unsupported|NotSupported/i.test(identity))
25
+ return { reason: 'unsupported', retryable: false };
26
+ if (/WrongState|InvalidState/i.test(identity))
27
+ return { reason: 'wrong-state', retryable: true };
28
+ if (/InProgress|Busy/i.test(identity))
29
+ return { reason: 'busy', retryable: true };
30
+ if (/NotFound|UnknownObject|UnknownModem/i.test(identity)) {
31
+ return { reason: 'not-found', retryable: false };
32
+ }
33
+ if (error instanceof TransportError && /timed out/i.test(error.message)) {
34
+ return { reason: 'timed-out', retryable: true };
35
+ }
36
+ return { reason: 'failed', retryable: false };
37
+ }
@@ -0,0 +1,8 @@
1
+ import type { MmDbusBackend } from '../../backend/index.js';
2
+ import type { ProviderExecutionContext } from '../contracts.js';
3
+ import type { ModemManagerProviderOperations, ModemManagerSnapshotResult } from './types.js';
4
+ export interface GenericOperationsDeps {
5
+ readonly backend: MmDbusBackend;
6
+ readSnapshot(context: ProviderExecutionContext): Promise<ModemManagerSnapshotResult>;
7
+ }
8
+ export declare function createGenericOperations(deps: GenericOperationsDeps): Pick<ModemManagerProviderOperations, 'radio' | 'modes' | 'bands' | 'signal' | 'sim' | 'power'>;
@@ -0,0 +1,207 @@
1
+ import { classifyOperationCompletion, defineOperationDescriptor, runtimePath, } from '../../domain/index.js';
2
+ import { bandWriteReadbackMatches, buildBandWriteDescriptor, buildModeWriteDescriptor, encodeModeSelection, matchAdvertisedCombination, sameSelection, selectionOf, } from '../../radio/index.js';
3
+ import { mapModemManagerError } from './errors.js';
4
+ const PROVIDER_ID = 'modemmanager';
5
+ const PROFILE = 'generic-mm';
6
+ function readDescriptor(id) {
7
+ return defineOperationDescriptor({
8
+ id,
9
+ support: { read: { supported: true }, write: { supported: false, reason: 'read-only' } },
10
+ authority: 'provider',
11
+ provider: PROVIDER_ID,
12
+ constraints: { kind: 'unconstrained' },
13
+ livePreconditions: ['modem-present', 'runtime-interface-present'],
14
+ availability: { state: 'available' },
15
+ mutationImpact: 'read',
16
+ retryClass: 'idempotent-read',
17
+ readback: { required: false },
18
+ rollback: { required: false },
19
+ journal: { required: false },
20
+ admission: { required: false },
21
+ evidence: { profiles: [PROFILE], firmware: [] },
22
+ confidence: 'high',
23
+ });
24
+ }
25
+ function writeDescriptor(id) {
26
+ return defineOperationDescriptor({
27
+ id,
28
+ support: { read: { supported: true }, write: { supported: true } },
29
+ authority: 'provider',
30
+ provider: PROVIDER_ID,
31
+ constraints: { kind: 'unconstrained' },
32
+ livePreconditions: ['modem-present', 'runtime-interface-present'],
33
+ availability: { state: 'available' },
34
+ mutationImpact: 'disruptive',
35
+ retryClass: 'never',
36
+ readback: { required: false },
37
+ rollback: { required: false },
38
+ journal: { required: true, reason: 'disruptive-radio-write' },
39
+ admission: { required: true, reason: 'provider-mutation' },
40
+ evidence: { profiles: [PROFILE], firmware: [] },
41
+ confidence: 'high',
42
+ });
43
+ }
44
+ function applied(context, value) {
45
+ return classifyOperationCompletion({
46
+ operation: 'read',
47
+ completionGeneration: context.generation,
48
+ currentGeneration: context.generation,
49
+ completion: { status: 'applied', value },
50
+ });
51
+ }
52
+ function refused(context, reason) {
53
+ return classifyOperationCompletion({
54
+ operation: 'read',
55
+ completionGeneration: context.generation,
56
+ currentGeneration: context.generation,
57
+ completion: { status: 'refused', reason },
58
+ });
59
+ }
60
+ function receiptReason(reason) {
61
+ const mapped = mapModemManagerError(new Error(reason));
62
+ return mapped.reason === 'failed' ? reason : mapped.reason;
63
+ }
64
+ export function createGenericOperations(deps) {
65
+ const readField = async (context, capability, select, reason) => {
66
+ const snapshot = await deps.readSnapshot(context);
67
+ if (!snapshot.ok)
68
+ return refused(context, snapshot.reason);
69
+ return snapshot.capabilities[capability]
70
+ ? applied(context, select(snapshot))
71
+ : refused(context, reason);
72
+ };
73
+ const readBands = async (context) => {
74
+ const snapshot = await deps.readSnapshot(context);
75
+ if (!snapshot.ok)
76
+ return refused(context, snapshot.reason);
77
+ if (!snapshot.capabilities.bandRead)
78
+ return refused(context, 'band-read-unsupported');
79
+ const result = await deps.backend.readBands(runtimePath(snapshot.modemPath));
80
+ return result.ok
81
+ ? applied(context, result.bands)
82
+ : refused(context, receiptReason(result.reason));
83
+ };
84
+ const radioDescriptor = writeDescriptor('modemmanager.radio-modes');
85
+ const radio = {
86
+ descriptor: radioDescriptor,
87
+ describe: () => Promise.resolve(radioDescriptor),
88
+ read: (context) => readField(context, 'modeRead', (snapshot) => snapshot.radio, 'mode-read-unsupported'),
89
+ write: async (context, input) => {
90
+ const snapshot = await deps.readSnapshot(context);
91
+ if (!snapshot.ok)
92
+ return refused(context, snapshot.reason);
93
+ if (!snapshot.capabilities.modeWrite)
94
+ return refused(context, 'mode-write-unsupported');
95
+ const receipt = await deps.backend.setRadioModes(runtimePath(snapshot.modemPath), input);
96
+ if (receipt.status !== 'applied')
97
+ return refused(context, receiptReason(receipt.reason));
98
+ return readField(context, 'modeRead', (next) => next.radio, 'mode-read-unsupported');
99
+ },
100
+ };
101
+ const readModeTruth = async (context) => readField(context, 'modeRead', (snapshot) => snapshot.radio.truth, 'mode-read-unsupported');
102
+ const modes = {
103
+ descriptor: buildModeWriteDescriptor({
104
+ provider: PROVIDER_ID,
105
+ profile: PROFILE,
106
+ truth: {
107
+ current: { state: 'not-reported' },
108
+ supported: { combinations: [], undecodable: [] },
109
+ },
110
+ writeSupported: true,
111
+ }),
112
+ describe: async (context) => {
113
+ const snapshot = await deps.readSnapshot(context);
114
+ return buildModeWriteDescriptor({
115
+ provider: PROVIDER_ID,
116
+ profile: PROFILE,
117
+ truth: snapshot.ok
118
+ ? snapshot.radio.truth
119
+ : {
120
+ current: { state: 'not-reported' },
121
+ supported: { combinations: [], undecodable: [] },
122
+ },
123
+ writeSupported: snapshot.ok && snapshot.capabilities.modeWrite,
124
+ });
125
+ },
126
+ read: readModeTruth,
127
+ write: async (context, selection) => {
128
+ const snapshot = await deps.readSnapshot(context);
129
+ if (!snapshot.ok)
130
+ return refused(context, snapshot.reason);
131
+ if (!snapshot.capabilities.modeWrite)
132
+ return refused(context, 'mode-write-unsupported');
133
+ // A selection the modem never advertised is refused outright — never rounded to
134
+ // the nearest advertised one. Substituting is how "prefer 4G" on a marginal cell
135
+ // silently becomes 5G-first, which `five-g-preference.ts` refuses for the same reason.
136
+ if (matchAdvertisedCombination(snapshot.radio.truth.supported, selection) === undefined) {
137
+ return refused(context, 'mode-combination-not-advertised');
138
+ }
139
+ const encoded = encodeModeSelection(selection);
140
+ if (!encoded.ok)
141
+ return refused(context, 'mode-name-unknown');
142
+ const receipt = await deps.backend.setModeCombination(runtimePath(snapshot.modemPath), encoded.allowedMask, encoded.preferredMask);
143
+ if (receipt.status !== 'applied')
144
+ return refused(context, receiptReason(receipt.reason));
145
+ const readback = await readModeTruth(context);
146
+ if (readback.status !== 'applied')
147
+ return readback;
148
+ // An accepted-but-ignored `SetCurrentModes` is indistinguishable from success at
149
+ // the call site, so the descriptor requires a readback and this enforces it.
150
+ return readback.value.current.state === 'reported' &&
151
+ sameSelection(selectionOf(readback.value.current.combination), selection)
152
+ ? readback
153
+ : refused(context, 'mode-write-readback-mismatch');
154
+ },
155
+ };
156
+ const bandDescriptorFor = (snapshot) => buildBandWriteDescriptor({
157
+ provider: PROVIDER_ID,
158
+ profile: PROFILE,
159
+ certification: snapshot.ok
160
+ ? snapshot.bandCertification
161
+ : {
162
+ required: true,
163
+ satisfied: false,
164
+ reason: 'band-certification-required',
165
+ offerable: [],
166
+ },
167
+ readSupported: snapshot.ok && snapshot.capabilities.bandRead,
168
+ });
169
+ const bands = {
170
+ descriptor: bandDescriptorFor({ ok: false, reason: 'not-found' }),
171
+ describe: async (context) => bandDescriptorFor(await deps.readSnapshot(context)),
172
+ read: readBands,
173
+ write: async (context, input) => {
174
+ const snapshot = await deps.readSnapshot(context);
175
+ if (!snapshot.ok)
176
+ return refused(context, snapshot.reason);
177
+ // The certification gate, second of two. `bandWrite` is already false without a
178
+ // catalog entry, and the descriptor already reads `refused` — but a band lock can
179
+ // take a working uplink off the air, so the call path refuses independently
180
+ // rather than trusting a consumer to have read the descriptor.
181
+ if (!snapshot.bandCertification.satisfied || !snapshot.capabilities.bandWrite)
182
+ return refused(context, 'band-write-certification-required');
183
+ const receipt = await deps.backend.setCurrentBands(runtimePath(snapshot.modemPath), input);
184
+ if (receipt.status !== 'applied')
185
+ return refused(context, receiptReason(receipt.reason));
186
+ const readback = await readBands(context);
187
+ if (readback.status !== 'applied')
188
+ return readback;
189
+ return bandWriteReadbackMatches(input, readback.value)
190
+ ? readback
191
+ : refused(context, 'band-write-readback-mismatch');
192
+ },
193
+ };
194
+ const signal = {
195
+ descriptor: readDescriptor('modemmanager.signal'),
196
+ read: (context) => readField(context, 'signalRead', (snapshot) => snapshot.signal, 'signal-read-unsupported'),
197
+ };
198
+ const sim = {
199
+ descriptor: readDescriptor('modemmanager.sim'),
200
+ read: (context) => readField(context, 'simRead', (snapshot) => snapshot.sim, 'sim-read-unsupported'),
201
+ };
202
+ const power = {
203
+ descriptor: readDescriptor('modemmanager.power'),
204
+ read: (context) => readField(context, 'powerRead', (snapshot) => snapshot.power, 'power-read-unsupported'),
205
+ };
206
+ return { radio, modes, bands, signal, sim, power };
207
+ }
@@ -0,0 +1,3 @@
1
+ export * from './errors.js';
2
+ export * from './provider.js';
3
+ export * from './types.js';
@@ -0,0 +1,3 @@
1
+ export * from './errors.js';
2
+ export * from './provider.js';
3
+ export * from './types.js';
@@ -0,0 +1,21 @@
1
+ import type { MmLocation } from '../../backend/index.js';
2
+ import type { DbusTransport } from '../../transport/index.js';
3
+ import type { MmUssd } from '../../ussd/index.js';
4
+ import type { ProviderExecutionContext } from '../contracts.js';
5
+ import type { ModemManagerProviderOperations, ModemManagerSnapshotResult } from './types.js';
6
+ export interface ModuleOperationsOptions {
7
+ readonly transport: DbusTransport;
8
+ readonly destination: string;
9
+ readonly location: MmLocation;
10
+ readonly ussd: MmUssd;
11
+ readonly now: () => number;
12
+ readSnapshot(context: ProviderExecutionContext): Promise<ModemManagerSnapshotResult>;
13
+ }
14
+ type ModuleOperationSurface = Pick<ModemManagerProviderOperations, 'location' | 'sms' | 'ussd' | 'initiateUssd' | 'respondUssd' | 'cancelUssd' | 'fccCoverage'>;
15
+ export declare class ModemManagerModuleOperations {
16
+ #private;
17
+ readonly operations: ModuleOperationSurface;
18
+ constructor(options: ModuleOperationsOptions);
19
+ stop(): Promise<void>;
20
+ }
21
+ export {};