@ceralive/modem-control 0.2.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (464) hide show
  1. package/README.md +317 -0
  2. package/dist/backend/at-lease.d.ts +57 -0
  3. package/dist/backend/at-lease.js +109 -0
  4. package/dist/backend/cell-info.d.ts +46 -0
  5. package/dist/backend/cell-info.js +124 -0
  6. package/dist/backend/constants.d.ts +24 -0
  7. package/{src/backend/constants.ts → dist/backend/constants.js} +4 -9
  8. package/dist/backend/device-classifier.d.ts +46 -0
  9. package/dist/backend/device-classifier.js +258 -0
  10. package/dist/backend/enrichment.d.ts +28 -0
  11. package/dist/backend/enrichment.js +59 -0
  12. package/dist/backend/features.d.ts +61 -0
  13. package/dist/backend/features.js +109 -0
  14. package/dist/backend/identity-ladder.d.ts +57 -0
  15. package/dist/backend/identity-ladder.js +158 -0
  16. package/dist/backend/identity-registry.d.ts +51 -0
  17. package/dist/backend/identity-registry.js +101 -0
  18. package/dist/backend/index.d.ts +30 -0
  19. package/dist/backend/index.js +35 -0
  20. package/dist/backend/lifecycle-interlock.d.ts +25 -0
  21. package/dist/backend/lifecycle-interlock.js +18 -0
  22. package/dist/backend/managed-objects.d.ts +39 -0
  23. package/dist/backend/managed-objects.js +82 -0
  24. package/dist/backend/mapping.d.ts +11 -0
  25. package/dist/backend/mapping.js +148 -0
  26. package/dist/backend/mm-backend.d.ts +44 -0
  27. package/dist/backend/mm-backend.js +154 -0
  28. package/dist/backend/mm-location.d.ts +21 -0
  29. package/dist/backend/mm-location.js +237 -0
  30. package/dist/backend/mm-mutations.d.ts +42 -0
  31. package/dist/backend/mm-mutations.js +259 -0
  32. package/dist/backend/modem-actor.d.ts +41 -0
  33. package/dist/backend/modem-actor.js +78 -0
  34. package/dist/backend/nm-auto-apn.d.ts +54 -0
  35. package/dist/backend/nm-auto-apn.js +124 -0
  36. package/dist/backend/nm-gsm-fields.d.ts +23 -0
  37. package/dist/backend/nm-gsm-fields.js +107 -0
  38. package/dist/backend/nmcli-nm-port.d.ts +28 -0
  39. package/dist/backend/nmcli-nm-port.js +173 -0
  40. package/dist/backend/nmcli-runner.d.ts +24 -0
  41. package/dist/backend/nmcli-runner.js +35 -0
  42. package/dist/backend/observer.d.ts +37 -0
  43. package/dist/backend/observer.js +219 -0
  44. package/dist/backend/power-contract.d.ts +49 -0
  45. package/dist/backend/power-contract.js +34 -0
  46. package/dist/backend/recovery-attribution.d.ts +32 -0
  47. package/dist/backend/recovery-attribution.js +57 -0
  48. package/dist/backend/recovery-budget.d.ts +45 -0
  49. package/dist/backend/recovery-budget.js +44 -0
  50. package/dist/backend/recovery-ladder.d.ts +94 -0
  51. package/dist/backend/recovery-ladder.js +116 -0
  52. package/dist/backend/router-ethernet.d.ts +19 -0
  53. package/dist/backend/router-ethernet.js +66 -0
  54. package/dist/backend/row-store.d.ts +13 -0
  55. package/dist/backend/row-store.js +82 -0
  56. package/dist/backend/signal-setup.d.ts +30 -0
  57. package/dist/backend/signal-setup.js +92 -0
  58. package/dist/backend/sim-unlock.d.ts +13 -0
  59. package/dist/backend/sim-unlock.js +153 -0
  60. package/dist/backend/transition-preconditions.d.ts +76 -0
  61. package/dist/backend/transition-preconditions.js +64 -0
  62. package/dist/backend/usage/accounting.d.ts +39 -0
  63. package/dist/backend/usage/accounting.js +73 -0
  64. package/dist/backend/usage/billing-cycle.d.ts +11 -0
  65. package/dist/backend/usage/billing-cycle.js +39 -0
  66. package/dist/backend/usage/boot-id.d.ts +6 -0
  67. package/{src/backend/usage/boot-id.ts → dist/backend/usage/boot-id.js} +7 -7
  68. package/dist/backend/usage/index.d.ts +8 -0
  69. package/dist/backend/usage/index.js +11 -0
  70. package/dist/backend/usage/policy-store.d.ts +50 -0
  71. package/dist/backend/usage/policy-store.js +161 -0
  72. package/dist/backend/usage/policy-write.d.ts +65 -0
  73. package/dist/backend/usage/policy-write.js +112 -0
  74. package/dist/backend/usage/proc-net-dev.d.ts +18 -0
  75. package/{src/backend/usage/proc-net-dev.ts → dist/backend/usage/proc-net-dev.js} +39 -47
  76. package/dist/backend/usage/sampler.d.ts +75 -0
  77. package/dist/backend/usage/sampler.js +211 -0
  78. package/dist/backend/usage/store.d.ts +38 -0
  79. package/dist/backend/usage/store.js +126 -0
  80. package/dist/backend/usb-device-snapshot.d.ts +31 -0
  81. package/dist/backend/usb-device-snapshot.js +1 -0
  82. package/dist/backend/usb-enumerator.d.ts +21 -0
  83. package/dist/backend/usb-enumerator.js +153 -0
  84. package/dist/backend/usb-mode-transition.d.ts +29 -0
  85. package/dist/backend/usb-mode-transition.js +196 -0
  86. package/dist/band/band-names.d.ts +42 -0
  87. package/dist/band/band-names.js +150 -0
  88. package/dist/band/certification.d.ts +84 -0
  89. package/dist/band/certification.js +127 -0
  90. package/dist/band/certified-bands.json +4 -0
  91. package/dist/band/index.d.ts +2 -0
  92. package/dist/band/index.js +8 -0
  93. package/dist/capability/detect.d.ts +52 -0
  94. package/dist/capability/detect.js +86 -0
  95. package/dist/capability/five-g-preference.d.ts +104 -0
  96. package/dist/capability/five-g-preference.js +171 -0
  97. package/dist/capability/index.d.ts +3 -0
  98. package/dist/capability/index.js +10 -0
  99. package/dist/capability/support-claim.d.ts +39 -0
  100. package/dist/capability/support-claim.js +81 -0
  101. package/dist/domain/brand.d.ts +10 -0
  102. package/dist/domain/brand.js +21 -0
  103. package/dist/domain/errors.d.ts +32 -0
  104. package/dist/domain/errors.js +48 -0
  105. package/dist/domain/generation.d.ts +8 -0
  106. package/dist/domain/generation.js +12 -0
  107. package/dist/domain/guards.d.ts +6 -0
  108. package/dist/domain/guards.js +127 -0
  109. package/dist/domain/identity.d.ts +109 -0
  110. package/dist/domain/identity.js +86 -0
  111. package/dist/domain/index.d.ts +14 -0
  112. package/dist/domain/index.js +18 -0
  113. package/dist/domain/mm-enums.d.ts +12 -0
  114. package/dist/domain/mm-enums.js +139 -0
  115. package/dist/domain/modem-presentation.d.ts +10 -0
  116. package/dist/domain/modem-presentation.js +39 -0
  117. package/dist/domain/observation.d.ts +39 -0
  118. package/dist/domain/observation.js +4 -0
  119. package/dist/domain/operation.d.ts +118 -0
  120. package/dist/domain/operation.js +85 -0
  121. package/dist/domain/physical-identity.d.ts +43 -0
  122. package/dist/domain/physical-identity.js +113 -0
  123. package/{src/domain/policy.ts → dist/domain/policy.d.ts} +34 -69
  124. package/dist/domain/policy.js +38 -0
  125. package/dist/domain/shadow-divergence.d.ts +27 -0
  126. package/dist/domain/shadow-divergence.js +70 -0
  127. package/dist/domain/snapshot.d.ts +53 -0
  128. package/dist/domain/snapshot.js +75 -0
  129. package/{src/domain/state.ts → dist/domain/state.d.ts} +23 -122
  130. package/dist/domain/state.js +38 -0
  131. package/dist/fcc/coverage.d.ts +63 -0
  132. package/dist/fcc/coverage.js +102 -0
  133. package/dist/fcc/index.d.ts +3 -0
  134. package/dist/fcc/index.js +12 -0
  135. package/dist/fcc/policy-store.d.ts +40 -0
  136. package/dist/fcc/policy-store.js +146 -0
  137. package/dist/fcc/policy-write.d.ts +32 -0
  138. package/dist/fcc/policy-write.js +37 -0
  139. package/dist/hardware/hilink-protocol.d.ts +39 -0
  140. package/dist/hardware/hilink-protocol.js +58 -0
  141. package/dist/hardware/index.d.ts +3 -0
  142. package/dist/hardware/index.js +15 -0
  143. package/dist/hardware/router-parsers.d.ts +89 -0
  144. package/dist/hardware/router-parsers.js +234 -0
  145. package/dist/index.d.ts +19 -0
  146. package/dist/index.js +26 -0
  147. package/dist/journal/codec.d.ts +44 -0
  148. package/dist/journal/codec.js +198 -0
  149. package/dist/journal/engine.d.ts +28 -0
  150. package/dist/journal/engine.js +68 -0
  151. package/dist/journal/entry.d.ts +74 -0
  152. package/dist/journal/entry.js +56 -0
  153. package/dist/journal/index.d.ts +6 -0
  154. package/dist/journal/index.js +6 -0
  155. package/dist/journal/legacy-ceraui.d.ts +73 -0
  156. package/dist/journal/legacy-ceraui.js +227 -0
  157. package/dist/journal/recovery.d.ts +58 -0
  158. package/dist/journal/recovery.js +117 -0
  159. package/dist/journal/store.d.ts +55 -0
  160. package/dist/journal/store.js +150 -0
  161. package/dist/location/fix-state.d.ts +53 -0
  162. package/dist/location/fix-state.js +75 -0
  163. package/dist/location/index.d.ts +2 -0
  164. package/dist/location/index.js +8 -0
  165. package/dist/location/nmea.d.ts +10 -0
  166. package/dist/location/nmea.js +89 -0
  167. package/dist/observations/envelope.d.ts +63 -0
  168. package/dist/observations/envelope.js +77 -0
  169. package/dist/observations/freshness.d.ts +28 -0
  170. package/dist/observations/freshness.js +76 -0
  171. package/dist/observations/index.d.ts +13 -0
  172. package/dist/observations/index.js +24 -0
  173. package/dist/observations/metric.d.ts +47 -0
  174. package/dist/observations/metric.js +73 -0
  175. package/dist/observations/model.d.ts +80 -0
  176. package/dist/observations/model.js +11 -0
  177. package/dist/observations/provenance.d.ts +94 -0
  178. package/dist/observations/provenance.js +67 -0
  179. package/dist/observations/raw.d.ts +42 -0
  180. package/dist/observations/raw.js +146 -0
  181. package/dist/observations/reading.d.ts +51 -0
  182. package/dist/observations/reading.js +65 -0
  183. package/dist/observations/sources/hilink.d.ts +10 -0
  184. package/dist/observations/sources/hilink.js +84 -0
  185. package/dist/observations/sources/modemmanager.d.ts +18 -0
  186. package/dist/observations/sources/modemmanager.js +239 -0
  187. package/dist/observations/sources/router-shared.d.ts +29 -0
  188. package/dist/observations/sources/router-shared.js +68 -0
  189. package/dist/observations/sources/ufi.d.ts +10 -0
  190. package/dist/observations/sources/ufi.js +111 -0
  191. package/dist/observations/sources/zte.d.ts +7 -0
  192. package/dist/observations/sources/zte.js +71 -0
  193. package/dist/observations/state-separation.d.ts +64 -0
  194. package/dist/observations/state-separation.js +52 -0
  195. package/dist/operations/contracts.d.ts +72 -0
  196. package/dist/operations/contracts.js +1 -0
  197. package/dist/operations/index.d.ts +1 -0
  198. package/dist/operations/index.js +1 -0
  199. package/dist/operations/operation-engine.d.ts +16 -0
  200. package/dist/operations/operation-engine.js +194 -0
  201. package/dist/ports/index.d.ts +12 -0
  202. package/{src/ports/index.ts → dist/ports/index.js} +12 -8
  203. package/dist/ports/location.d.ts +87 -0
  204. package/dist/ports/location.js +36 -0
  205. package/dist/ports/modem-manager.d.ts +89 -0
  206. package/dist/ports/modem-manager.js +9 -0
  207. package/dist/ports/mutation-admission.d.ts +27 -0
  208. package/dist/ports/mutation-admission.js +9 -0
  209. package/dist/ports/network-manager.d.ts +68 -0
  210. package/dist/ports/network-manager.js +13 -0
  211. package/{src/ports/observation.ts → dist/ports/observation.d.ts} +16 -27
  212. package/dist/ports/observation.js +7 -0
  213. package/dist/ports/ops.d.ts +44 -0
  214. package/dist/ports/ops.js +16 -0
  215. package/{src/ports/receipts.ts → dist/ports/receipts.d.ts} +5 -27
  216. package/dist/ports/receipts.js +9 -0
  217. package/dist/ports/reconcile.d.ts +33 -0
  218. package/dist/ports/reconcile.js +200 -0
  219. package/dist/ports/resource-ownership.d.ts +29 -0
  220. package/dist/ports/resource-ownership.js +1 -0
  221. package/dist/ports/router.d.ts +19 -0
  222. package/dist/ports/router.js +7 -0
  223. package/dist/ports/sms.d.ts +64 -0
  224. package/dist/ports/sms.js +24 -0
  225. package/dist/ports/uhubctl.d.ts +6 -0
  226. package/dist/ports/uhubctl.js +1 -0
  227. package/dist/providers/contracts.d.ts +124 -0
  228. package/dist/providers/contracts.js +10 -0
  229. package/dist/providers/huawei-hilink/index.d.ts +2 -0
  230. package/dist/providers/huawei-hilink/index.js +2 -0
  231. package/dist/providers/huawei-hilink/operations.d.ts +20 -0
  232. package/dist/providers/huawei-hilink/operations.js +56 -0
  233. package/dist/providers/huawei-hilink/provider.d.ts +52 -0
  234. package/dist/providers/huawei-hilink/provider.js +76 -0
  235. package/dist/providers/huawei-hilink/runtime.d.ts +22 -0
  236. package/dist/providers/huawei-hilink/runtime.js +171 -0
  237. package/dist/providers/huawei-hilink/session.d.ts +28 -0
  238. package/dist/providers/huawei-hilink/session.js +120 -0
  239. package/dist/providers/huawei-hilink/transport.d.ts +19 -0
  240. package/dist/providers/huawei-hilink/transport.js +1 -0
  241. package/dist/providers/index.d.ts +8 -0
  242. package/dist/providers/index.js +8 -0
  243. package/dist/providers/matcher.d.ts +3 -0
  244. package/dist/providers/matcher.js +205 -0
  245. package/dist/providers/modem-manager/errors.d.ts +7 -0
  246. package/dist/providers/modem-manager/errors.js +37 -0
  247. package/dist/providers/modem-manager/generic-operations.d.ts +8 -0
  248. package/dist/providers/modem-manager/generic-operations.js +207 -0
  249. package/dist/providers/modem-manager/index.d.ts +3 -0
  250. package/dist/providers/modem-manager/index.js +3 -0
  251. package/dist/providers/modem-manager/module-operations.d.ts +21 -0
  252. package/dist/providers/modem-manager/module-operations.js +118 -0
  253. package/dist/providers/modem-manager/provider.d.ts +39 -0
  254. package/dist/providers/modem-manager/provider.js +152 -0
  255. package/dist/providers/modem-manager/snapshot.d.ts +5 -0
  256. package/dist/providers/modem-manager/snapshot.js +204 -0
  257. package/dist/providers/modem-manager/types.d.ts +135 -0
  258. package/dist/providers/modem-manager/types.js +1 -0
  259. package/dist/providers/network-manager/adapter.d.ts +71 -0
  260. package/dist/providers/network-manager/adapter.js +348 -0
  261. package/dist/providers/network-manager/index.d.ts +2 -0
  262. package/dist/providers/network-manager/index.js +2 -0
  263. package/dist/providers/network-manager/types.d.ts +163 -0
  264. package/dist/providers/network-manager/types.js +77 -0
  265. package/dist/providers/registry.d.ts +13 -0
  266. package/dist/providers/registry.js +33 -0
  267. package/dist/providers/ufi-himi/index.d.ts +6 -0
  268. package/dist/providers/ufi-himi/index.js +6 -0
  269. package/dist/providers/ufi-himi/operations.d.ts +41 -0
  270. package/dist/providers/ufi-himi/operations.js +66 -0
  271. package/dist/providers/ufi-himi/prohibitions.d.ts +62 -0
  272. package/dist/providers/ufi-himi/prohibitions.js +88 -0
  273. package/dist/providers/ufi-himi/provider.d.ts +41 -0
  274. package/dist/providers/ufi-himi/provider.js +204 -0
  275. package/dist/providers/ufi-himi/qualcomm-evidence.d.ts +32 -0
  276. package/dist/providers/ufi-himi/qualcomm-evidence.js +51 -0
  277. package/dist/providers/ufi-himi/session.d.ts +46 -0
  278. package/dist/providers/ufi-himi/session.js +92 -0
  279. package/dist/providers/ufi-himi/transport.d.ts +29 -0
  280. package/dist/providers/ufi-himi/transport.js +25 -0
  281. package/dist/providers/zte-goform/index.d.ts +2 -0
  282. package/dist/providers/zte-goform/index.js +2 -0
  283. package/dist/providers/zte-goform/provider.d.ts +51 -0
  284. package/dist/providers/zte-goform/provider.js +101 -0
  285. package/dist/providers/zte-goform/session.d.ts +17 -0
  286. package/dist/providers/zte-goform/session.js +133 -0
  287. package/dist/providers/zte-goform/transport.d.ts +16 -0
  288. package/dist/providers/zte-goform/transport.js +1 -0
  289. package/dist/radio/band-truth.d.ts +50 -0
  290. package/dist/radio/band-truth.js +92 -0
  291. package/dist/radio/index.d.ts +3 -0
  292. package/dist/radio/index.js +10 -0
  293. package/dist/radio/mode-combinations.d.ts +78 -0
  294. package/dist/radio/mode-combinations.js +198 -0
  295. package/dist/radio/mode-truth.d.ts +67 -0
  296. package/dist/radio/mode-truth.js +112 -0
  297. package/dist/redact.d.ts +15 -0
  298. package/dist/redact.js +189 -0
  299. package/dist/safety/composition-root.d.ts +28 -0
  300. package/dist/safety/composition-root.js +65 -0
  301. package/dist/safety/flock-resource-ownership.d.ts +11 -0
  302. package/dist/safety/flock-resource-ownership.js +151 -0
  303. package/dist/safety/index.d.ts +2 -0
  304. package/dist/safety/index.js +2 -0
  305. package/dist/sms/dbus-messaging.d.ts +17 -0
  306. package/dist/sms/dbus-messaging.js +185 -0
  307. package/dist/sms/inbox-store.d.ts +10 -0
  308. package/dist/sms/inbox-store.js +82 -0
  309. package/dist/sms/index.d.ts +4 -0
  310. package/dist/sms/index.js +10 -0
  311. package/dist/sms/mmcli-parse.d.ts +54 -0
  312. package/dist/sms/mmcli-parse.js +224 -0
  313. package/dist/sms/normalize.d.ts +42 -0
  314. package/dist/sms/normalize.js +95 -0
  315. package/dist/testing/domain-fakes.d.ts +58 -0
  316. package/dist/testing/domain-fakes.js +98 -0
  317. package/dist/testing/index.d.ts +2 -0
  318. package/dist/testing/index.js +17 -0
  319. package/dist/testing/provider-fakes.d.ts +45 -0
  320. package/dist/testing/provider-fakes.js +64 -0
  321. package/dist/transport/calls.d.ts +9 -0
  322. package/dist/transport/calls.js +88 -0
  323. package/dist/transport/codec.d.ts +3 -0
  324. package/dist/transport/codec.js +207 -0
  325. package/dist/transport/dbus-native.d.ts +57 -0
  326. package/dist/transport/dbus-native.js +17 -0
  327. package/dist/transport/errors.d.ts +21 -0
  328. package/{src/transport/errors.ts → dist/transport/errors.js} +34 -46
  329. package/dist/transport/index.d.ts +4 -0
  330. package/dist/transport/index.js +9 -0
  331. package/dist/transport/signals.d.ts +15 -0
  332. package/dist/transport/signals.js +123 -0
  333. package/dist/transport/signature.d.ts +7 -0
  334. package/dist/transport/signature.js +94 -0
  335. package/dist/transport/transport.d.ts +2 -0
  336. package/dist/transport/transport.js +202 -0
  337. package/dist/transport/types.d.ts +61 -0
  338. package/dist/transport/types.js +19 -0
  339. package/dist/usb-mode/catalog-schema.d.ts +139 -0
  340. package/dist/usb-mode/catalog-schema.js +97 -0
  341. package/dist/usb-mode/catalog.d.ts +21 -0
  342. package/{src/usb-mode/catalog.ts → dist/usb-mode/catalog.js} +10 -32
  343. package/dist/usb-mode/certified-catalog.json +67 -0
  344. package/dist/usb-mode/index.d.ts +5 -0
  345. package/dist/usb-mode/index.js +15 -0
  346. package/dist/usb-mode/ingestion.d.ts +111 -0
  347. package/dist/usb-mode/ingestion.js +187 -0
  348. package/dist/usb-mode/promotion-review.d.ts +21 -0
  349. package/dist/usb-mode/promotion-review.js +87 -0
  350. package/dist/usb-mode/usb-devices-parse.d.ts +36 -0
  351. package/dist/usb-mode/usb-devices-parse.js +157 -0
  352. package/dist/ussd/calls.d.ts +32 -0
  353. package/dist/ussd/calls.js +96 -0
  354. package/dist/ussd/index.d.ts +5 -0
  355. package/dist/ussd/index.js +12 -0
  356. package/dist/ussd/mm-ussd.d.ts +37 -0
  357. package/dist/ussd/mm-ussd.js +205 -0
  358. package/dist/ussd/refusal.d.ts +53 -0
  359. package/dist/ussd/refusal.js +154 -0
  360. package/dist/ussd/registration.d.ts +20 -0
  361. package/dist/ussd/registration.js +101 -0
  362. package/dist/ussd/session.d.ts +84 -0
  363. package/dist/ussd/session.js +163 -0
  364. package/package.json +38 -4
  365. package/src/backend/at-lease.test.ts +0 -106
  366. package/src/backend/at-lease.ts +0 -158
  367. package/src/backend/cell-info.test.ts +0 -154
  368. package/src/backend/cell-info.ts +0 -160
  369. package/src/backend/device-classifier.test.ts +0 -168
  370. package/src/backend/device-classifier.ts +0 -248
  371. package/src/backend/enrichment.ts +0 -96
  372. package/src/backend/features.test.ts +0 -162
  373. package/src/backend/features.ts +0 -179
  374. package/src/backend/identity-ladder.test.ts +0 -117
  375. package/src/backend/identity-ladder.ts +0 -221
  376. package/src/backend/identity-registry.test.ts +0 -89
  377. package/src/backend/identity-registry.ts +0 -151
  378. package/src/backend/index.ts +0 -221
  379. package/src/backend/lifecycle-interlock.ts +0 -38
  380. package/src/backend/managed-objects.ts +0 -108
  381. package/src/backend/mapping.ts +0 -160
  382. package/src/backend/mm-backend.ts +0 -191
  383. package/src/backend/mm-mutations.ts +0 -228
  384. package/src/backend/modem-actor.test.ts +0 -95
  385. package/src/backend/modem-actor.ts +0 -112
  386. package/src/backend/nm-auto-apn.ts +0 -161
  387. package/src/backend/nm-gsm-fields.ts +0 -122
  388. package/src/backend/nmcli-nm-port.ts +0 -228
  389. package/src/backend/nmcli-runner.ts +0 -52
  390. package/src/backend/observer.ts +0 -297
  391. package/src/backend/power-contract.test.ts +0 -40
  392. package/src/backend/power-contract.ts +0 -83
  393. package/src/backend/recovery-attribution.test.ts +0 -102
  394. package/src/backend/recovery-attribution.ts +0 -86
  395. package/src/backend/recovery-budget.test.ts +0 -64
  396. package/src/backend/recovery-budget.ts +0 -84
  397. package/src/backend/recovery-ladder.test.ts +0 -257
  398. package/src/backend/recovery-ladder.ts +0 -249
  399. package/src/backend/router-ethernet.test.ts +0 -71
  400. package/src/backend/router-ethernet.ts +0 -90
  401. package/src/backend/row-store.ts +0 -105
  402. package/src/backend/signal-setup.ts +0 -112
  403. package/src/backend/sim-unlock.ts +0 -193
  404. package/src/backend/transition-preconditions.ts +0 -149
  405. package/src/backend/usage/accounting.test.ts +0 -147
  406. package/src/backend/usage/accounting.ts +0 -123
  407. package/src/backend/usage/billing-cycle.test.ts +0 -62
  408. package/src/backend/usage/billing-cycle.ts +0 -45
  409. package/src/backend/usage/index.ts +0 -37
  410. package/src/backend/usage/proc-net-dev.test.ts +0 -56
  411. package/src/backend/usage/sampler.test.ts +0 -219
  412. package/src/backend/usage/sampler.ts +0 -228
  413. package/src/backend/usage/store.test.ts +0 -148
  414. package/src/backend/usage/store.ts +0 -177
  415. package/src/backend/usb-enumerator.test.ts +0 -87
  416. package/src/backend/usb-enumerator.ts +0 -181
  417. package/src/backend/usb-mode-transition.test.ts +0 -323
  418. package/src/backend/usb-mode-transition.ts +0 -253
  419. package/src/domain/brand.ts +0 -29
  420. package/src/domain/errors.ts +0 -77
  421. package/src/domain/guards.test.ts +0 -218
  422. package/src/domain/guards.ts +0 -144
  423. package/src/domain/identity.test.ts +0 -83
  424. package/src/domain/identity.ts +0 -165
  425. package/src/domain/index.ts +0 -12
  426. package/src/domain/snapshot.test.ts +0 -266
  427. package/src/domain/snapshot.ts +0 -120
  428. package/src/index.test.ts +0 -6
  429. package/src/index.ts +0 -15
  430. package/src/ports/README.md +0 -61
  431. package/src/ports/forbidden-surface.test.ts +0 -241
  432. package/src/ports/modem-manager.ts +0 -72
  433. package/src/ports/network-manager.ts +0 -87
  434. package/src/ports/ops.ts +0 -60
  435. package/src/ports/ops.type-test.ts +0 -39
  436. package/src/ports/receipts.test.ts +0 -153
  437. package/src/ports/reconcile.test.ts +0 -152
  438. package/src/ports/reconcile.ts +0 -338
  439. package/src/ports/router.ts +0 -29
  440. package/src/redact.test.ts +0 -82
  441. package/src/redact.ts +0 -73
  442. package/src/transport/README.md +0 -65
  443. package/src/transport/calls.ts +0 -113
  444. package/src/transport/characterization.test.ts +0 -260
  445. package/src/transport/codec.test.ts +0 -118
  446. package/src/transport/codec.ts +0 -240
  447. package/src/transport/conformance-python.test.ts +0 -152
  448. package/src/transport/conformance-same-lib.test.ts +0 -115
  449. package/src/transport/dbus-native-lib.d.ts +0 -19
  450. package/src/transport/dbus-native.ts +0 -85
  451. package/src/transport/index.ts +0 -30
  452. package/src/transport/no-library-leak.test.ts +0 -61
  453. package/src/transport/reliability.test.ts +0 -173
  454. package/src/transport/signals.ts +0 -150
  455. package/src/transport/signature.ts +0 -110
  456. package/src/transport/test-support/fake-service.ts +0 -168
  457. package/src/transport/test-support/independent-producer.py +0 -110
  458. package/src/transport/test-support/private-bus.ts +0 -66
  459. package/src/transport/transport.ts +0 -250
  460. package/src/transport/types.ts +0 -118
  461. package/src/usb-mode/catalog-schema.test.ts +0 -181
  462. package/src/usb-mode/catalog-schema.ts +0 -113
  463. package/src/usb-mode/certified-catalog.json +0 -67
  464. package/src/usb-mode/index.ts +0 -27
@@ -1,161 +0,0 @@
1
- // Auto-APN: boot capability probe, the two atomic transition argv builders, and the
2
- // activation-result classifier.
3
- //
4
- // Auto-APN (`apn:"auto"` ⇒ NM `gsm.auto-config yes`) needs NetworkManager >= 1.22
5
- // (bookworm ships 1.42). A boot capability probe reads nmcli's version — replacing any
6
- // implicit/hardcoded gating — and when NM is too old an auto policy resolves to
7
- // `unsupported` up front with the `autoApnUnavailable` advisory (no modify at all).
8
- //
9
- // When NM DOES support auto-config a bad/unknown SIM can still have no MBPI match: the
10
- // connection fails to activate with a GSM_APN_FAILED device-state reason. That is
11
- // classified at ACTIVATION, not guessed ahead of time — reactivate the exact
12
- // (uuid, ifname) pair, await the terminal state, then:
13
- // GSM_APN_FAILED under an auto profile → unsupported + autoApnUnavailable advisory
14
- // any other activation error → failed
15
- // nmcli's own activation wait timed out → pending
16
-
17
- import { type Receipt, receipt } from '../ports';
18
- import { cliArg, passwordFlags } from './nm-gsm-fields';
19
- import { type NmcliResult, type NmcliRunner, runNmcli } from './nmcli-runner';
20
-
21
- /** The single advisory Auto-APN raises when it cannot be honored. */
22
- export const AUTO_APN_ADVISORY = 'autoApnUnavailable';
23
- export type AutoApnAdvisory = typeof AUTO_APN_ADVISORY;
24
-
25
- /** The outcome of an Auto-APN transition: a receipt plus an optional advisory flag. */
26
- export interface AutoApnTransitionResult {
27
- readonly receipt: Receipt;
28
- readonly advisory?: AutoApnAdvisory;
29
- }
30
-
31
- /** Explicit APN + credentials for the manual direction of a transition. */
32
- export interface ManualApn {
33
- readonly apn: string;
34
- readonly username?: string;
35
- readonly password?: string;
36
- }
37
-
38
- const AUTO_CONFIG_MIN_MAJOR = 1;
39
- const AUTO_CONFIG_MIN_MINOR = 22;
40
-
41
- /** nmcli's own `--wait` timeout exit status (nmcli(1): 3 = timeout expired). */
42
- const NMCLI_EXIT_TIMEOUT = 3;
43
-
44
- const APN_FAILED = /gsm[_-]?apn[_-]?failed/i;
45
-
46
- /** Parse `nmcli tool, version 1.42.4` → `{ major, minor }` (null on garbage). */
47
- export function parseNmVersion(output: string): { major: number; minor: number } | null {
48
- const match = /version\s+(\d+)\.(\d+)/i.exec(output);
49
- if (match?.[1] === undefined || match[2] === undefined) {
50
- return null;
51
- }
52
- return { major: Number(match[1]), minor: Number(match[2]) };
53
- }
54
-
55
- /** `true` iff the parsed NM version is >= 1.22 (auto-config support floor). */
56
- export function autoApnSupportedByVersion(version: { major: number; minor: number }): boolean {
57
- if (version.major !== AUTO_CONFIG_MIN_MAJOR) {
58
- return version.major > AUTO_CONFIG_MIN_MAJOR;
59
- }
60
- return version.minor >= AUTO_CONFIG_MIN_MINOR;
61
- }
62
-
63
- /**
64
- * Decide from a `nmcli --version` result whether Auto-APN is available on this install.
65
- * Fail-CLOSED — an unreadable/garbled version is "not capable", so we never claim
66
- * Auto-APN works when we cannot prove it does.
67
- */
68
- export function autoApnCapableFromVersion(result: NmcliResult): boolean {
69
- if (result.exitCode !== 0) {
70
- return false;
71
- }
72
- const version = parseNmVersion(result.stdout);
73
- return version !== null && autoApnSupportedByVersion(version);
74
- }
75
-
76
- /** Boot capability probe: run `nmcli --version` and resolve Auto-APN availability. */
77
- export async function probeAutoApnCapability(runner: NmcliRunner): Promise<boolean> {
78
- return autoApnCapableFromVersion(await runNmcli(runner, ['--version']));
79
- }
80
-
81
- /**
82
- * ONE atomic `connection modify` that flips a profile to Auto-APN: clears every
83
- * credential AND sets `gsm.auto-config yes` in the SAME invocation. Order/atomicity
84
- * matter — NM rejects `auto-config yes` while any credential is still set, so the clear
85
- * and the enable MUST land together (never two modifies).
86
- */
87
- export function toAutoArgs(id: string): string[] {
88
- return [
89
- 'connection',
90
- 'modify',
91
- id,
92
- 'gsm.apn',
93
- '',
94
- 'gsm.username',
95
- '',
96
- 'gsm.password',
97
- '',
98
- 'gsm.password-flags',
99
- '4',
100
- 'gsm.auto-config',
101
- 'yes',
102
- ];
103
- }
104
-
105
- /**
106
- * The exact reverse: ONE atomic `connection modify` restoring explicit APN + creds and
107
- * clearing `gsm.auto-config`, with password-flags matching the (new) password.
108
- */
109
- export function toManualArgs(id: string, creds: ManualApn): string[] {
110
- return [
111
- 'connection',
112
- 'modify',
113
- id,
114
- 'gsm.apn',
115
- cliArg(creds.apn),
116
- 'gsm.username',
117
- cliArg(creds.username),
118
- 'gsm.password',
119
- cliArg(creds.password),
120
- 'gsm.password-flags',
121
- passwordFlags(creds.password),
122
- 'gsm.auto-config',
123
- 'no',
124
- ];
125
- }
126
-
127
- /**
128
- * Classify a `connection up` result into a receipt (+ advisory). `underAuto` marks that
129
- * the profile being activated is an Auto-APN profile, so an APN failure is the "no MBPI
130
- * match for this SIM" case → unsupported + advisory rather than a hard fail.
131
- */
132
- export function classifyActivation(
133
- result: NmcliResult,
134
- options: { readonly underAuto: boolean },
135
- ): AutoApnTransitionResult {
136
- if (result.exitCode === 0) {
137
- return { receipt: receipt('connection', 'applied', 'connection activated') };
138
- }
139
- if (result.exitCode === NMCLI_EXIT_TIMEOUT || /timeout/i.test(result.stderr)) {
140
- return {
141
- receipt: receipt(
142
- 'connection',
143
- 'pending',
144
- 'activation wait timed out; terminal state unknown',
145
- ),
146
- };
147
- }
148
- if (options.underAuto && (APN_FAILED.test(result.stderr) || APN_FAILED.test(result.stdout))) {
149
- return {
150
- receipt: receipt(
151
- 'connection',
152
- 'unsupported',
153
- 'Auto-APN found no operator match for this SIM',
154
- ),
155
- advisory: AUTO_APN_ADVISORY,
156
- };
157
- }
158
- return {
159
- receipt: receipt('connection', 'failed', result.stderr || 'connection activation failed'),
160
- };
161
- }
@@ -1,122 +0,0 @@
1
- // Pure nmcli GSM-connection argv builders — the FULL nine-field write parity today's
2
- // CeraUI carried (modem-registration.ts `sanitizeModemConfigForNetworkManager` +
3
- // `addConnectionForModem`), rebuilt from scratch for the device-exact adapter so the
4
- // greenfield port loses nothing the wrap-first path wrote.
5
- //
6
- // The nine fields and their exact NM conventions (`gsm.*` + `connection.*` setting
7
- // semantics) are the contract A4.1 must not regress:
8
- // gsm.apn / gsm.username / gsm.password — creds ("" when unset)
9
- // gsm.password-flags — "4" NOT_REQUIRED (no password),
10
- // "0" NONE / system-stored (password set)
11
- // gsm.home-only — "yes" ⇒ roaming DISABLED
12
- // gsm.network-id — manual operator PLMN, only while roaming
13
- // gsm.auto-config — "yes" ⇒ Auto-APN (creds MUST be empty)
14
- // connection.autoconnect — always "yes"
15
- // connection.autoconnect-retries — "2"
16
-
17
- import type { ConnectionId, GsmProfile, GsmProfileInput, GsmProfilePatch } from '../ports';
18
-
19
- // Empty-string fallback; Bun runtime limitation with empty CLI args: a bare
20
- // `undefined` reaching `Bun.spawn` argv drops the slot and shifts every following
21
- // token, so every optional nmcli value is coerced to "" first — the same `value || ""`
22
- // convention CeraUI documented on gsm.apn / gsm.username / gsm.password.
23
- export const cliArg = (value: string | undefined): string => value || '';
24
-
25
- /** NM secret-flags: no password ⇒ NOT_REQUIRED ("4"); password set ⇒ NONE ("0"). */
26
- export const passwordFlags = (password: string | undefined): string => (password ? '0' : '4');
27
-
28
- /** `gsm.network-id` — the manual operator id, and only while roaming (home-only off). */
29
- const networkIdField = (input: GsmProfileInput): string =>
30
- input.homeOnly ? '' : cliArg(input.networkId);
31
-
32
- /**
33
- * The nine `gsm.*` + `connection.*` key/value pairs for a profile, in stable order.
34
- * With `autoConfig` set the creds are forced empty (NM rejects `auto-config yes` while
35
- * any credential is present — nm-setting-gsm.c:440-447), so an Auto-APN create is always
36
- * NM-valid by construction.
37
- */
38
- export function gsmFieldPairs(input: GsmProfileInput): Array<[string, string]> {
39
- const auto = input.autoConfig;
40
- return [
41
- ['gsm.apn', auto ? '' : cliArg(input.apn)],
42
- ['gsm.username', auto ? '' : cliArg(input.username)],
43
- ['gsm.password', auto ? '' : cliArg(input.password)],
44
- ['gsm.password-flags', passwordFlags(auto ? undefined : input.password)],
45
- ['gsm.home-only', input.homeOnly ? 'yes' : 'no'],
46
- ['gsm.network-id', networkIdField(input)],
47
- ['gsm.auto-config', auto ? 'yes' : 'no'],
48
- ['connection.autoconnect', 'yes'],
49
- ['connection.autoconnect-retries', '2'],
50
- ];
51
- }
52
-
53
- /** Flatten key/value pairs into the alternating argv nmcli expects. */
54
- export function flattenPairs(pairs: ReadonlyArray<readonly [string, string]>): string[] {
55
- return pairs.flat();
56
- }
57
-
58
- /** `connection add type gsm con-name <name> <nine fields>` — device-exact create argv. */
59
- export function createGsmArgs(input: GsmProfileInput): string[] {
60
- return [
61
- 'connection',
62
- 'add',
63
- 'type',
64
- 'gsm',
65
- 'con-name',
66
- input.connectionName,
67
- ...flattenPairs(gsmFieldPairs(input)),
68
- ];
69
- }
70
-
71
- /**
72
- * The key/value pairs for a `connection modify`. Roaming (home-only) and the manual
73
- * operator id move together — turning roaming off clears `gsm.network-id`, honoring the
74
- * `gsm.network-id = roaming ? id : ""` invariant in the SAME modify.
75
- */
76
- export function patchPairs(patch: GsmProfilePatch): string[] {
77
- const pairs: string[] = [];
78
- if (patch.connectionName !== undefined) {
79
- pairs.push('connection.id', patch.connectionName);
80
- }
81
- if (patch.apn !== undefined) {
82
- pairs.push('gsm.apn', cliArg(patch.apn));
83
- }
84
- if (patch.username !== undefined) {
85
- pairs.push('gsm.username', cliArg(patch.username));
86
- }
87
- if (patch.password !== undefined) {
88
- pairs.push(
89
- 'gsm.password',
90
- cliArg(patch.password),
91
- 'gsm.password-flags',
92
- passwordFlags(patch.password),
93
- );
94
- }
95
- if (patch.autoConfig !== undefined) {
96
- pairs.push('gsm.auto-config', patch.autoConfig ? 'yes' : 'no');
97
- }
98
- if (patch.homeOnly !== undefined) {
99
- pairs.push('gsm.home-only', patch.homeOnly ? 'yes' : 'no');
100
- pairs.push('gsm.network-id', patch.homeOnly ? '' : cliArg(patch.networkId));
101
- } else if (patch.networkId !== undefined) {
102
- pairs.push('gsm.network-id', cliArg(patch.networkId));
103
- }
104
- return pairs;
105
- }
106
-
107
- /** Map an nmcli terse readback back onto a `GsmProfile` (inverse of the write parity). */
108
- export function buildProfile(id: ConnectionId, settings: Map<string, string>): GsmProfile {
109
- const username = settings.get('gsm.username') ?? '';
110
- const password = settings.get('gsm.password') ?? '';
111
- const networkId = settings.get('gsm.network-id') ?? '';
112
- return {
113
- connectionId: id,
114
- connectionName: settings.get('connection.id') ?? '',
115
- apn: settings.get('gsm.apn') ?? '',
116
- homeOnly: settings.get('gsm.home-only') === 'yes',
117
- autoConfig: settings.get('gsm.auto-config') === 'yes',
118
- ...(username !== '' ? { username } : {}),
119
- ...(password !== '' ? { password } : {}),
120
- ...(networkId !== '' ? { networkId } : {}),
121
- };
122
- }
@@ -1,228 +0,0 @@
1
- // The device-exact NetworkManager adapter: a FRESH nmcli-based `NetworkManagerPort`
2
- // (no reused CeraUI code) with the full nine-field GSM write parity, verify-then-
3
- // device-disconnect deactivation (never id-only `connection down`), abandoned-lease
4
- // quiesce, and atomic Auto-APN transitions.
5
-
6
- import { epochMillis } from '../domain';
7
- import {
8
- type ConnectionId,
9
- connectionId,
10
- type DeviceIfname,
11
- type GsmProfile,
12
- type GsmProfileInput,
13
- type GsmProfilePatch,
14
- type NetworkManagerPort,
15
- type QuiesceLease,
16
- type Receipt,
17
- receipt,
18
- } from '../ports';
19
- import {
20
- AUTO_APN_ADVISORY,
21
- type AutoApnTransitionResult,
22
- classifyActivation,
23
- type ManualApn,
24
- toAutoArgs,
25
- toManualArgs,
26
- } from './nm-auto-apn';
27
- import { buildProfile, createGsmArgs, patchPairs } from './nm-gsm-fields';
28
- import { type NmcliResult, type NmcliRunner, parseTerse, runNmcli } from './nmcli-runner';
29
-
30
- const READBACK_FIELDS = [
31
- 'connection.id',
32
- 'gsm.apn',
33
- 'gsm.username',
34
- 'gsm.password',
35
- 'gsm.password-flags',
36
- 'gsm.home-only',
37
- 'gsm.network-id',
38
- 'gsm.auto-config',
39
- 'connection.autoconnect',
40
- 'connection.autoconnect-retries',
41
- ];
42
-
43
- /** Default abandoned-lease TTL: a held quiesce lease older than this auto-releases. */
44
- const DEFAULT_LEASE_TTL_MS = 60_000;
45
-
46
- export interface NmcliNmPortOptions {
47
- readonly runner: NmcliRunner;
48
- /** Resolved once at boot by the capability probe; default `true`. */
49
- readonly autoApnCapable?: boolean;
50
- readonly leaseTtlMs?: number;
51
- readonly now?: () => number;
52
- }
53
-
54
- export class NmcliNmPort implements NetworkManagerPort {
55
- readonly #runner: NmcliRunner;
56
- readonly #autoApnCapable: boolean;
57
- readonly #leaseTtlMs: number;
58
- readonly #now: () => number;
59
- readonly #leases = new Set<QuiesceLease>();
60
-
61
- constructor(options: NmcliNmPortOptions) {
62
- this.#runner = options.runner;
63
- this.#autoApnCapable = options.autoApnCapable ?? true;
64
- this.#leaseTtlMs = options.leaseTtlMs ?? DEFAULT_LEASE_TTL_MS;
65
- this.#now = options.now ?? Date.now;
66
- }
67
-
68
- async createGsmProfile(profile: GsmProfileInput): Promise<GsmProfile> {
69
- const result = await runNmcli(this.#runner, createGsmArgs(profile));
70
- const uuid = /\(([^)]+)\) successfully added/.exec(result.stdout)?.[1];
71
- if (uuid === undefined) {
72
- throw new Error(`nmcli connection add failed: ${result.stderr || result.stdout}`);
73
- }
74
- return this.#requireProfile(connectionId(uuid));
75
- }
76
-
77
- async readGsmProfile(id: ConnectionId): Promise<GsmProfile | undefined> {
78
- const result = await runNmcli(this.#runner, [
79
- '-t',
80
- '-f',
81
- READBACK_FIELDS.join(','),
82
- 'connection',
83
- 'show',
84
- id,
85
- ]);
86
- if (result.exitCode !== 0) {
87
- return undefined;
88
- }
89
- return buildProfile(id, parseTerse(result.stdout));
90
- }
91
-
92
- async updateGsmProfile(id: ConnectionId, patch: GsmProfilePatch): Promise<GsmProfile> {
93
- const result = await runNmcli(this.#runner, ['connection', 'modify', id, ...patchPairs(patch)]);
94
- if (result.exitCode !== 0) {
95
- throw new Error(`nmcli connection modify failed: ${result.stderr}`);
96
- }
97
- return this.#requireProfile(id);
98
- }
99
-
100
- async deleteGsmProfile(id: ConnectionId): Promise<void> {
101
- await runNmcli(this.#runner, ['connection', 'delete', id]);
102
- }
103
-
104
- async activate(id: ConnectionId, ifname: DeviceIfname): Promise<Receipt> {
105
- const result = await runNmcli(this.#runner, ['connection', 'up', id, 'ifname', ifname]);
106
- return activationReceipt(result, `activated ${id} on ${ifname}`);
107
- }
108
-
109
- async deactivate(id: ConnectionId, ifname: DeviceIfname): Promise<Receipt> {
110
- if ((await this.#activeUuidOn(ifname)) !== id) {
111
- return receipt('enabled', 'applied', `${id} not active on ${ifname}; nothing to deactivate`);
112
- }
113
- const result = await runNmcli(this.#runner, ['device', 'disconnect', ifname]);
114
- return activationReceipt(result, `deactivated ${id} on ${ifname}`);
115
- }
116
-
117
- async acquireQuiesceLease(id: ConnectionId, ifname: DeviceIfname): Promise<QuiesceLease> {
118
- if ((await this.#activeUuidOn(ifname)) === id) {
119
- await runNmcli(this.#runner, ['device', 'disconnect', ifname]);
120
- }
121
- const lease: QuiesceLease = {
122
- connectionId: id,
123
- deviceIfname: ifname,
124
- acquiredAt: epochMillis(this.#now()),
125
- };
126
- this.#leases.add(lease);
127
- return lease;
128
- }
129
-
130
- async releaseQuiesceLease(lease: QuiesceLease): Promise<void> {
131
- if (this.#leases.delete(lease)) {
132
- await this.#reactivate(lease);
133
- }
134
- }
135
-
136
- /** Reactivate every lease held past the TTL — the abandoned-lease watchdog. */
137
- async sweepExpiredLeases(now: number = this.#now()): Promise<void> {
138
- for (const lease of [...this.#leases]) {
139
- if (now - lease.acquiredAt >= this.#leaseTtlMs) {
140
- this.#leases.delete(lease);
141
- await this.#reactivate(lease);
142
- }
143
- }
144
- }
145
-
146
- /** Flip a profile to Auto-APN via ONE atomic modify, then reactivate + classify. */
147
- async transitionToAuto(id: ConnectionId, ifname: DeviceIfname): Promise<AutoApnTransitionResult> {
148
- if (!this.#autoApnCapable) {
149
- return {
150
- receipt: receipt('connection', 'unsupported', 'Auto-APN requires NetworkManager >= 1.22'),
151
- advisory: AUTO_APN_ADVISORY,
152
- };
153
- }
154
- const modify = await runNmcli(this.#runner, toAutoArgs(id));
155
- if (modify.exitCode !== 0) {
156
- return {
157
- receipt: receipt('connection', 'failed', modify.stderr || 'auto-config modify rejected'),
158
- };
159
- }
160
- return this.#reactivateAndClassify(id, ifname, true);
161
- }
162
-
163
- /** Flip a profile to manual APN via ONE atomic modify, then reactivate + classify. */
164
- async transitionToManual(
165
- id: ConnectionId,
166
- ifname: DeviceIfname,
167
- creds: ManualApn,
168
- ): Promise<AutoApnTransitionResult> {
169
- const modify = await runNmcli(this.#runner, toManualArgs(id, creds));
170
- if (modify.exitCode !== 0) {
171
- return {
172
- receipt: receipt('connection', 'failed', modify.stderr || 'manual-apn modify rejected'),
173
- };
174
- }
175
- return this.#reactivateAndClassify(id, ifname, false);
176
- }
177
-
178
- async #reactivateAndClassify(
179
- id: ConnectionId,
180
- ifname: DeviceIfname,
181
- underAuto: boolean,
182
- ): Promise<AutoApnTransitionResult> {
183
- const up = await runNmcli(this.#runner, ['connection', 'up', id, 'ifname', ifname]);
184
- return classifyActivation(up, { underAuto });
185
- }
186
-
187
- async #reactivate(lease: QuiesceLease): Promise<void> {
188
- await runNmcli(this.#runner, [
189
- 'connection',
190
- 'up',
191
- lease.connectionId,
192
- 'ifname',
193
- lease.deviceIfname,
194
- ]);
195
- }
196
-
197
- async #activeUuidOn(ifname: DeviceIfname): Promise<string | undefined> {
198
- const result = await runNmcli(this.#runner, [
199
- '-t',
200
- '-f',
201
- 'UUID,DEVICE',
202
- 'connection',
203
- 'show',
204
- '--active',
205
- ]);
206
- for (const line of result.stdout.split('\n')) {
207
- const [uuid, device] = line.split(':');
208
- if (device === ifname && uuid) {
209
- return uuid;
210
- }
211
- }
212
- return undefined;
213
- }
214
-
215
- async #requireProfile(id: ConnectionId): Promise<GsmProfile> {
216
- const profile = await this.readGsmProfile(id);
217
- if (profile === undefined) {
218
- throw new Error(`nmcli: profile ${id} did not read back`);
219
- }
220
- return profile;
221
- }
222
- }
223
-
224
- function activationReceipt(result: NmcliResult, appliedReason: string): Receipt {
225
- return result.exitCode === 0
226
- ? receipt('enabled', 'applied', appliedReason)
227
- : receipt('enabled', 'failed', result.stderr || 'nmcli returned a non-zero exit code');
228
- }
@@ -1,52 +0,0 @@
1
- // The nmcli invocation seam. The shipping adapter spawns the real `nmcli`; tests
2
- // inject A2.3's stateful in-memory runner (structurally the same `run(argv)`). The
3
- // port never spawns directly — it only ever talks to an injected `NmcliRunner`, so
4
- // the exact code path exercised under test is the one that runs on-device.
5
-
6
- export interface NmcliResult {
7
- readonly stdout: string;
8
- readonly stderr: string;
9
- readonly exitCode: number;
10
- }
11
-
12
- /**
13
- * A runner over nmcli argv. `run` may be synchronous (the stateful test double) or
14
- * asynchronous (the real `Bun.spawn` adapter) — the port awaits either way.
15
- */
16
- export interface NmcliRunner {
17
- run(argv: readonly string[]): NmcliResult | Promise<NmcliResult>;
18
- }
19
-
20
- /** Await a runner's result whether it returned synchronously or as a promise. */
21
- export async function runNmcli(runner: NmcliRunner, argv: readonly string[]): Promise<NmcliResult> {
22
- return runner.run(argv);
23
- }
24
-
25
- /**
26
- * The device-exact runner: spawns the real `nmcli` via Bun. Every argv the port
27
- * builds is passed verbatim, so what the tests assert against the stateful stub is
28
- * byte-for-byte what runs on the device.
29
- */
30
- export class SpawnNmcliRunner implements NmcliRunner {
31
- async run(argv: readonly string[]): Promise<NmcliResult> {
32
- const proc = Bun.spawn(['nmcli', ...argv], { stdout: 'pipe', stderr: 'pipe' });
33
- const [stdout, stderr, exitCode] = await Promise.all([
34
- new Response(proc.stdout).text(),
35
- new Response(proc.stderr).text(),
36
- proc.exited,
37
- ]);
38
- return { stdout, stderr, exitCode };
39
- }
40
- }
41
-
42
- /** Parse nmcli terse (`-t`) `key:value` lines into a map, splitting on the first `:`. */
43
- export function parseTerse(stdout: string): Map<string, string> {
44
- const settings = new Map<string, string>();
45
- for (const line of stdout.split('\n')) {
46
- const separator = line.indexOf(':');
47
- if (separator >= 0) {
48
- settings.set(line.slice(0, separator), line.slice(separator + 1));
49
- }
50
- }
51
- return settings;
52
- }