@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,44 @@
1
+ import type { DesiredRadio } from '../domain/index.js';
2
+ import type { ConnectionId, DeviceIfname, GsmProfileInput, GsmProfilePatch } from './network-manager.js';
3
+ /** Radio + SIM ops — owned SOLELY by the ModemManager port. */
4
+ export type MmOp = {
5
+ readonly kind: 'setRadioModes';
6
+ readonly preference: DesiredRadio;
7
+ } | {
8
+ readonly kind: 'setPrimarySimSlot';
9
+ readonly slotIndex: number;
10
+ };
11
+ /** Connection / APN / activation ops — owned SOLELY by the NetworkManager port. */
12
+ export type NmOp = {
13
+ readonly kind: 'createGsmProfile';
14
+ readonly profile: GsmProfileInput;
15
+ } | {
16
+ readonly kind: 'updateGsmProfile';
17
+ readonly connectionId: ConnectionId;
18
+ readonly patch: GsmProfilePatch;
19
+ } | {
20
+ readonly kind: 'activate';
21
+ readonly connectionId: ConnectionId;
22
+ readonly deviceIfname: DeviceIfname;
23
+ } | {
24
+ readonly kind: 'deactivate';
25
+ readonly connectionId: ConnectionId;
26
+ readonly deviceIfname: DeviceIfname;
27
+ };
28
+ /**
29
+ * A planned op tagged with its owning port. The two arms are disjoint on BOTH the
30
+ * `port` discriminant AND the op `kind` space, so the planner cannot emit — and a
31
+ * reviewer cannot write — a radio op under the NM port, or a connection op under
32
+ * the MM port.
33
+ */
34
+ export type PortTaggedOp = {
35
+ readonly port: 'mm';
36
+ readonly op: MmOp;
37
+ } | {
38
+ readonly port: 'nm';
39
+ readonly op: NmOp;
40
+ };
41
+ /** Tag an MM op for the ModemManager port. Only an `MmOp` is accepted. */
42
+ export declare function mmOp(op: MmOp): PortTaggedOp;
43
+ /** Tag an NM op for the NetworkManager port. Only an `NmOp` is accepted. */
44
+ export declare function nmOp(op: NmOp): PortTaggedOp;
@@ -0,0 +1,16 @@
1
+ // Port-tagged reconcile ops — a planned mutation TAGGED with the port that owns it.
2
+ //
3
+ // The op kinds are DISJOINT by construction: a radio / SIM op (`MmOp`) shares no
4
+ // `kind` with a connection / APN op (`NmOp`). Tagging is therefore type-checked —
5
+ // `{ port: 'nm', op: { kind: 'setRadioModes', … } }` does NOT compile, because a
6
+ // radio op is not assignable to the NM arm. That mis-tag is a COMPILE-TIME error
7
+ // (see `ops.type-test.ts` for the `@ts-expect-error` proofs). This is the ownership
8
+ // matrix (README) enforced by the type system, not merely by convention.
9
+ /** Tag an MM op for the ModemManager port. Only an `MmOp` is accepted. */
10
+ export function mmOp(op) {
11
+ return { port: 'mm', op };
12
+ }
13
+ /** Tag an NM op for the NetworkManager port. Only an `NmOp` is accepted. */
14
+ export function nmOp(op) {
15
+ return { port: 'nm', op };
16
+ }
@@ -1,19 +1,5 @@
1
- // Reconcile receipts — the HONEST outcome of trying to apply one policy dimension.
2
- //
3
- // Every attempt to converge a dimension yields exactly one receipt with a status
4
- // AND a reason. Nothing is ever silently dropped: "prefer 5G" on a 4G-only modem is
5
- // reported as `unsupported` with a reason, never quietly downgraded to "5G off".
6
-
7
1
  /** The policy dimension a receipt is about. */
8
- export type PolicyDimension =
9
- | 'enabled'
10
- | 'connection'
11
- | 'roaming'
12
- | 'radio'
13
- | 'simSlot'
14
- | 'recovery'
15
- | 'usage';
16
-
2
+ export type PolicyDimension = 'enabled' | 'connection' | 'roaming' | 'radio' | 'band' | 'simSlot' | 'recovery' | 'usage';
17
3
  /**
18
4
  * Receipt status taxonomy:
19
5
  * - applied — the desired state is in effect (converged, or already was).
@@ -22,22 +8,14 @@ export type PolicyDimension =
22
8
  * - failed — an op was attempted and errored.
23
9
  */
24
10
  export type ReceiptStatus = 'applied' | 'pending' | 'unsupported' | 'failed';
25
-
26
11
  /**
27
12
  * One reconcile receipt. `reason` is ALWAYS populated — a receipt with no reason
28
13
  * would be a silent outcome, which the contract forbids.
29
14
  */
30
15
  export interface Receipt {
31
- readonly dimension: PolicyDimension;
32
- readonly status: ReceiptStatus;
33
- readonly reason: string;
16
+ readonly dimension: PolicyDimension;
17
+ readonly status: ReceiptStatus;
18
+ readonly reason: string;
34
19
  }
35
-
36
20
  /** Build a receipt. Kept as a helper so `reason` can never be forgotten. */
37
- export function receipt(
38
- dimension: PolicyDimension,
39
- status: ReceiptStatus,
40
- reason: string,
41
- ): Receipt {
42
- return { dimension, status, reason };
43
- }
21
+ export declare function receipt(dimension: PolicyDimension, status: ReceiptStatus, reason: string): Receipt;
@@ -0,0 +1,9 @@
1
+ // Reconcile receipts — the HONEST outcome of trying to apply one policy dimension.
2
+ //
3
+ // Every attempt to converge a dimension yields exactly one receipt with a status
4
+ // AND a reason. Nothing is ever silently dropped: "prefer 5G" on a 4G-only modem is
5
+ // reported as `unsupported` with a reason, never quietly downgraded to "5G off".
6
+ /** Build a receipt. Kept as a helper so `reason` can never be forgotten. */
7
+ export function receipt(dimension, status, reason) {
8
+ return { dimension, status, reason };
9
+ }
@@ -0,0 +1,33 @@
1
+ import type { DesiredCellularPolicy, NmActivation, RadioAccessTechnology } from '../domain/index.js';
2
+ import type { ConnectionId, DeviceIfname } from './network-manager.js';
3
+ import { type PortTaggedOp } from './ops.js';
4
+ import { type Receipt } from './receipts.js';
5
+ /** What the modem / stack can actually do — the capability set the planner honors. */
6
+ export interface ModemCapabilities {
7
+ readonly supportedRats: ReadonlySet<RadioAccessTechnology>;
8
+ readonly simSlotCount: number;
9
+ readonly supportsAutoApn: boolean;
10
+ }
11
+ /**
12
+ * The currently-applied cellular configuration, as the planner sees it — the "is"
13
+ * state, distinct from the observational snapshot: it tracks what has actually been
14
+ * written (profile, radio preference, primary slot) plus the live NM activation.
15
+ */
16
+ export interface AppliedCellularState {
17
+ readonly nmActivation: NmActivation;
18
+ readonly hasProfile: boolean;
19
+ readonly connectionId?: ConnectionId;
20
+ readonly deviceIfname?: DeviceIfname;
21
+ readonly appliedApn?: 'auto' | string;
22
+ readonly appliedRoaming?: boolean;
23
+ readonly appliedRadioPreference?: readonly RadioAccessTechnology[];
24
+ readonly activePrimarySlot?: number;
25
+ readonly activationFailureReason?: string;
26
+ }
27
+ /** A reconcile plan: the ops to run and one receipt per policy dimension. */
28
+ export interface Plan {
29
+ readonly ops: readonly PortTaggedOp[];
30
+ readonly receipts: readonly Receipt[];
31
+ }
32
+ /** Reconcile a desired policy against the applied state into ops + receipts. */
33
+ export declare function planReconcile(current: AppliedCellularState, desired: DesiredCellularPolicy, capabilities: ModemCapabilities): Plan;
@@ -0,0 +1,200 @@
1
+ // The desired-state planner — pure. Given the currently-applied state, a desired
2
+ // policy, and the modem's capabilities, it produces the port-tagged ops needed to
3
+ // converge PLUS an honest receipt per policy dimension. It performs no I/O and no
4
+ // side effects: the same inputs always yield the same ops and receipts, which is
5
+ // what makes re-applying a policy idempotent (see reconcile.test.ts).
6
+ import { mmOp, nmOp } from './ops.js';
7
+ import { receipt } from './receipts.js';
8
+ /** Reconcile a desired policy against the applied state into ops + receipts. */
9
+ export function planReconcile(current, desired, capabilities) {
10
+ const profile = planNmProfile(current, desired, capabilities);
11
+ const results = [
12
+ planRadio(current, desired, capabilities),
13
+ planSimSlot(current, desired, capabilities),
14
+ planEnabled(current, desired),
15
+ { receipt: planRecovery(desired) },
16
+ { receipt: planUsage(desired) },
17
+ ];
18
+ const ops = [];
19
+ const receipts = [profile.connection, profile.roaming];
20
+ if (profile.op !== undefined) {
21
+ ops.push(profile.op);
22
+ }
23
+ for (const result of results) {
24
+ receipts.push(result.receipt);
25
+ if (result.op !== undefined) {
26
+ ops.push(result.op);
27
+ }
28
+ }
29
+ return { ops, receipts };
30
+ }
31
+ function profileFromPolicy(desired) {
32
+ const auto = desired.connection.apn === 'auto';
33
+ const base = {
34
+ connectionName: 'ceralive-cellular',
35
+ apn: auto ? '' : desired.connection.apn,
36
+ homeOnly: !desired.roaming,
37
+ autoConfig: auto,
38
+ };
39
+ // SENSITIVE creds only when explicitly provided AND not in auto-config mode.
40
+ if (!auto && desired.connection.auth !== undefined) {
41
+ const { username, password } = desired.connection.auth;
42
+ return {
43
+ ...base,
44
+ ...(username !== undefined ? { username } : {}),
45
+ ...(password !== undefined ? { password } : {}),
46
+ };
47
+ }
48
+ return base;
49
+ }
50
+ // NM owns APN / auth / roaming / autoconnect: one profile write converges all of
51
+ // them, so connection + roaming share a single op and each get their own receipt.
52
+ function planNmProfile(current, desired, capabilities) {
53
+ const auto = desired.connection.apn === 'auto';
54
+ if (auto && !capabilities.supportsAutoApn) {
55
+ return {
56
+ connection: receipt('connection', 'unsupported', 'Auto-APN is not available on this NetworkManager / modem'),
57
+ roaming: receipt('roaming', 'unsupported', 'roaming cannot be applied without a connection profile'),
58
+ };
59
+ }
60
+ const needsWrite = !current.hasProfile ||
61
+ current.appliedApn !== desired.connection.apn ||
62
+ current.appliedRoaming !== desired.roaming;
63
+ if (!needsWrite) {
64
+ return {
65
+ connection: receipt('connection', 'applied', `connection APN '${desired.connection.apn}' already configured`),
66
+ roaming: receipt('roaming', 'applied', `roaming already ${desired.roaming ? 'enabled' : 'disabled'}`),
67
+ };
68
+ }
69
+ const op = current.hasProfile && current.connectionId !== undefined
70
+ ? nmOp({
71
+ kind: 'updateGsmProfile',
72
+ connectionId: current.connectionId,
73
+ patch: profileFromPolicy(desired),
74
+ })
75
+ : nmOp({ kind: 'createGsmProfile', profile: profileFromPolicy(desired) });
76
+ const verb = current.hasProfile ? 'updating' : 'creating';
77
+ return {
78
+ connection: receipt('connection', 'pending', `${verb} connection profile for APN '${desired.connection.apn}'`),
79
+ roaming: receipt('roaming', 'pending', `roaming ${desired.roaming ? 'enabled' : 'disabled'} via connection profile`),
80
+ op,
81
+ };
82
+ }
83
+ function radioMatches(applied, desired) {
84
+ if (applied === undefined || applied.length !== desired.length) {
85
+ return false;
86
+ }
87
+ return applied.every((rat, index) => rat === desired[index]);
88
+ }
89
+ function planRadio(current, desired, capabilities) {
90
+ const preference = desired.radio.preferenceOrdered;
91
+ const top = preference[0];
92
+ if (top === undefined) {
93
+ return {
94
+ receipt: receipt('radio', 'failed', 'radio preference must list at least one access technology'),
95
+ };
96
+ }
97
+ if (!capabilities.supportedRats.has(top)) {
98
+ const supported = [...capabilities.supportedRats].join(', ') || 'none';
99
+ return {
100
+ receipt: receipt('radio', 'unsupported', `preferred radio access technology '${top}' is not supported by this modem (supports ${supported})`),
101
+ };
102
+ }
103
+ if (radioMatches(current.appliedRadioPreference, preference)) {
104
+ return {
105
+ receipt: receipt('radio', 'applied', `radio preference already ${preference.join(' > ')}`),
106
+ };
107
+ }
108
+ return {
109
+ receipt: receipt('radio', 'pending', `setting radio preference to ${preference.join(' > ')}`),
110
+ op: mmOp({ kind: 'setRadioModes', preference: desired.radio }),
111
+ };
112
+ }
113
+ function planSimSlot(current, desired, capabilities) {
114
+ if (desired.simSlot === undefined) {
115
+ return { receipt: receipt('simSlot', 'applied', 'no primary SIM slot preference set') };
116
+ }
117
+ if (capabilities.simSlotCount <= 1) {
118
+ return {
119
+ receipt: receipt('simSlot', 'unsupported', `primary SIM slot selection requires a multi-slot modem (this modem has ${capabilities.simSlotCount})`),
120
+ };
121
+ }
122
+ if (desired.simSlot < 1 || desired.simSlot > capabilities.simSlotCount) {
123
+ return {
124
+ receipt: receipt('simSlot', 'failed', `SIM slot ${desired.simSlot} is out of range (1..${capabilities.simSlotCount})`),
125
+ };
126
+ }
127
+ if (current.activePrimarySlot === desired.simSlot) {
128
+ return {
129
+ receipt: receipt('simSlot', 'applied', `SIM slot ${desired.simSlot} already primary`),
130
+ };
131
+ }
132
+ return {
133
+ receipt: receipt('simSlot', 'pending', `switching primary SIM slot to ${desired.simSlot}`),
134
+ op: mmOp({ kind: 'setPrimarySimSlot', slotIndex: desired.simSlot }),
135
+ };
136
+ }
137
+ function planEnabled(current, desired) {
138
+ const nm = current.nmActivation;
139
+ const addressable = current.connectionId !== undefined && current.deviceIfname !== undefined;
140
+ if (desired.enabled) {
141
+ switch (nm) {
142
+ case 'activated':
143
+ return { receipt: receipt('enabled', 'applied', 'connection is active') };
144
+ case 'activating':
145
+ return { receipt: receipt('enabled', 'pending', 'connection activation in progress') };
146
+ case 'failed':
147
+ return {
148
+ receipt: receipt('enabled', 'failed', `connection activation failed${current.activationFailureReason ? `: ${current.activationFailureReason}` : ''}`),
149
+ };
150
+ case 'unmanaged':
151
+ return {
152
+ receipt: receipt('enabled', 'unsupported', 'device is not managed by NetworkManager'),
153
+ };
154
+ default:
155
+ if (!addressable ||
156
+ current.connectionId === undefined ||
157
+ current.deviceIfname === undefined) {
158
+ return {
159
+ receipt: receipt('enabled', 'pending', 'awaiting connection profile and data interface before activation'),
160
+ };
161
+ }
162
+ return {
163
+ receipt: receipt('enabled', 'pending', 'activating connection'),
164
+ op: nmOp({
165
+ kind: 'activate',
166
+ connectionId: current.connectionId,
167
+ deviceIfname: current.deviceIfname,
168
+ }),
169
+ };
170
+ }
171
+ }
172
+ if ((nm === 'activated' || nm === 'activating') &&
173
+ current.connectionId !== undefined &&
174
+ current.deviceIfname !== undefined) {
175
+ return {
176
+ receipt: receipt('enabled', 'pending', 'deactivating connection'),
177
+ op: nmOp({
178
+ kind: 'deactivate',
179
+ connectionId: current.connectionId,
180
+ deviceIfname: current.deviceIfname,
181
+ }),
182
+ };
183
+ }
184
+ return { receipt: receipt('enabled', 'applied', 'connection is inactive') };
185
+ }
186
+ // Recovery + usage are LOCAL-CONTROLLER owned (README ownership table): they emit no
187
+ // MM / NM op, only a receipt recording that the local policy was accepted.
188
+ function planRecovery(desired) {
189
+ return receipt('recovery', 'applied', desired.recovery.enabled ? 'recovery policy recorded (enabled)' : 'recovery disabled (default)');
190
+ }
191
+ function planUsage(desired) {
192
+ const parts = [];
193
+ if (desired.usage.cycleDay !== undefined) {
194
+ parts.push(`cycle day ${desired.usage.cycleDay}`);
195
+ }
196
+ if (desired.usage.thresholdBytes !== undefined) {
197
+ parts.push(`threshold ${desired.usage.thresholdBytes} bytes`);
198
+ }
199
+ return receipt('usage', 'applied', parts.length > 0 ? `usage policy recorded (${parts.join(', ')})` : 'no usage policy set');
200
+ }
@@ -0,0 +1,29 @@
1
+ export declare const DEFAULT_MODEM_CONTROL_LOCK_PATH = "/run/ceralive/modem-control.lock";
2
+ export type ExclusiveResource = 'file-store' | 'router-session' | 'usb-hub';
3
+ export type ResourceOwnershipRequest = {
4
+ readonly resource: ExclusiveResource;
5
+ };
6
+ export type ResourceOwnershipHolder = {
7
+ readonly pid: number;
8
+ readonly startedAtEpochMs: number;
9
+ };
10
+ export type ResourceOwnershipLoss = {
11
+ readonly reason: 'holder-exited';
12
+ };
13
+ export interface ResourceOwnershipLease {
14
+ readonly holder: ResourceOwnershipHolder;
15
+ readonly lost: Promise<ResourceOwnershipLoss>;
16
+ release(): Promise<void>;
17
+ }
18
+ export type ResourceOwnershipResult = {
19
+ readonly status: 'acquired';
20
+ readonly lease: ResourceOwnershipLease;
21
+ } | {
22
+ readonly status: 'refused';
23
+ readonly reason: 'already-owned';
24
+ readonly holder?: ResourceOwnershipHolder;
25
+ };
26
+ /** Acquire-or-refuse ownership; implementations must never queue a contender. */
27
+ export interface ResourceOwnershipPort {
28
+ acquire(request: ResourceOwnershipRequest): Promise<ResourceOwnershipResult>;
29
+ }
@@ -0,0 +1 @@
1
+ export const DEFAULT_MODEM_CONTROL_LOCK_PATH = '/run/ceralive/modem-control.lock';
@@ -0,0 +1,19 @@
1
+ import type { EpochMillis } from '../domain/index.js';
2
+ import type { DeviceIfname } from './network-manager.js';
3
+ /** Whether a router-class device is present on an interface. */
4
+ export type RouterPresence = 'present' | 'absent';
5
+ /**
6
+ * Advisory health of a router-class uplink. Every field is informational — a
7
+ * degraded router is reported, never removed from the routing set on health alone.
8
+ */
9
+ export interface RouterHealth {
10
+ readonly presence: RouterPresence;
11
+ readonly gatewayReachable: boolean;
12
+ readonly egressHealthy: boolean;
13
+ readonly observedAt: EpochMillis;
14
+ }
15
+ /** The router port — presence and advisory health only. No mutation verbs. */
16
+ export interface RouterPort {
17
+ probePresence(ifname: DeviceIfname): Promise<RouterPresence>;
18
+ checkHealth(ifname: DeviceIfname): Promise<RouterHealth>;
19
+ }
@@ -0,0 +1,7 @@
1
+ // The router port — for devices MM cannot control (HiLink, router-ethernet class).
2
+ //
3
+ // This port is DELIBERATELY tiny: presence and advisory health ONLY. Health never
4
+ // drives activation or recovery — a degraded router stays in the routing set (it is
5
+ // never evicted on health alone). A router device is an Ethernet uplink we OBSERVE,
6
+ // not a modem we configure; there is no bearer / APN / radio verb here.
7
+ export {};
@@ -0,0 +1,64 @@
1
+ import type { Unsubscribe } from './observation.js';
2
+ /** A message's lifecycle state, verbatim from ModemManager's own vocabulary. */
3
+ export type SmsState = 'unknown' | 'stored' | 'receiving' | 'received' | 'sending' | 'sent';
4
+ /** One stored message, normalized. `text` is `''` for a data-only (WAP/PDU) message. */
5
+ export interface SmsMessage {
6
+ /** The trailing `/SMS/<n>` object index, stringified. */
7
+ readonly id: string;
8
+ readonly from?: string;
9
+ /** The service-centre timestamp, verbatim as the device reported it. */
10
+ readonly timestamp?: string;
11
+ readonly text: string;
12
+ readonly state: SmsState;
13
+ }
14
+ /**
15
+ * Why an inbox read produced no list. These are the four operator-actionable
16
+ * facts, and they are deliberately distinct from one another — a modem with no
17
+ * Messaging interface, a radio that has not come up, a selector nothing answers
18
+ * to, and drift in whatever the source printed are four different next steps.
19
+ */
20
+ export type SmsReadRefusal = 'unsupported' | 'not_enabled' | 'unknown_modem' | 'read_failed';
21
+ /**
22
+ * The result of an inbox read. A REFUSAL IS NEVER AN EMPTY LIST: `{ok: true,
23
+ * messages: []}` means this modem has an inbox and it is empty.
24
+ */
25
+ export type SmsInboxResult = {
26
+ readonly ok: true;
27
+ readonly messages: readonly SmsMessage[];
28
+ } | {
29
+ readonly ok: false;
30
+ readonly reason: SmsReadRefusal;
31
+ };
32
+ /**
33
+ * One inbox change. `resynced` carries a full authoritative list and is what a
34
+ * source restart produces — a consumer replaces its rows with it rather than
35
+ * merging, so a restart can never duplicate a row it already holds.
36
+ */
37
+ export type SmsInboxEvent = {
38
+ readonly kind: 'added';
39
+ readonly message: SmsMessage;
40
+ } | {
41
+ readonly kind: 'deleted';
42
+ readonly id: string;
43
+ } | {
44
+ readonly kind: 'resynced';
45
+ readonly messages: readonly SmsMessage[];
46
+ };
47
+ export type SmsInboxListener = (event: SmsInboxEvent) => void;
48
+ /**
49
+ * The read-only SMS port. `list()` resolves the current inbox once; `observe()`
50
+ * streams every subsequent change; `stop()` releases the source.
51
+ *
52
+ * It is deliberately NOT an extension of `ModemManagerPort`: a consumer that
53
+ * only reads an inbox must not acquire radio, SIM, or inhibit verbs along the
54
+ * way — the same narrowing argument `ModemObservationPort` makes for the
55
+ * shadow reader.
56
+ */
57
+ export interface SmsObservationPort {
58
+ /** Read the modem's stored inbox, newest-first and capped. */
59
+ list(): Promise<SmsInboxResult>;
60
+ /** Subscribe to Added/Deleted/resync events. Returns an unsubscribe. */
61
+ observe(listener: SmsInboxListener): Unsubscribe;
62
+ /** Release the source. Idempotent. */
63
+ stop(): Promise<void>;
64
+ }
@@ -0,0 +1,24 @@
1
+ // The SMS port — LIST / READ and Added/Deleted observation. NOTHING ELSE.
2
+ //
3
+ // READ-ONLY IS PERMANENT, NOT A PHASE LIMITATION. There is no verb here that
4
+ // composes, stores, sends, or deletes a message, and none may ever be added.
5
+ // Sending or deleting an SMS is billable and irreversible, and it turns a
6
+ // diagnostic read into real modem-control capability over the subscriber's
7
+ // account. CeraUI carries the same contract on its own side, enforced by a grep
8
+ // gate (`tests/modem-sms-readonly-gate.test.ts`); `sms/readonly-gate.test.ts`
9
+ // is this package's half. Neither may be weakened to land a write path — that
10
+ // is a new spec change with its own confirmation/interlock design.
11
+ //
12
+ // THE OBSERVATION MODEL IS "LIST ONCE, THEN FOLLOW THE SIGNALS". ModemManager's
13
+ // Messaging interface emits `Added` and `Deleted` for every inbox change, so a
14
+ // consumer lists once at start and folds events from then on. Re-listing on a
15
+ // poll tick is the anti-pattern this port exists to remove: it costs one method
16
+ // call per message per tick and still cannot report an arrival any sooner than
17
+ // the tick it lands on.
18
+ //
19
+ // CONTENT NEVER RIDES A DIAGNOSTIC. A message body routinely carries a one-time
20
+ // code and its originator identifies the subscriber, so nothing on this port
21
+ // puts either into a reason string, a receipt, or a log line — see
22
+ // `../redact.ts` for the key class and `../sms/mmcli-parse.ts` for the
23
+ // content-free parse errors.
24
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { PowerCapability, PowerCycleContext, PowerCycleResult } from '../backend/power-contract.js';
2
+ /** Consumer-supplied USB-hub actuator. This package intentionally ships no implementation. */
3
+ export interface UhubctlPort {
4
+ readonly capability: PowerCapability;
5
+ cycle(context: PowerCycleContext): Promise<PowerCycleResult>;
6
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,124 @@
1
+ import type { DeviceGeneration, ObservationEnvelope, OperationDescriptor, OperationResult, PhysicalModemId } from '../domain/index.js';
2
+ export declare const PROVIDER_TRANSPORTS: readonly ['usb', 'pci', 'network', 'modemmanager'];
3
+ export type ProviderTransport = (typeof PROVIDER_TRANSPORTS)[number];
4
+ export declare const PASSIVE_FACT_KINDS: readonly ['usb', 'pci', 'interface', 'driver', 'gateway', 'model', 'firmware'];
5
+ export type PassiveFactKind = (typeof PASSIVE_FACT_KINDS)[number];
6
+ export type EvidenceStrength = 'none' | 'weak' | 'moderate' | 'strong';
7
+ export type MatcherScore = 'unsupported' | 'maybe' | 'likely' | 'supported';
8
+ export type PassiveFact = {
9
+ readonly kind: PassiveFactKind;
10
+ readonly value: string;
11
+ };
12
+ export type ProviderMatchRequest = {
13
+ readonly physicalModemId: PhysicalModemId;
14
+ readonly generation: DeviceGeneration;
15
+ readonly transport: ProviderTransport;
16
+ readonly passiveFacts: readonly PassiveFact[];
17
+ readonly composition: string;
18
+ readonly firmware?: string;
19
+ };
20
+ export type ProviderExecutionContext = ProviderMatchRequest & {
21
+ readonly profile: string;
22
+ };
23
+ export type PassiveMatcher = {
24
+ readonly id: string;
25
+ readonly fact: PassiveFactKind;
26
+ readonly expected: readonly string[];
27
+ readonly profiles: readonly string[];
28
+ readonly strength: Exclude<EvidenceStrength, 'none'>;
29
+ readonly required: boolean;
30
+ };
31
+ export type FingerprintResult = {
32
+ readonly signal: 'match' | 'mismatch' | 'unknown';
33
+ readonly strength: Exclude<EvidenceStrength, 'none'>;
34
+ readonly profiles: readonly string[];
35
+ readonly detail: string;
36
+ };
37
+ export type UnauthenticatedProbe = {
38
+ readonly id: string;
39
+ readonly run: (context: ProviderMatchRequest) => Promise<FingerprintResult>;
40
+ };
41
+ export type CapabilityReader = {
42
+ readonly id: string;
43
+ readonly read: (context: ProviderExecutionContext) => Promise<Omit<FingerprintResult, 'profiles'>>;
44
+ };
45
+ export type AuthenticatedProfileResult = {
46
+ readonly status: 'matched';
47
+ readonly profile: string;
48
+ readonly detail: string;
49
+ } | {
50
+ readonly status: 'refused';
51
+ readonly detail: string;
52
+ } | {
53
+ readonly status: 'unavailable';
54
+ readonly detail: string;
55
+ };
56
+ /** One algorithm and one call per generation-scoped evaluation; cycling is unrepresentable. */
57
+ export type AuthenticatedProfile = {
58
+ readonly algorithm: string;
59
+ readonly attemptLimit: 1;
60
+ readonly authenticate: (context: ProviderMatchRequest, profileCandidates: readonly string[]) => Promise<AuthenticatedProfileResult>;
61
+ };
62
+ export type ProviderOperationsSurface = {
63
+ readonly access: 'read-only' | 'read-write';
64
+ };
65
+ export interface ProviderReadOperations<O> {
66
+ readonly descriptor: OperationDescriptor<never, O>;
67
+ readonly read: (context: ProviderExecutionContext) => Promise<OperationResult<O>>;
68
+ }
69
+ export interface ProviderWriteOperations<I, O> {
70
+ readonly descriptor: OperationDescriptor<I, O>;
71
+ readonly read: (context: ProviderExecutionContext) => Promise<OperationResult<O>>;
72
+ readonly write: (context: ProviderExecutionContext, input: I) => Promise<OperationResult<O>>;
73
+ }
74
+ export type ProviderContractFixture = {
75
+ readonly profile: string;
76
+ readonly request: Readonly<Record<string, unknown>>;
77
+ readonly response: Readonly<Record<string, unknown>>;
78
+ };
79
+ export interface ProviderDefinition<TObservation = unknown, TOperations extends ProviderOperationsSurface = ProviderOperationsSurface> {
80
+ readonly id: string;
81
+ readonly profileVersion: string;
82
+ readonly eligibleTransports: readonly ProviderTransport[];
83
+ readonly passiveMatchers: readonly PassiveMatcher[];
84
+ readonly unauthenticatedProbes: readonly UnauthenticatedProbe[];
85
+ readonly authenticatedProfile?: AuthenticatedProfile;
86
+ readonly capabilityReaders: readonly CapabilityReader[];
87
+ readonly observe: (context: ProviderExecutionContext) => Promise<readonly ObservationEnvelope<TObservation>[]>;
88
+ readonly operations: (profile: string) => TOperations;
89
+ readonly contractFixtures: readonly ProviderContractFixture[];
90
+ }
91
+ export type MatcherEvidence = {
92
+ readonly provider: string;
93
+ readonly profile: string | null;
94
+ readonly stage: 'transport-eligibility' | 'passive-facts' | 'unauthenticated-fingerprint' | 'authenticated-profile' | 'capability-read';
95
+ readonly source: string;
96
+ readonly signal: 'match' | 'mismatch' | 'unknown';
97
+ readonly strength: EvidenceStrength;
98
+ readonly detail: string;
99
+ };
100
+ type MatchResultBase = {
101
+ readonly score: MatcherScore;
102
+ readonly confidence: number;
103
+ readonly evidence: readonly MatcherEvidence[];
104
+ readonly conflicts: readonly MatcherEvidence[];
105
+ readonly generation: DeviceGeneration;
106
+ readonly physicalModemId: PhysicalModemId;
107
+ };
108
+ export type ProviderMatchResult = (MatchResultBase & {
109
+ readonly status: 'unsupported' | 'ambiguous';
110
+ readonly provider: null;
111
+ readonly profile: null;
112
+ readonly operations: null;
113
+ readonly writable: false;
114
+ }) | (MatchResultBase & {
115
+ readonly status: 'selected';
116
+ readonly provider: string;
117
+ readonly profile: string;
118
+ readonly operations: ProviderOperationsSurface;
119
+ readonly writable: boolean;
120
+ });
121
+ export interface ProviderMatcher {
122
+ match(request: ProviderMatchRequest): Promise<ProviderMatchResult>;
123
+ }
124
+ export {};
@@ -0,0 +1,10 @@
1
+ export const PROVIDER_TRANSPORTS = ['usb', 'pci', 'network', 'modemmanager'];
2
+ export const PASSIVE_FACT_KINDS = [
3
+ 'usb',
4
+ 'pci',
5
+ 'interface',
6
+ 'driver',
7
+ 'gateway',
8
+ 'model',
9
+ 'firmware',
10
+ ];
@@ -0,0 +1,2 @@
1
+ export * from './provider.js';
2
+ export * from './transport.js';
@@ -0,0 +1,2 @@
1
+ export * from './provider.js';
2
+ export * from './transport.js';