@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
@@ -0,0 +1,118 @@
1
+ import { DomainError } from './errors.js';
2
+ import type { DeviceGeneration } from './generation.js';
3
+ export type OperationSupport = {
4
+ readonly supported: true;
5
+ } | {
6
+ readonly supported: false;
7
+ readonly reason: string;
8
+ };
9
+ export type OperationConstraints<I> = {
10
+ readonly kind: 'unconstrained';
11
+ } | {
12
+ readonly kind: 'allowed-values';
13
+ readonly values: readonly I[];
14
+ } | {
15
+ readonly kind: 'numeric-range';
16
+ readonly min: number;
17
+ readonly max: number;
18
+ readonly step?: number;
19
+ };
20
+ export type OperationAvailability = {
21
+ readonly state: 'available';
22
+ } | {
23
+ readonly state: 'unavailable';
24
+ readonly reason: string;
25
+ } | {
26
+ readonly state: 'refused';
27
+ readonly reason: string;
28
+ };
29
+ export type MutationImpact = 'read' | 'write' | 'session' | 'disruptive' | 'recovery';
30
+ export type RetryClass = 'never' | 'idempotent-read';
31
+ export type OperationConfidence = 'high' | 'medium' | 'low' | 'unknown';
32
+ export type OperationRequirement = {
33
+ readonly required: false;
34
+ } | {
35
+ readonly required: true;
36
+ readonly reason: string;
37
+ };
38
+ export type OperationReadback<I, O> = {
39
+ readonly required: false;
40
+ } | {
41
+ readonly required: true;
42
+ readonly reason: string;
43
+ readonly matches: (input: I, observed: O) => boolean;
44
+ };
45
+ export type OperationDescriptor<I, O> = {
46
+ readonly id: string;
47
+ readonly support: {
48
+ readonly read: OperationSupport;
49
+ readonly write: OperationSupport;
50
+ };
51
+ readonly authority: 'provider' | 'controller' | 'hardware';
52
+ readonly provider: string;
53
+ readonly constraints: OperationConstraints<I>;
54
+ readonly livePreconditions: readonly string[];
55
+ readonly availability: OperationAvailability;
56
+ readonly mutationImpact: MutationImpact;
57
+ readonly retryClass: RetryClass;
58
+ readonly readback: OperationReadback<I, O>;
59
+ readonly rollback: OperationRequirement;
60
+ readonly journal: OperationRequirement;
61
+ readonly admission: OperationRequirement;
62
+ readonly evidence: {
63
+ readonly profiles: readonly string[];
64
+ readonly firmware: readonly string[];
65
+ };
66
+ readonly confidence: OperationConfidence;
67
+ };
68
+ export declare class OperationDescriptorError extends DomainError {
69
+ readonly name = "OperationDescriptorError";
70
+ readonly reason = "retry-requires-idempotent-read";
71
+ constructor();
72
+ }
73
+ export declare function defineOperationDescriptor<I, O>(descriptor: OperationDescriptor<I, O>): OperationDescriptor<I, O>;
74
+ export type OperationCompletion<O> = {
75
+ readonly status: 'applied';
76
+ readonly value: O;
77
+ } | {
78
+ readonly status: 'refused';
79
+ readonly reason: string;
80
+ } | {
81
+ readonly status: 'failed';
82
+ readonly reason: string;
83
+ } | {
84
+ readonly status: 'timed-out';
85
+ } | {
86
+ readonly status: 'dropped';
87
+ };
88
+ export type OperationResult<O> = {
89
+ readonly status: 'applied';
90
+ readonly value: O;
91
+ readonly generation: DeviceGeneration;
92
+ readonly requiresReconciliation: false;
93
+ } | {
94
+ readonly status: 'refused';
95
+ readonly reason: string;
96
+ readonly generation: DeviceGeneration;
97
+ readonly requiresReconciliation: false;
98
+ } | {
99
+ readonly status: 'unknown-outcome';
100
+ readonly reason: 'stale-generation' | 'write-reply-timed-out' | 'write-reply-dropped';
101
+ readonly generation: DeviceGeneration;
102
+ readonly requiresReconciliation: true;
103
+ } | {
104
+ readonly status: 'failed';
105
+ readonly reason: string;
106
+ readonly generation: DeviceGeneration;
107
+ readonly requiresReconciliation: false;
108
+ };
109
+ export type OperationCompletionContext<O> = {
110
+ readonly operation: 'read' | 'write';
111
+ readonly completionGeneration: DeviceGeneration;
112
+ readonly currentGeneration: DeviceGeneration;
113
+ readonly completion: OperationCompletion<O>;
114
+ };
115
+ /** Apply generation and write-reply uncertainty before exposing a completion to callers. */
116
+ export declare function classifyOperationCompletion<O>(context: OperationCompletionContext<O>): OperationResult<O>;
117
+ /** Automatic retries are restricted to explicitly classified idempotent reads. */
118
+ export declare function canAutoRetry<I, O>(descriptor: OperationDescriptor<I, O>, result: OperationResult<O>): boolean;
@@ -0,0 +1,85 @@
1
+ import { DomainError } from './errors.js';
2
+ export class OperationDescriptorError extends DomainError {
3
+ name = 'OperationDescriptorError';
4
+ reason = 'retry-requires-idempotent-read';
5
+ constructor() {
6
+ super('operation descriptor refused: automatic retry requires a supported idempotent read');
7
+ }
8
+ }
9
+ export function defineOperationDescriptor(descriptor) {
10
+ if (descriptor.retryClass === 'idempotent-read' &&
11
+ (descriptor.mutationImpact !== 'read' || !descriptor.support.read.supported)) {
12
+ throw new OperationDescriptorError();
13
+ }
14
+ return descriptor;
15
+ }
16
+ /** Apply generation and write-reply uncertainty before exposing a completion to callers. */
17
+ export function classifyOperationCompletion(context) {
18
+ const generation = context.completionGeneration;
19
+ if (generation !== context.currentGeneration) {
20
+ return {
21
+ status: 'unknown-outcome',
22
+ reason: 'stale-generation',
23
+ requiresReconciliation: true,
24
+ generation,
25
+ };
26
+ }
27
+ switch (context.completion.status) {
28
+ case 'applied':
29
+ return {
30
+ status: 'applied',
31
+ value: context.completion.value,
32
+ generation,
33
+ requiresReconciliation: false,
34
+ };
35
+ case 'refused':
36
+ return {
37
+ status: 'refused',
38
+ reason: context.completion.reason,
39
+ generation,
40
+ requiresReconciliation: false,
41
+ };
42
+ case 'failed':
43
+ return {
44
+ status: 'failed',
45
+ reason: context.completion.reason,
46
+ generation,
47
+ requiresReconciliation: false,
48
+ };
49
+ case 'timed-out':
50
+ return context.operation === 'write'
51
+ ? {
52
+ status: 'unknown-outcome',
53
+ reason: 'write-reply-timed-out',
54
+ requiresReconciliation: true,
55
+ generation,
56
+ }
57
+ : {
58
+ status: 'failed',
59
+ reason: 'read-reply-timed-out',
60
+ requiresReconciliation: false,
61
+ generation,
62
+ };
63
+ case 'dropped':
64
+ return context.operation === 'write'
65
+ ? {
66
+ status: 'unknown-outcome',
67
+ reason: 'write-reply-dropped',
68
+ requiresReconciliation: true,
69
+ generation,
70
+ }
71
+ : {
72
+ status: 'failed',
73
+ reason: 'read-reply-dropped',
74
+ requiresReconciliation: false,
75
+ generation,
76
+ };
77
+ }
78
+ }
79
+ /** Automatic retries are restricted to explicitly classified idempotent reads. */
80
+ export function canAutoRetry(descriptor, result) {
81
+ return (descriptor.mutationImpact === 'read' &&
82
+ descriptor.support.read.supported &&
83
+ descriptor.retryClass === 'idempotent-read' &&
84
+ result.status === 'failed');
85
+ }
@@ -0,0 +1,43 @@
1
+ import type { Brand } from './brand.js';
2
+ import { DomainError } from './errors.js';
3
+ export type PhysicalModemId = Brand<string, 'PhysicalModemId'>;
4
+ export type StableKey = Brand<string, 'StableKey'>;
5
+ export type PhysicalIdentitySource = 'serial' | 'id-path' | 'fallback';
6
+ export type PhysicalIdentityFacts = {
7
+ readonly serial?: string;
8
+ readonly idPath?: string;
9
+ readonly fallback?: string;
10
+ };
11
+ export type ResolvedPhysicalModemIdentity = {
12
+ readonly physicalModemId: PhysicalModemId;
13
+ readonly stableKey: StableKey;
14
+ readonly source: PhysicalIdentitySource;
15
+ };
16
+ export type PhysicalModemIdentityErrorReason = 'empty' | 'unsupported-shape' | 'mm-object-path' | 'interface-name' | 'ip-address' | 'equipment-identifier' | 'subscriber-identifier' | 'fallback-too-long' | 'no-identity-facts';
17
+ export declare class PhysicalModemIdentityError extends DomainError {
18
+ readonly reason: PhysicalModemIdentityErrorReason;
19
+ readonly name = "PhysicalModemIdentityError";
20
+ constructor(reason: PhysicalModemIdentityErrorReason);
21
+ }
22
+ /** Construct only from a canonical serial/ID_PATH/bounded-fallback identity. */
23
+ export declare function physicalModemId(value: string): PhysicalModemId;
24
+ /** Construct the actor/storage key for a validated physical modem identity. */
25
+ export declare function stableKeyFromPhysicalModemId(id: PhysicalModemId): StableKey;
26
+ /** Parse a canonical stable key while re-validating its embedded physical identity. */
27
+ export declare function stableKey(value: string): StableKey;
28
+ /** Resolve one identity with the frozen serial → ID_PATH → bounded-fallback precedence. */
29
+ export declare function resolvePhysicalModemIdentity(facts: PhysicalIdentityFacts): ResolvedPhysicalModemIdentity;
30
+ export type PortableIdentityAnchor = 'usb-serial' | 'id-path' | 'ifname';
31
+ export interface PortablePhysicalObservation {
32
+ readonly ifname: string;
33
+ readonly idPath?: string;
34
+ readonly vendorId?: string;
35
+ readonly serial?: string;
36
+ }
37
+ export interface PortablePhysicalDeviceIdentity {
38
+ readonly identityKey: string;
39
+ readonly anchor: PortableIdentityAnchor;
40
+ readonly linkId: string;
41
+ }
42
+ export declare function mintLinkId(identityKey: string): string;
43
+ export declare function resolvePortablePhysicalIdentity(observation: PortablePhysicalObservation): PortablePhysicalDeviceIdentity;
@@ -0,0 +1,113 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { DomainError } from './errors.js';
3
+ export class PhysicalModemIdentityError extends DomainError {
4
+ reason;
5
+ name = 'PhysicalModemIdentityError';
6
+ constructor(reason) {
7
+ super(`physical modem identity refused: ${reason}`);
8
+ this.reason = reason;
9
+ }
10
+ }
11
+ const PHYSICAL_ID_PREFIXES = [
12
+ ['serial', 'serial:'],
13
+ ['id-path', 'id-path:'],
14
+ ['fallback', 'fallback:'],
15
+ ];
16
+ const FALLBACK_MAX_LENGTH = 128;
17
+ const MM_OBJECT_PATH = /^\/org\/freedesktop\/ModemManager1\/Modem(?:\/|$)/;
18
+ const INTERFACE_NAME = /^(?:wwan|eth|enp|ens|eno|wlan|wl|ppp|usb|rmnet|cdc-wdm|ttyUSB|ttyACM)\d[\w.-]*$/i;
19
+ const EQUIPMENT_IDENTIFIER = /^\d{14,16}$/;
20
+ const SUBSCRIBER_IDENTIFIER = /^(?:89\d{16,30}|\d{18,32})$/;
21
+ function isIpv4(value) {
22
+ const octets = value.split('.');
23
+ return (octets.length === 4 && octets.every((octet) => /^\d{1,3}$/.test(octet) && Number(octet) <= 255));
24
+ }
25
+ function isIpv6(value) {
26
+ if (!value.includes(':') || !/^[0-9a-f:]+$/i.test(value)) {
27
+ return false;
28
+ }
29
+ const groups = value.split(':');
30
+ const hasCompression = value.includes('::');
31
+ const populatedGroups = groups.filter((group) => group.length > 0);
32
+ return (populatedGroups.every((group) => group.length <= 4) &&
33
+ (hasCompression ? populatedGroups.length < 8 : populatedGroups.length === 8));
34
+ }
35
+ function assertSafeIdentityPart(value) {
36
+ if (value.length === 0) {
37
+ throw new PhysicalModemIdentityError('empty');
38
+ }
39
+ if (MM_OBJECT_PATH.test(value)) {
40
+ throw new PhysicalModemIdentityError('mm-object-path');
41
+ }
42
+ if (INTERFACE_NAME.test(value)) {
43
+ throw new PhysicalModemIdentityError('interface-name');
44
+ }
45
+ if (isIpv4(value) || isIpv6(value)) {
46
+ throw new PhysicalModemIdentityError('ip-address');
47
+ }
48
+ if (SUBSCRIBER_IDENTIFIER.test(value)) {
49
+ throw new PhysicalModemIdentityError('subscriber-identifier');
50
+ }
51
+ if (EQUIPMENT_IDENTIFIER.test(value)) {
52
+ throw new PhysicalModemIdentityError('equipment-identifier');
53
+ }
54
+ }
55
+ function splitPhysicalModemId(value) {
56
+ for (const [source, prefix] of PHYSICAL_ID_PREFIXES) {
57
+ if (value.startsWith(prefix)) {
58
+ return [source, value.slice(prefix.length)];
59
+ }
60
+ }
61
+ throw new PhysicalModemIdentityError('unsupported-shape');
62
+ }
63
+ /** Construct only from a canonical serial/ID_PATH/bounded-fallback identity. */
64
+ export function physicalModemId(value) {
65
+ const [source, identityPart] = splitPhysicalModemId(value.trim());
66
+ assertSafeIdentityPart(identityPart);
67
+ if (source === 'fallback' && identityPart.length > FALLBACK_MAX_LENGTH) {
68
+ throw new PhysicalModemIdentityError('fallback-too-long');
69
+ }
70
+ return `${source}:${identityPart}`;
71
+ }
72
+ /** Construct the actor/storage key for a validated physical modem identity. */
73
+ export function stableKeyFromPhysicalModemId(id) {
74
+ return `modem:${id}`;
75
+ }
76
+ /** Parse a canonical stable key while re-validating its embedded physical identity. */
77
+ export function stableKey(value) {
78
+ if (!value.startsWith('modem:')) {
79
+ throw new PhysicalModemIdentityError('unsupported-shape');
80
+ }
81
+ return stableKeyFromPhysicalModemId(physicalModemId(value.slice('modem:'.length)));
82
+ }
83
+ /** Resolve one identity with the frozen serial → ID_PATH → bounded-fallback precedence. */
84
+ export function resolvePhysicalModemIdentity(facts) {
85
+ const candidates = [
86
+ ['serial', facts.serial],
87
+ ['id-path', facts.idPath],
88
+ ['fallback', facts.fallback],
89
+ ];
90
+ for (const [source, candidate] of candidates) {
91
+ const normalized = candidate?.trim();
92
+ if (normalized !== undefined && normalized.length > 0) {
93
+ const id = physicalModemId(`${source}:${normalized}`);
94
+ return { physicalModemId: id, stableKey: stableKeyFromPhysicalModemId(id), source };
95
+ }
96
+ }
97
+ throw new PhysicalModemIdentityError('no-identity-facts');
98
+ }
99
+ export function mintLinkId(identityKey) {
100
+ return `lnk_${createHash('sha256').update(identityKey).digest('hex').slice(0, 16)}`;
101
+ }
102
+ export function resolvePortablePhysicalIdentity(observation) {
103
+ const serial = observation.serial?.trim();
104
+ const idPath = observation.idPath?.trim();
105
+ const vendor = observation.vendorId?.trim().toLowerCase();
106
+ const identityKey = serial
107
+ ? `usb-serial:${vendor ? `${vendor}:` : ''}${serial}`
108
+ : idPath
109
+ ? `id-path:${idPath}`
110
+ : `ifname:${observation.ifname}`;
111
+ const anchor = serial ? 'usb-serial' : idPath ? 'id-path' : 'ifname';
112
+ return { identityKey, anchor, linkId: mintLinkId(identityKey) };
113
+ }
@@ -1,14 +1,5 @@
1
- // Durable desired-state policy the operator's intent for one modem.
2
- //
3
- // A2.1 stubbed this shape so the domain could enforce "an ambiguous identity may
4
- // never bind durable policy" (see `policyBindingKey`). A2.2 owns the REAL
5
- // definition below: the full connection / roaming / radio / simSlot / recovery /
6
- // usage intent the desired-state planner reconciles (see `../ports/reconcile`).
7
-
8
- import type { EquipmentId, LogicalSlotId, ModemIdentity } from './identity';
9
- import { assertCanBindPolicy } from './identity';
10
- import type { RadioAccessTechnology } from './state';
11
-
1
+ import type { EquipmentId, LogicalSlotId, ModemIdentity } from './identity.js';
2
+ import type { RadioAccessTechnology } from './state.js';
12
3
  /**
13
4
  * The subset of identity a durable policy is allowed to key off.
14
5
  *
@@ -17,13 +8,11 @@ import type { RadioAccessTechnology } from './state';
17
8
  * anchored by the equipment id — never to a per-boot handle or a subscriber id.
18
9
  */
19
10
  export interface PolicyBindingKey {
20
- readonly logicalSlotId?: LogicalSlotId;
21
- readonly equipmentId: EquipmentId;
11
+ readonly logicalSlotId?: LogicalSlotId;
12
+ readonly equipmentId: EquipmentId;
22
13
  }
23
-
24
14
  /** IP address family a connection requests. */
25
15
  export type IpFamily = 'ipv4' | 'ipv6' | 'ipv4v6';
26
-
27
16
  /**
28
17
  * Connection credentials. TODAY'S SEMANTICS (draft §round-5 auth): these are
29
18
  * persisted ONLY in the NetworkManager profile; the controller keeps them
@@ -31,24 +20,22 @@ export type IpFamily = 'ipv4' | 'ipv6' | 'ipv4v6';
31
20
  * SENSITIVE and is ALWAYS redacted in logs / output / receipts (see `../redact`).
32
21
  */
33
22
  export interface DesiredAuth {
34
- readonly username?: string;
35
- /** SENSITIVE — always redacted; NM-profile-persisted only, transient in memory. */
36
- readonly password?: string;
23
+ readonly username?: string;
24
+ /** SENSITIVE — always redacted; NM-profile-persisted only, transient in memory. */
25
+ readonly password?: string;
37
26
  }
38
-
39
27
  /** The data-connection intent. `apn: "auto"` selects NM Auto-APN (A4.1). */
40
28
  export interface DesiredConnection {
41
- readonly apn: 'auto' | string;
42
- readonly ipFamily: IpFamily;
43
- readonly auth?: DesiredAuth;
44
- /**
45
- * Manual operator selection (`gsm.network-id`) — pin registration to a specific
46
- * PLMN. Honored only while roaming (A4.1 amendment): the NM profile writes
47
- * `gsm.network-id = roaming ? networkId : ""`, so it is cleared when roaming is off.
48
- */
49
- readonly networkId?: string;
29
+ readonly apn: 'auto' | string;
30
+ readonly ipFamily: IpFamily;
31
+ readonly auth?: DesiredAuth;
32
+ /**
33
+ * Manual operator selection (`gsm.network-id`) — pin registration to a specific
34
+ * PLMN. Honored only while roaming (A4.1 amendment): the NM profile writes
35
+ * `gsm.network-id = roaming ? networkId : ""`, so it is cleared when roaming is off.
36
+ */
37
+ readonly networkId?: string;
50
38
  }
51
-
52
39
  /**
53
40
  * Radio access-technology intent. `preferenceOrdered` is ranked most-preferred
54
41
  * first ("prefer 5G" ⇒ `['5gnr', 'lte', …]`); it must be honored as a PREFERENCE,
@@ -57,27 +44,24 @@ export interface DesiredConnection {
57
44
  * when present, hard-limits the technologies the modem may use at all.
58
45
  */
59
46
  export interface DesiredRadio {
60
- readonly preferenceOrdered: readonly RadioAccessTechnology[];
61
- readonly allowedSet?: ReadonlySet<RadioAccessTechnology>;
47
+ readonly preferenceOrdered: readonly RadioAccessTechnology[];
48
+ readonly allowedSet?: ReadonlySet<RadioAccessTechnology>;
62
49
  }
63
-
64
50
  /**
65
51
  * Recovery intent. DISABLED BY DEFAULT — the evidence-gated recovery ladder (A3.4)
66
52
  * adds the per-step budgets / cooldowns; in Phase A `enabled` defaults to `false`
67
53
  * and no recovery action is ever taken unless it is explicitly turned on.
68
54
  */
69
55
  export interface DesiredRecovery {
70
- readonly enabled: boolean;
56
+ readonly enabled: boolean;
71
57
  }
72
-
73
58
  /** Data-usage policy — local-controller owned (see ports README ownership table). */
74
59
  export interface DesiredUsage {
75
- /** Day of month (1–31) the usage cycle resets; UTC, month-length clamped (A4.3). */
76
- readonly cycleDay?: number;
77
- /** Advisory threshold in bytes; crossing it raises an advisory, never gates. */
78
- readonly thresholdBytes?: number;
60
+ /** Day of month (1–31) the usage cycle resets; UTC, month-length clamped (A4.3). */
61
+ readonly cycleDay?: number;
62
+ /** Advisory threshold in bytes; crossing it raises an advisory, never gates. */
63
+ readonly thresholdBytes?: number;
79
64
  }
80
-
81
65
  /**
82
66
  * Operator-authored durable cellular policy, bound to a high/medium-confidence
83
67
  * identity via `boundTo` (a low-confidence identity cannot produce a binding key,
@@ -92,46 +76,27 @@ export interface DesiredUsage {
92
76
  * - usage — data-usage cycle + advisory threshold.
93
77
  */
94
78
  export interface DesiredCellularPolicy {
95
- readonly boundTo: PolicyBindingKey;
96
- readonly enabled: boolean;
97
- readonly connection: DesiredConnection;
98
- readonly roaming: boolean;
99
- readonly radio: DesiredRadio;
100
- readonly simSlot?: number;
101
- readonly recovery: DesiredRecovery;
102
- readonly usage: DesiredUsage;
79
+ readonly boundTo: PolicyBindingKey;
80
+ readonly enabled: boolean;
81
+ readonly connection: DesiredConnection;
82
+ readonly roaming: boolean;
83
+ readonly radio: DesiredRadio;
84
+ readonly simSlot?: number;
85
+ readonly recovery: DesiredRecovery;
86
+ readonly usage: DesiredUsage;
103
87
  }
104
-
105
88
  /** Recovery disabled — the Phase-A default (A3.4 adds budgets / cooldowns). */
106
- export const RECOVERY_DISABLED: DesiredRecovery = { enabled: false };
107
-
89
+ export declare const RECOVERY_DISABLED: DesiredRecovery;
108
90
  /**
109
91
  * Build a sensible default policy for a bindable identity: NM activation on,
110
92
  * Auto-APN, dual-stack IP, roaming off, prefer newest RAT down to GSM, recovery
111
93
  * disabled, no usage limits. Concrete adapters and the planner refine from here.
112
94
  */
113
- export function defaultCellularPolicy(boundTo: PolicyBindingKey): DesiredCellularPolicy {
114
- return {
115
- boundTo,
116
- enabled: true,
117
- connection: { apn: 'auto', ipFamily: 'ipv4v6' },
118
- roaming: false,
119
- radio: { preferenceOrdered: ['5gnr', 'lte', 'umts', 'gsm'] },
120
- recovery: RECOVERY_DISABLED,
121
- usage: {},
122
- };
123
- }
124
-
95
+ export declare function defaultCellularPolicy(boundTo: PolicyBindingKey): DesiredCellularPolicy;
125
96
  /**
126
97
  * Derive the durable binding key for an identity, REFUSING low-confidence
127
98
  * (ambiguous) identities. This is the structural gate that makes it impossible to
128
99
  * bind durable policy to a duplicate/zero-IMEI modem: no key can be produced, so
129
100
  * no `DesiredCellularPolicy` can name it. Throws `PolicyBindingRefusedError`.
130
101
  */
131
- export function policyBindingKey(identity: ModemIdentity): PolicyBindingKey {
132
- assertCanBindPolicy(identity);
133
- // exactOptionalPropertyTypes: only include logicalSlotId when actually present.
134
- return identity.logicalSlotId !== undefined
135
- ? { logicalSlotId: identity.logicalSlotId, equipmentId: identity.equipmentId }
136
- : { equipmentId: identity.equipmentId };
137
- }
102
+ export declare function policyBindingKey(identity: ModemIdentity): PolicyBindingKey;
@@ -0,0 +1,38 @@
1
+ // Durable desired-state policy — the operator's intent for one modem.
2
+ //
3
+ // A2.1 stubbed this shape so the domain could enforce "an ambiguous identity may
4
+ // never bind durable policy" (see `policyBindingKey`). A2.2 owns the REAL
5
+ // definition below: the full connection / roaming / radio / simSlot / recovery /
6
+ // usage intent the desired-state planner reconciles (see `../ports/reconcile`).
7
+ import { assertCanBindPolicy } from './identity.js';
8
+ /** Recovery disabled — the Phase-A default (A3.4 adds budgets / cooldowns). */
9
+ export const RECOVERY_DISABLED = { enabled: false };
10
+ /**
11
+ * Build a sensible default policy for a bindable identity: NM activation on,
12
+ * Auto-APN, dual-stack IP, roaming off, prefer newest RAT down to GSM, recovery
13
+ * disabled, no usage limits. Concrete adapters and the planner refine from here.
14
+ */
15
+ export function defaultCellularPolicy(boundTo) {
16
+ return {
17
+ boundTo,
18
+ enabled: true,
19
+ connection: { apn: 'auto', ipFamily: 'ipv4v6' },
20
+ roaming: false,
21
+ radio: { preferenceOrdered: ['5gnr', 'lte', 'umts', 'gsm'] },
22
+ recovery: RECOVERY_DISABLED,
23
+ usage: {},
24
+ };
25
+ }
26
+ /**
27
+ * Derive the durable binding key for an identity, REFUSING low-confidence
28
+ * (ambiguous) identities. This is the structural gate that makes it impossible to
29
+ * bind durable policy to a duplicate/zero-IMEI modem: no key can be produced, so
30
+ * no `DesiredCellularPolicy` can name it. Throws `PolicyBindingRefusedError`.
31
+ */
32
+ export function policyBindingKey(identity) {
33
+ assertCanBindPolicy(identity);
34
+ // exactOptionalPropertyTypes: only include logicalSlotId when actually present.
35
+ return identity.logicalSlotId !== undefined
36
+ ? { logicalSlotId: identity.logicalSlotId, equipmentId: identity.equipmentId }
37
+ : { equipmentId: identity.equipmentId };
38
+ }
@@ -0,0 +1,27 @@
1
+ export type ShadowGeneration = '2G' | '3G' | '4G' | '5G';
2
+ export type ShadowSignalBucket = 'none' | 'poor' | 'fair' | 'good' | 'excellent';
3
+ export interface ShadowModemState {
4
+ readonly deviceKey: string;
5
+ readonly present: boolean;
6
+ readonly registration?: string;
7
+ readonly signalBucket?: ShadowSignalBucket;
8
+ readonly operatorName?: string;
9
+ readonly simPresent?: boolean;
10
+ readonly networkType?: ShadowGeneration;
11
+ }
12
+ export declare const SHADOW_COMPARABLE_FIELDS: readonly ["present", "registration", "signalBucket", "operatorName", "simPresent", "networkType"];
13
+ export type ShadowComparableField = (typeof SHADOW_COMPARABLE_FIELDS)[number];
14
+ export interface ShadowFieldDivergence {
15
+ readonly field: ShadowComparableField;
16
+ readonly mmcli: unknown;
17
+ readonly dbus: unknown;
18
+ }
19
+ export interface ShadowModemDivergence {
20
+ readonly deviceKey: string;
21
+ readonly kind: 'only-in-mmcli' | 'only-in-dbus' | 'field-mismatch';
22
+ readonly fields?: readonly ShadowFieldDivergence[];
23
+ }
24
+ export declare function classifyShadowDivergences(mmcli: readonly ShadowModemState[], dbus: readonly ShadowModemState[]): ShadowModemDivergence[];
25
+ export declare function foldGeneration(token: string): ShadowGeneration | undefined;
26
+ export declare function foldGenerations(tokens: Iterable<string>): ShadowGeneration | undefined;
27
+ export declare function foldSignalBucket(quality: number | undefined): ShadowSignalBucket | undefined;
@@ -0,0 +1,70 @@
1
+ export const SHADOW_COMPARABLE_FIELDS = [
2
+ 'present',
3
+ 'registration',
4
+ 'signalBucket',
5
+ 'operatorName',
6
+ 'simPresent',
7
+ 'networkType',
8
+ ];
9
+ export function classifyShadowDivergences(mmcli, dbus) {
10
+ const left = new Map(mmcli.map((state) => [state.deviceKey, state]));
11
+ const right = new Map(dbus.map((state) => [state.deviceKey, state]));
12
+ const result = [];
13
+ for (const [deviceKey, mmcliState] of left) {
14
+ const dbusState = right.get(deviceKey);
15
+ if (dbusState === undefined) {
16
+ result.push({ deviceKey, kind: 'only-in-mmcli' });
17
+ continue;
18
+ }
19
+ const fields = [];
20
+ for (const field of SHADOW_COMPARABLE_FIELDS) {
21
+ const a = mmcliState[field];
22
+ const b = dbusState[field];
23
+ if (a !== undefined && b !== undefined && a !== b)
24
+ fields.push({ field, mmcli: a, dbus: b });
25
+ }
26
+ if (fields.length > 0)
27
+ result.push({ deviceKey, kind: 'field-mismatch', fields });
28
+ }
29
+ for (const deviceKey of right.keys())
30
+ if (!left.has(deviceKey))
31
+ result.push({ deviceKey, kind: 'only-in-dbus' });
32
+ return result;
33
+ }
34
+ const GENERATIONS = {
35
+ '2g': '2G',
36
+ gsm: '2G',
37
+ '3g': '3G',
38
+ '3g+': '3G',
39
+ umts: '3G',
40
+ '4g': '4G',
41
+ lte: '4G',
42
+ '5g': '5G',
43
+ '5gnr': '5G',
44
+ };
45
+ const ORDER = ['2G', '3G', '4G', '5G'];
46
+ export function foldGeneration(token) {
47
+ return GENERATIONS[token.trim().toLowerCase()];
48
+ }
49
+ export function foldGenerations(tokens) {
50
+ let best;
51
+ for (const token of tokens) {
52
+ const value = foldGeneration(token);
53
+ if (value !== undefined && (best === undefined || ORDER.indexOf(value) > ORDER.indexOf(best)))
54
+ best = value;
55
+ }
56
+ return best;
57
+ }
58
+ export function foldSignalBucket(quality) {
59
+ if (quality === undefined || !Number.isFinite(quality))
60
+ return undefined;
61
+ return quality <= 0
62
+ ? 'none'
63
+ : quality < 25
64
+ ? 'poor'
65
+ : quality < 50
66
+ ? 'fair'
67
+ : quality < 75
68
+ ? 'good'
69
+ : 'excellent';
70
+ }