@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,137 @@
1
+ import type { DesiredRadio, ObservationEnvelope, OperationDescriptor, OperationResult, RadioPower } from '../../domain/index.js';
2
+ import type { SimPresence, SimPresenceEvidence } from '../../hardware/router-parsers.js';
3
+ import type { GnssFixState } from '../../location/index.js';
4
+ import type { NormalizedModemObservation } from '../../observations/index.js';
5
+ import type { FixRead, GnssSource, LocationStatusResult, LocationToggleResult, ModemBands, ObservationList, ObservationListener, SmsObservationPort } from '../../ports/index.js';
6
+ import type { BandWriteCertification, ModeSelection, RadioModeTruth } from '../../radio/index.js';
7
+ import type { MmUssd, UssdVerbResult } from '../../ussd/index.js';
8
+ import type { ProviderExecutionContext, ProviderOperationsSurface } from '../contracts.js';
9
+ import type { ModemManagerRefusalReason } from './errors.js';
10
+ import type { RuntimeCompositionOperation } from './runtime-composition-operation.js';
11
+ export type ModemManagerCapabilities = {
12
+ readonly modeRead: boolean;
13
+ readonly modeWrite: boolean;
14
+ readonly bandRead: boolean;
15
+ readonly bandWrite: boolean;
16
+ readonly signalRead: boolean;
17
+ readonly simRead: boolean;
18
+ readonly multiSim: boolean;
19
+ readonly location: boolean;
20
+ readonly sms: boolean;
21
+ readonly ussd: boolean;
22
+ readonly powerRead: boolean;
23
+ };
24
+ export type ModemManagerRadioState = {
25
+ readonly current: {
26
+ readonly allowed: number;
27
+ readonly preferred: number;
28
+ };
29
+ readonly supported: readonly {
30
+ readonly allowed: number;
31
+ readonly preferred: number;
32
+ }[];
33
+ /**
34
+ * The SAME properties decoded without loss — `preferred: 'none'` stays `none`, an
35
+ * unfamiliar mode bit stays offerable as `unknown-combination`, and a member that
36
+ * is not a `(uu)` lands in `undecodable` rather than vanishing. `supported` above
37
+ * is the pre-existing raw-mask view and DROPS a zero-allowed member; this one does
38
+ * not, which is the whole point of carrying both.
39
+ */
40
+ readonly truth: RadioModeTruth;
41
+ };
42
+ export type ModemManagerSignalState = {
43
+ readonly quality?: number;
44
+ readonly recent?: boolean;
45
+ readonly extendedAvailable: boolean;
46
+ };
47
+ export type ModemManagerSimState = {
48
+ /**
49
+ * The modem exports an ACTIVE SIM object path. `true` is positive evidence of a
50
+ * SIM; `false` is NOT evidence of absence — read `presence` for that.
51
+ */
52
+ readonly present: boolean;
53
+ /** `absent` only ever comes from `StateFailedReason: sim-missing`. */
54
+ readonly presence: SimPresence;
55
+ readonly presenceEvidence: SimPresenceEvidence;
56
+ readonly slotCount: number;
57
+ readonly primarySlot: number;
58
+ readonly lockRequired?: number;
59
+ readonly simType?: number;
60
+ readonly esimStatus?: number;
61
+ };
62
+ export type ModemManagerProviderSnapshot = {
63
+ readonly modemPath: string;
64
+ readonly capabilities: ModemManagerCapabilities;
65
+ readonly radio: ModemManagerRadioState;
66
+ /** The band-lock gate for THIS device, resolved from the shipped certification catalog. */
67
+ readonly bandCertification: BandWriteCertification;
68
+ readonly signal: ModemManagerSignalState;
69
+ readonly sim: ModemManagerSimState;
70
+ readonly power: RadioPower;
71
+ readonly observation: ObservationEnvelope<NormalizedModemObservation>;
72
+ };
73
+ export type ModemManagerSnapshotResult = ({
74
+ readonly ok: true;
75
+ } & ModemManagerProviderSnapshot) | {
76
+ readonly ok: false;
77
+ readonly reason: ModemManagerRefusalReason;
78
+ };
79
+ export interface ContextReadOperation<O> {
80
+ readonly descriptor: OperationDescriptor<never, O>;
81
+ read(context: ProviderExecutionContext): Promise<OperationResult<O>>;
82
+ }
83
+ export interface ContextWriteOperation<I, O> {
84
+ /** The shape-only descriptor: what this operation is, before any device answered. */
85
+ readonly descriptor: OperationDescriptor<I, O>;
86
+ /**
87
+ * The descriptor as the LIVE device entitles it.
88
+ *
89
+ * A static descriptor cannot carry a device's own catalog, so it cannot say which
90
+ * mode combinations this modem advertises or whether this SKU's band lock is
91
+ * certified. Those are exactly the facts a consumer needs before it offers a
92
+ * control, so they are read here instead of being inferred from a capability flag.
93
+ */
94
+ describe(context: ProviderExecutionContext): Promise<OperationDescriptor<I, O>>;
95
+ read(context: ProviderExecutionContext): Promise<OperationResult<O>>;
96
+ write(context: ProviderExecutionContext, input: I): Promise<OperationResult<O>>;
97
+ }
98
+ export type SmsPortResult = {
99
+ readonly ok: true;
100
+ readonly port: SmsObservationPort;
101
+ } | {
102
+ readonly ok: false;
103
+ readonly reason: ModemManagerRefusalReason;
104
+ };
105
+ export interface ModemManagerProviderOperations extends ProviderOperationsSurface {
106
+ readonly radio: ContextWriteOperation<DesiredRadio, ModemManagerRadioState>;
107
+ /**
108
+ * The modem's OWN mode vocabulary — an advertised `(allowed, preferred)` pair,
109
+ * selected verbatim. Separate from `radio` because `DesiredRadio` is an ordered RAT
110
+ * preference and cannot express `preferred: none`, which real hardware advertises.
111
+ */
112
+ readonly modes: ContextWriteOperation<ModeSelection, RadioModeTruth>;
113
+ readonly bands: ContextWriteOperation<readonly string[], ModemBands>;
114
+ readonly signal: ContextReadOperation<ModemManagerSignalState>;
115
+ readonly sim: ContextReadOperation<ModemManagerSimState>;
116
+ readonly power: ContextReadOperation<RadioPower>;
117
+ readonly usbComposition: RuntimeCompositionOperation;
118
+ readonly location: {
119
+ status(context: ProviderExecutionContext): Promise<LocationStatusResult>;
120
+ enable(context: ProviderExecutionContext, sources: readonly GnssSource[]): Promise<LocationToggleResult>;
121
+ disable(context: ProviderExecutionContext): Promise<LocationToggleResult>;
122
+ readFix(context: ProviderExecutionContext): Promise<FixRead>;
123
+ state(context: ProviderExecutionContext): GnssFixState;
124
+ tick(context: ProviderExecutionContext): GnssFixState;
125
+ };
126
+ sms(context: ProviderExecutionContext): Promise<SmsPortResult>;
127
+ readonly ussd: MmUssd;
128
+ initiateUssd(context: ProviderExecutionContext, ussdCommand: string): Promise<UssdVerbResult>;
129
+ respondUssd(context: ProviderExecutionContext, ussdResponse: string): Promise<UssdVerbResult>;
130
+ cancelUssd(context: ProviderExecutionContext): Promise<UssdVerbResult>;
131
+ readonly fccCoverage: (vid: string | undefined, pid: string | undefined) => 'present' | 'absent' | 'unknown';
132
+ }
133
+ export interface ModemManagerProviderLifecycle {
134
+ start(): Promise<ObservationList>;
135
+ observe(listener: ObservationListener): () => void;
136
+ stop(): Promise<void>;
137
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,71 @@
1
+ import type { DeviceGeneration } from '../../domain/index.js';
2
+ import type { AppliedConfiguration, DesiredProfile, ModemStateView, NormalizationContext, ObservedState, StateDivergence } from '../../observations/index.js';
3
+ import type { ConnectionId, DeviceIfname, GsmProfile, NetworkManagerPort } from '../../ports/index.js';
4
+ import type { NmApplyResult, NmBearerState, NmDesiredRequest, NmObservationInput, NmObservationResult, NmSaveResult } from './types.js';
5
+ export interface NetworkManagerAdapterOptions {
6
+ readonly port: NetworkManagerPort;
7
+ /** Clock for the desired/applied slots only; observations carry their own. */
8
+ readonly now?: () => number;
9
+ }
10
+ export interface NmApplyOptions {
11
+ readonly operationId: string;
12
+ readonly generation: DeviceGeneration;
13
+ }
14
+ export declare class NetworkManagerAdapter {
15
+ #private;
16
+ constructor(options: NetworkManagerAdapterOptions);
17
+ /** Every connection this adapter holds a slot for, in insertion order. */
18
+ trackedConnections(): readonly ConnectionId[];
19
+ /**
20
+ * Save an operator's profile and record it as DESIRED.
21
+ *
22
+ * The desired slot is built from what the caller ASKED for, not from what NM read
23
+ * back. That is the whole point of the slot: if NM normalized a field on the way
24
+ * in, the next `describeStateDivergence` is supposed to report it, and seeding
25
+ * desired from the readback would make that divergence structurally invisible.
26
+ */
27
+ saveDesiredProfile(request: NmDesiredRequest): Promise<NmSaveResult>;
28
+ /**
29
+ * Record that the operator wants NO bearer on this connection's device.
30
+ *
31
+ * It performs no I/O on purpose — desired is a record of intent, and the
32
+ * deactivation it implies happens when `applyDesired` is called, so a request and
33
+ * its execution stay two separately observable facts.
34
+ */
35
+ releaseDesired(id: ConnectionId, requestedBy: string): NmSaveResult;
36
+ desiredFor(id: ConnectionId): DesiredProfile<NmBearerState> | null;
37
+ /** The saved profile as NM itself holds it — a straight read, no slot involved. */
38
+ readSavedProfile(id: ConnectionId): Promise<GsmProfile | undefined>;
39
+ /**
40
+ * Put the desired state into force and record what was applied.
41
+ *
42
+ * The applied slot is built from the profile NM READ BACK, not from the request:
43
+ * "what was actually put into force" is a claim about NM's content, and asserting
44
+ * it from the input would make a silently-rejected field look applied.
45
+ */
46
+ applyDesired(id: ConnectionId, options: NmApplyOptions): Promise<NmApplyResult>;
47
+ appliedFor(id: ConnectionId): AppliedConfiguration<NmBearerState> | null;
48
+ /**
49
+ * The interface the applied bearer landed on, or `null` when no bearer is in
50
+ * force. An applied `unbound` state resolves to `null` rather than to its device:
51
+ * there is a device, but no bearer on it, and returning the name would read as one.
52
+ */
53
+ resolveAppliedInterface(id: ConnectionId): DeviceIfname | null;
54
+ /**
55
+ * Fold one complete NM readout.
56
+ *
57
+ * Never touches the desired slot. Clears the applied slot only when the readout
58
+ * positively contradicts it, and always says which of the four contradictions it
59
+ * was.
60
+ */
61
+ observe(input: NmObservationInput): NmObservationResult;
62
+ observedFor(id: ConnectionId): ObservedState<NmBearerState> | null;
63
+ /**
64
+ * The three-slot view. `context` supplies the provenance for the "we have not
65
+ * observed this yet" case — this layer has no clock or epoch counter of its own
66
+ * for observations, so it cannot manufacture one.
67
+ */
68
+ stateView(id: ConnectionId, context: NormalizationContext): ModemStateView<NmBearerState, NmBearerState, NmBearerState> | null;
69
+ /** `desiredVsApplied` ("did our write happen") and `appliedVsObserved` ("did it stick"). */
70
+ divergence(id: ConnectionId, context: NormalizationContext): StateDivergence | null;
71
+ }
@@ -0,0 +1,348 @@
1
+ // The thin NetworkManager adapter: desired profiles, applied bearers, and the
2
+ // interface each one landed on — the saved-vs-applied boundary and nothing else.
3
+ //
4
+ // It is the ONLY bearer/APN authority surface in the package. `ModemManagerPort` has
5
+ // no connect verb by construction (`ports/forbidden-surface.test.ts`), and the
6
+ // ModemManager provider has no bearer operation, so nothing else can put a bearer into
7
+ // force. Correspondingly, this adapter performs no radio, band, SIM or power operation:
8
+ // those belong to the ModemManager provider, and expressing them here would create a
9
+ // second writer for a resource the ownership matrix gives to exactly one.
10
+ //
11
+ // Two rules shape every method below.
12
+ //
13
+ // 1. OBSERVED STATE NEVER WRITES THE DESIRED SLOT. `observe()` may clear the applied
14
+ // slot and always rewrites the observed one; it does not touch `desired` on any
15
+ // path. Reality overtaking a write does not un-ask the question the operator asked
16
+ // — and if it did, a re-enumeration would silently erase the configuration the
17
+ // controller is supposed to restore.
18
+ // 2. WRITES GO THROUGH THE PORT'S TYPED OPERATIONS, AND ONLY THOSE. The NM half of the
19
+ // port-tagged op set (`ports/ops.ts` `NmOp`) is create / update / activate /
20
+ // deactivate. There is deliberately no delete path here: profile removal is not in
21
+ // that set, so this adapter cannot express it.
22
+ import { epochMillis } from '../../domain/index.js';
23
+ import { appliedConfiguration, describeStateDivergence, desiredProfile, freshObservation, observedState, unavailableObservation, } from '../../observations/index.js';
24
+ import { receipt } from '../../ports/index.js';
25
+ import { boundBearer, nmBearerStateEquals, unboundBearer } from './types.js';
26
+ const SOURCE = 'networkmanager';
27
+ /** The device states in which NM is still settling an activation, not losing it. */
28
+ const TRANSITIONAL_STATES = new Set([
29
+ 'prepare',
30
+ 'config',
31
+ 'need-auth',
32
+ 'ip-config',
33
+ 'ip-check',
34
+ 'secondaries',
35
+ 'deactivating',
36
+ ]);
37
+ export class NetworkManagerAdapter {
38
+ #port;
39
+ #now;
40
+ #slots = new Map();
41
+ #observedGeneration = null;
42
+ constructor(options) {
43
+ this.#port = options.port;
44
+ this.#now = options.now ?? Date.now;
45
+ }
46
+ /** Every connection this adapter holds a slot for, in insertion order. */
47
+ trackedConnections() {
48
+ return [...this.#slots.keys()];
49
+ }
50
+ // ── desired ────────────────────────────────────────────────────────────────
51
+ /**
52
+ * Save an operator's profile and record it as DESIRED.
53
+ *
54
+ * The desired slot is built from what the caller ASKED for, not from what NM read
55
+ * back. That is the whole point of the slot: if NM normalized a field on the way
56
+ * in, the next `describeStateDivergence` is supposed to report it, and seeding
57
+ * desired from the readback would make that divergence structurally invisible.
58
+ */
59
+ async saveDesiredProfile(request) {
60
+ let saved;
61
+ try {
62
+ saved =
63
+ request.connectionId === undefined
64
+ ? await this.#port.createGsmProfile(request.profile)
65
+ : await this.#port.updateGsmProfile(request.connectionId, request.profile);
66
+ }
67
+ catch (error) {
68
+ return {
69
+ ok: false,
70
+ reason: 'write-failed',
71
+ receipt: receipt('connection', 'failed', describeError(error)),
72
+ };
73
+ }
74
+ const id = saved.connectionId;
75
+ const desired = desiredProfile(boundBearer({
76
+ connectionId: id,
77
+ deviceIfname: request.deviceIfname,
78
+ apn: request.profile.apn,
79
+ autoConfig: request.profile.autoConfig,
80
+ homeOnly: request.profile.homeOnly,
81
+ }), epochMillis(this.#now()), request.requestedBy);
82
+ this.#slotsFor(id).desired = desired;
83
+ return { ok: true, connectionId: id, desired };
84
+ }
85
+ /**
86
+ * Record that the operator wants NO bearer on this connection's device.
87
+ *
88
+ * It performs no I/O on purpose — desired is a record of intent, and the
89
+ * deactivation it implies happens when `applyDesired` is called, so a request and
90
+ * its execution stay two separately observable facts.
91
+ */
92
+ releaseDesired(id, requestedBy) {
93
+ const slots = this.#slots.get(id);
94
+ const ifname = slots === undefined ? undefined : targetIfname(slots);
95
+ if (ifname === undefined) {
96
+ return {
97
+ ok: false,
98
+ reason: 'no-desired-profile',
99
+ receipt: receipt('connection', 'failed', `no tracked device for connection ${id}`),
100
+ };
101
+ }
102
+ const desired = desiredProfile(unboundBearer(ifname), epochMillis(this.#now()), requestedBy);
103
+ this.#slotsFor(id).desired = desired;
104
+ return { ok: true, connectionId: id, desired };
105
+ }
106
+ desiredFor(id) {
107
+ return this.#slots.get(id)?.desired ?? null;
108
+ }
109
+ /** The saved profile as NM itself holds it — a straight read, no slot involved. */
110
+ async readSavedProfile(id) {
111
+ return await this.#port.readGsmProfile(id);
112
+ }
113
+ // ── applied ────────────────────────────────────────────────────────────────
114
+ /**
115
+ * Put the desired state into force and record what was applied.
116
+ *
117
+ * The applied slot is built from the profile NM READ BACK, not from the request:
118
+ * "what was actually put into force" is a claim about NM's content, and asserting
119
+ * it from the input would make a silently-rejected field look applied.
120
+ */
121
+ async applyDesired(id, options) {
122
+ const desired = this.desiredFor(id);
123
+ if (desired === null) {
124
+ return refuse('no-desired-profile', `connection ${id} has no desired profile`);
125
+ }
126
+ return desired.profile.kind === 'unbound'
127
+ ? await this.#deactivate(id, desired.profile.deviceIfname, options)
128
+ : await this.#activate(id, desired.profile, options);
129
+ }
130
+ appliedFor(id) {
131
+ return this.#slots.get(id)?.applied ?? null;
132
+ }
133
+ /**
134
+ * The interface the applied bearer landed on, or `null` when no bearer is in
135
+ * force. An applied `unbound` state resolves to `null` rather than to its device:
136
+ * there is a device, but no bearer on it, and returning the name would read as one.
137
+ */
138
+ resolveAppliedInterface(id) {
139
+ const applied = this.appliedFor(id);
140
+ if (applied === null || applied.configuration.kind !== 'bound') {
141
+ return null;
142
+ }
143
+ return applied.configuration.binding.deviceIfname;
144
+ }
145
+ // ── observed ───────────────────────────────────────────────────────────────
146
+ /**
147
+ * Fold one complete NM readout.
148
+ *
149
+ * Never touches the desired slot. Clears the applied slot only when the readout
150
+ * positively contradicts it, and always says which of the four contradictions it
151
+ * was.
152
+ */
153
+ observe(input) {
154
+ const generation = input.context.generation;
155
+ if (this.#observedGeneration !== null && generation < this.#observedGeneration) {
156
+ return {
157
+ kind: 'refused',
158
+ reason: 'superseded-generation',
159
+ currentGeneration: this.#observedGeneration,
160
+ };
161
+ }
162
+ this.#observedGeneration = generation;
163
+ const devices = new Map(input.devices.map((device) => [device.ifname, device]));
164
+ const outcomes = [];
165
+ const losses = [];
166
+ for (const [id, slots] of this.#slots) {
167
+ const ifname = targetIfname(slots);
168
+ if (ifname === undefined) {
169
+ continue;
170
+ }
171
+ const device = devices.get(ifname);
172
+ slots.observed = observedState(device === undefined
173
+ ? unavailableObservation(SOURCE, input.context, 'device-absent')
174
+ : freshObservation(SOURCE, input.context, observedBearer(device)));
175
+ const outcome = this.#classify(id, slots, ifname, device, input.context.observedAt);
176
+ outcomes.push({ connectionId: id, outcome });
177
+ if (outcome.status === 'lost') {
178
+ losses.push(outcome.loss);
179
+ }
180
+ }
181
+ return {
182
+ kind: 'accepted',
183
+ generation,
184
+ observedAt: input.context.observedAt,
185
+ outcomes,
186
+ losses,
187
+ };
188
+ }
189
+ observedFor(id) {
190
+ return this.#slots.get(id)?.observed ?? null;
191
+ }
192
+ /**
193
+ * The three-slot view. `context` supplies the provenance for the "we have not
194
+ * observed this yet" case — this layer has no clock or epoch counter of its own
195
+ * for observations, so it cannot manufacture one.
196
+ */
197
+ stateView(id, context) {
198
+ const slots = this.#slots.get(id);
199
+ if (slots === undefined) {
200
+ return null;
201
+ }
202
+ return {
203
+ desired: slots.desired,
204
+ applied: slots.applied,
205
+ observed: slots.observed ??
206
+ observedState(unavailableObservation(SOURCE, context, 'provider-unavailable')),
207
+ };
208
+ }
209
+ /** `desiredVsApplied` ("did our write happen") and `appliedVsObserved` ("did it stick"). */
210
+ divergence(id, context) {
211
+ const view = this.stateView(id, context);
212
+ return view === null ? null : describeStateDivergence(view, nmBearerStateEquals);
213
+ }
214
+ // ── internals ──────────────────────────────────────────────────────────────
215
+ async #activate(id, desired, options) {
216
+ if (desired.kind !== 'bound') {
217
+ return refuse('no-desired-profile', `connection ${id} has no bearer to activate`);
218
+ }
219
+ const ifname = desired.binding.deviceIfname;
220
+ const saved = await this.#port.readGsmProfile(id);
221
+ if (saved === undefined) {
222
+ return refuse('profile-absent', `connection ${id} is no longer saved in NetworkManager`);
223
+ }
224
+ let activation;
225
+ try {
226
+ activation = await this.#port.activate(id, ifname);
227
+ }
228
+ catch (error) {
229
+ return refuse('activation-failed', describeError(error));
230
+ }
231
+ if (activation.status !== 'applied') {
232
+ return { ok: false, reason: 'activation-failed', receipt: activation };
233
+ }
234
+ const applied = appliedConfiguration({
235
+ configuration: boundBearer({
236
+ connectionId: id,
237
+ deviceIfname: ifname,
238
+ apn: saved.apn,
239
+ autoConfig: saved.autoConfig,
240
+ homeOnly: saved.homeOnly,
241
+ }),
242
+ appliedAt: epochMillis(this.#now()),
243
+ generation: options.generation,
244
+ operationId: options.operationId,
245
+ });
246
+ this.#slotsFor(id).applied = applied;
247
+ return { ok: true, applied, receipt: activation };
248
+ }
249
+ async #deactivate(id, ifname, options) {
250
+ let result;
251
+ try {
252
+ result = await this.#port.deactivate(id, ifname);
253
+ }
254
+ catch (error) {
255
+ return refuse('deactivation-failed', describeError(error));
256
+ }
257
+ if (result.status !== 'applied') {
258
+ return { ok: false, reason: 'deactivation-failed', receipt: result };
259
+ }
260
+ const applied = appliedConfiguration({
261
+ configuration: unboundBearer(ifname),
262
+ appliedAt: epochMillis(this.#now()),
263
+ generation: options.generation,
264
+ operationId: options.operationId,
265
+ });
266
+ this.#slotsFor(id).applied = applied;
267
+ return { ok: true, applied, receipt: result };
268
+ }
269
+ #classify(id, slots, ifname, device, observedAt) {
270
+ const applied = slots.applied;
271
+ if (applied === null) {
272
+ return { status: 'unapplied' };
273
+ }
274
+ const lose = (reason) => {
275
+ slots.applied = null;
276
+ return {
277
+ status: 'lost',
278
+ loss: {
279
+ connectionId: id,
280
+ deviceIfname: ifname,
281
+ reason,
282
+ lostAt: observedAt,
283
+ generation: applied.generation,
284
+ previous: applied,
285
+ },
286
+ };
287
+ };
288
+ if (device === undefined) {
289
+ return lose('interface-absent');
290
+ }
291
+ if (device.state === 'failed') {
292
+ return lose('activation-failed');
293
+ }
294
+ if (applied.configuration.kind === 'unbound') {
295
+ // We deliberately took the bearer down; anything active here is somebody else.
296
+ return device.activeConnection === undefined
297
+ ? { status: 'retained', applied }
298
+ : lose('connection-replaced');
299
+ }
300
+ if (device.activeConnection === undefined) {
301
+ return lose('interface-detached');
302
+ }
303
+ if (device.activeConnection.connectionId !== id) {
304
+ return lose('connection-replaced');
305
+ }
306
+ if (device.state === 'activated') {
307
+ return { status: 'retained', applied };
308
+ }
309
+ return TRANSITIONAL_STATES.has(device.state)
310
+ ? { status: 'pending', applied, deviceState: device.state }
311
+ : lose('interface-detached');
312
+ }
313
+ #slotsFor(id) {
314
+ const existing = this.#slots.get(id);
315
+ if (existing !== undefined) {
316
+ return existing;
317
+ }
318
+ const created = { desired: null, applied: null, observed: null };
319
+ this.#slots.set(id, created);
320
+ return created;
321
+ }
322
+ }
323
+ /** The device the slots are about: where the bearer IS, else where it was asked for. */
324
+ function targetIfname(slots) {
325
+ const state = slots.applied?.configuration ?? slots.desired?.profile;
326
+ if (state === undefined) {
327
+ return undefined;
328
+ }
329
+ return state.kind === 'bound' ? state.binding.deviceIfname : state.deviceIfname;
330
+ }
331
+ function observedBearer(device) {
332
+ const active = device.activeConnection;
333
+ return active === undefined
334
+ ? unboundBearer(device.ifname)
335
+ : boundBearer({
336
+ connectionId: active.connectionId,
337
+ deviceIfname: device.ifname,
338
+ apn: active.apn,
339
+ autoConfig: active.autoConfig,
340
+ homeOnly: active.homeOnly,
341
+ });
342
+ }
343
+ function refuse(reason, message) {
344
+ return { ok: false, reason, receipt: receipt('connection', 'failed', message) };
345
+ }
346
+ function describeError(error) {
347
+ return error instanceof Error ? error.message : 'NetworkManager rejected the request';
348
+ }
@@ -0,0 +1,2 @@
1
+ export * from './adapter.js';
2
+ export * from './types.js';
@@ -0,0 +1,2 @@
1
+ export * from './adapter.js';
2
+ export * from './types.js';