@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,46 @@
1
+ import { z } from 'zod';
2
+ import type { ProviderMatchRequest } from '../contracts.js';
3
+ import { type UfiHttpResponse, type UfiReadCommand, type UfiTransport } from './transport.js';
4
+ export type UfiCredentials = {
5
+ readonly username: string;
6
+ readonly password: string;
7
+ };
8
+ export type UfiOptions = {
9
+ readonly interfaceName: string;
10
+ readonly adminUrl: string;
11
+ readonly transport: UfiTransport;
12
+ /** Ephemeral bench input. Injected by the caller, never persisted, never logged. */
13
+ readonly credentials: UfiCredentials;
14
+ readonly now?: () => number;
15
+ };
16
+ /** The HIMI firmware's own word for "your session is gone". */
17
+ export declare const UFI_SESSION_REFUSAL = "SessionOut";
18
+ declare const ufiReplySchema: z.ZodObject<{
19
+ reply: z.ZodString;
20
+ session: z.ZodOptional<z.ZodString>;
21
+ params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
22
+ }, z.core.$strip>;
23
+ export type UfiReply = z.infer<typeof ufiReplySchema>;
24
+ export declare function parseUfiReply(body: string): UfiReply | undefined;
25
+ /**
26
+ * Session acquisition, bounded to ONE login per physical modem per generation.
27
+ *
28
+ * The bound is a set of spent keys rather than a counter reset on failure: a HIMI
29
+ * device that refused a credential refuses it again, and a provider that re-tries on
30
+ * every poll turns a wrong password into a lockout nobody asked for. A refused or
31
+ * expired session therefore surfaces as an honest auth-expired READING; the next
32
+ * enumeration (a new generation) gets the next single attempt.
33
+ */
34
+ export declare class UfiSessionRuntime {
35
+ #private;
36
+ protected readonly options: UfiOptions;
37
+ constructor(options: UfiOptions);
38
+ protected session(request: ProviderMatchRequest): Promise<string | undefined>;
39
+ protected read(command: UfiReadCommand, session: string): Promise<UfiHttpResponse>;
40
+ /** A refused session is dropped so the NEXT generation may spend its own attempt. */
41
+ protected forgetSession(request: ProviderMatchRequest): void;
42
+ protected sessionKey(request: ProviderMatchRequest): string;
43
+ protected observedAt(): number;
44
+ private request;
45
+ }
46
+ export {};
@@ -0,0 +1,92 @@
1
+ import { z } from 'zod';
2
+ import { UFI_API_PATH, } from './transport.js';
3
+ /** The HIMI firmware's own word for "your session is gone". */
4
+ export const UFI_SESSION_REFUSAL = 'SessionOut';
5
+ const flatRecordSchema = z.record(z.string(), z.union([z.string(), z.number()]));
6
+ const ufiReplySchema = z.object({
7
+ reply: z.string(),
8
+ session: z.string().optional(),
9
+ params: flatRecordSchema.optional(),
10
+ });
11
+ export function parseUfiReply(body) {
12
+ const parsed = z
13
+ .string()
14
+ .transform((value, context) => {
15
+ try {
16
+ return JSON.parse(value);
17
+ }
18
+ catch (error) {
19
+ if (!(error instanceof SyntaxError))
20
+ throw error;
21
+ context.addIssue({ code: 'custom', message: 'invalid JSON' });
22
+ return z.NEVER;
23
+ }
24
+ })
25
+ .pipe(ufiReplySchema)
26
+ .safeParse(body);
27
+ return parsed.success ? parsed.data : undefined;
28
+ }
29
+ /**
30
+ * Session acquisition, bounded to ONE login per physical modem per generation.
31
+ *
32
+ * The bound is a set of spent keys rather than a counter reset on failure: a HIMI
33
+ * device that refused a credential refuses it again, and a provider that re-tries on
34
+ * every poll turns a wrong password into a lockout nobody asked for. A refused or
35
+ * expired session therefore surfaces as an honest auth-expired READING; the next
36
+ * enumeration (a new generation) gets the next single attempt.
37
+ */
38
+ export class UfiSessionRuntime {
39
+ options;
40
+ #sessions = new Map();
41
+ #spent = new Set();
42
+ constructor(options) {
43
+ this.options = options;
44
+ }
45
+ async session(request) {
46
+ const key = this.sessionKey(request);
47
+ const held = this.#sessions.get(key);
48
+ if (held !== undefined)
49
+ return held;
50
+ if (this.#spent.has(key))
51
+ return undefined;
52
+ this.#spent.add(key);
53
+ const response = await this.request('login', JSON.stringify({
54
+ cmdid: 'login',
55
+ username: this.options.credentials.username,
56
+ password: this.options.credentials.password,
57
+ }));
58
+ const reply = response.status === 200 ? parseUfiReply(response.body) : undefined;
59
+ const session = reply?.reply === 'ok' ? reply.session : undefined;
60
+ if (session === undefined || session === '')
61
+ return undefined;
62
+ this.#sessions.set(key, session);
63
+ return session;
64
+ }
65
+ read(command, session) {
66
+ return this.request(command, JSON.stringify({ cmdid: command, sessionId: session }), session);
67
+ }
68
+ /** A refused session is dropped so the NEXT generation may spend its own attempt. */
69
+ forgetSession(request) {
70
+ this.#sessions.delete(this.sessionKey(request));
71
+ }
72
+ sessionKey(request) {
73
+ return `${request.physicalModemId}:${request.generation}`;
74
+ }
75
+ observedAt() {
76
+ return this.options.now?.() ?? Date.now();
77
+ }
78
+ request(command, body, session) {
79
+ return this.options.transport.request({
80
+ method: 'POST',
81
+ url: `${this.options.adminUrl}${UFI_API_PATH}`,
82
+ command,
83
+ body,
84
+ headers: [
85
+ ...(session === undefined ? [] : [`Authorization: ${session}`]),
86
+ 'Content-Type: application/json;charset=UTF-8',
87
+ ],
88
+ interfaceName: this.options.interfaceName,
89
+ redirect: 'error',
90
+ });
91
+ }
92
+ }
@@ -0,0 +1,29 @@
1
+ /** The seven read commands the HIMI firmware answers. Bench-observed, not inferred. */
2
+ export declare const UFI_READ_COMMANDS: readonly ["getoverview", "getsysinfo", "getallstatus", "getapninfo", "getnetworkmode", "gethimiusbtether", "getproduceinfo"];
3
+ export type UfiReadCommand = (typeof UFI_READ_COMMANDS)[number];
4
+ /**
5
+ * The ONE non-read command. It opens a server-side session and changes nothing on the
6
+ * device; the modem's configuration, radio and storage are untouched by it.
7
+ */
8
+ export declare const UFI_SESSION_COMMANDS: readonly ["login"];
9
+ export type UfiSessionCommand = (typeof UFI_SESSION_COMMANDS)[number];
10
+ export declare const UFI_COMMANDS: readonly ["getoverview", "getsysinfo", "getallstatus", "getapninfo", "getnetworkmode", "gethimiusbtether", "getproduceinfo", "login"];
11
+ export type UfiCommand = UfiReadCommand | UfiSessionCommand;
12
+ export declare const UFI_API_PATH = "/himiapi/json";
13
+ export type UfiHttpRequest = {
14
+ readonly method: 'POST';
15
+ readonly url: string;
16
+ /** Frozen vocabulary: the field a write would have to travel in cannot name one. */
17
+ readonly command: UfiCommand;
18
+ readonly body: string;
19
+ readonly headers: readonly string[];
20
+ readonly interfaceName: string;
21
+ readonly redirect: 'error';
22
+ };
23
+ export type UfiHttpResponse = {
24
+ readonly status: number;
25
+ readonly body: string;
26
+ };
27
+ export interface UfiTransport {
28
+ request(request: UfiHttpRequest): Promise<UfiHttpResponse>;
29
+ }
@@ -0,0 +1,25 @@
1
+ // Qualcomm UFI / HIMI transport shape.
2
+ //
3
+ // HIMI is not a REST surface: every call is `POST /himiapi/json` and the VERB lives in
4
+ // the JSON body's `cmdid`. So "read-only" cannot be expressed here as an HTTP-method
5
+ // restriction the way it can for HiLink. The command vocabulary is frozen as a union
6
+ // instead — a request this transport can carry names a member of `UFI_COMMANDS`, and no
7
+ // `set*` / flash / reset member exists — which makes a write UNREPRESENTABLE rather than
8
+ // merely refused at runtime.
9
+ /** The seven read commands the HIMI firmware answers. Bench-observed, not inferred. */
10
+ export const UFI_READ_COMMANDS = [
11
+ 'getoverview',
12
+ 'getsysinfo',
13
+ 'getallstatus',
14
+ 'getapninfo',
15
+ 'getnetworkmode',
16
+ 'gethimiusbtether',
17
+ 'getproduceinfo',
18
+ ];
19
+ /**
20
+ * The ONE non-read command. It opens a server-side session and changes nothing on the
21
+ * device; the modem's configuration, radio and storage are untouched by it.
22
+ */
23
+ export const UFI_SESSION_COMMANDS = ['login'];
24
+ export const UFI_COMMANDS = [...UFI_READ_COMMANDS, ...UFI_SESSION_COMMANDS];
25
+ export const UFI_API_PATH = '/himiapi/json';
@@ -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';
@@ -0,0 +1,51 @@
1
+ import type { ObservationEnvelope } from '../../domain/index.js';
2
+ import type { NormalizedModemObservation } from '../../observations/index.js';
3
+ import type { CapabilityReader, ProviderDefinition, ProviderExecutionContext, ProviderMatchRequest, ProviderOperationsSurface } from '../contracts.js';
4
+ import { ZteSessionRuntime } from './session.js';
5
+ import type { ZteTransport } from './transport.js';
6
+ export declare const ZTE_PATHS: {
7
+ readonly get: "/goform/goform_get_cmd_process";
8
+ readonly set: "/goform/goform_set_cmd_process";
9
+ };
10
+ export type ZteProfile = {
11
+ readonly id: 'mf79u-legacy' | 'mf266-salted';
12
+ readonly firmware: 'MF79U' | 'MF266';
13
+ readonly algorithm: 'legacy-base64' | 'salted-sha256';
14
+ };
15
+ export declare const ZTE_PROFILES: readonly [{
16
+ readonly id: "mf79u-legacy";
17
+ readonly firmware: "MF79U";
18
+ readonly algorithm: "legacy-base64";
19
+ }, {
20
+ readonly id: "mf266-salted";
21
+ readonly firmware: "MF266";
22
+ readonly algorithm: "salted-sha256";
23
+ }];
24
+ export declare const ZTE_UNKNOWN_PROFILE = "zte-unknown-read-only";
25
+ export type ZteCredentials = {
26
+ readonly username: string;
27
+ readonly password: string;
28
+ };
29
+ export type ZteOptions = {
30
+ readonly interfaceName: string;
31
+ readonly adminUrl: string;
32
+ readonly transport: ZteTransport;
33
+ readonly credentials: ZteCredentials;
34
+ readonly now?: () => number;
35
+ };
36
+ type ZteOperations = ProviderOperationsSurface;
37
+ export declare function zteProfileForFirmware(firmware: string | undefined): ZteProfile | undefined;
38
+ export declare function zteProfileById(profileId: string): ZteProfile | undefined;
39
+ export declare class ZteGoformProvider extends ZteSessionRuntime {
40
+ fingerprint(request: ProviderMatchRequest): Promise<{
41
+ signal: "unknown" | "match";
42
+ strength: "strong";
43
+ profiles: string[];
44
+ detail: string;
45
+ }>;
46
+ capability(): CapabilityReader;
47
+ observe(context: ProviderExecutionContext): Promise<readonly ObservationEnvelope<NormalizedModemObservation>[]>;
48
+ operations(_profile: string): ZteOperations;
49
+ }
50
+ export declare function createZteGoformDefinition(options: ZteOptions): ProviderDefinition<NormalizedModemObservation, ZteOperations>;
51
+ export {};
@@ -0,0 +1,101 @@
1
+ import { normalizeZteObservation } from '../../observations/sources/zte.js';
2
+ import { parseZteRecord, ZteSessionRuntime } from './session.js';
3
+ export const ZTE_PATHS = {
4
+ get: '/goform/goform_get_cmd_process',
5
+ set: '/goform/goform_set_cmd_process',
6
+ };
7
+ export const ZTE_PROFILES = [
8
+ { id: 'mf79u-legacy', firmware: 'MF79U', algorithm: 'legacy-base64' },
9
+ { id: 'mf266-salted', firmware: 'MF266', algorithm: 'salted-sha256' },
10
+ ];
11
+ export const ZTE_UNKNOWN_PROFILE = 'zte-unknown-read-only';
12
+ export function zteProfileForFirmware(firmware) {
13
+ return ZTE_PROFILES.find((profile) => profile.firmware === firmware);
14
+ }
15
+ export function zteProfileById(profileId) {
16
+ return ZTE_PROFILES.find((profile) => profile.id === profileId);
17
+ }
18
+ export class ZteGoformProvider extends ZteSessionRuntime {
19
+ async fingerprint(request) {
20
+ const response = await this.get('cr_version,network_type');
21
+ const record = response.status === 200 ? parseZteRecord(response.body) : undefined;
22
+ const profile = zteProfileForFirmware(request.firmware);
23
+ const matches = record !== undefined;
24
+ return {
25
+ signal: matches ? 'match' : 'unknown',
26
+ strength: 'strong',
27
+ profiles: matches ? [profile?.id ?? ZTE_UNKNOWN_PROFILE] : [],
28
+ detail: matches ? 'zte-goform-shape' : 'zte-goform-not-proven',
29
+ };
30
+ }
31
+ capability() {
32
+ return {
33
+ id: 'zte-telemetry',
34
+ read: async () => {
35
+ const response = await this.get('network_type,signalbar,rssi,lte_rsrp,lte_rsrq,lte_snr');
36
+ const supported = response.status === 200 && parseZteRecord(response.body) !== undefined;
37
+ return {
38
+ signal: supported ? 'match' : 'mismatch',
39
+ strength: 'strong',
40
+ detail: supported ? 'telemetry-reported' : 'telemetry-refused',
41
+ };
42
+ },
43
+ };
44
+ }
45
+ async observe(context) {
46
+ const response = await this.get('network_type,signalbar,rssi,lte_rsrp,lte_rsrq,lte_snr,network_provider,cell_id', this.sessionCookie(context));
47
+ if (response.status !== 200)
48
+ return [];
49
+ return [
50
+ normalizeZteObservation({ body: response.body }, {
51
+ stableKey: `modem:${this.options.interfaceName}`,
52
+ generation: context.generation,
53
+ sourceEpoch: 1,
54
+ observedAt: (this.options.now?.() ?? Date.now()),
55
+ }),
56
+ ];
57
+ }
58
+ operations(_profile) {
59
+ return { access: 'read-only' };
60
+ }
61
+ }
62
+ export function createZteGoformDefinition(options) {
63
+ const runtime = new ZteGoformProvider(options);
64
+ return {
65
+ id: 'zte-goform',
66
+ profileVersion: '1',
67
+ eligibleTransports: ['network'],
68
+ passiveMatchers: ZTE_PROFILES.map((profile) => ({
69
+ id: `firmware-${profile.firmware}`,
70
+ fact: 'firmware',
71
+ expected: [profile.firmware],
72
+ profiles: [profile.id],
73
+ strength: 'strong',
74
+ required: true,
75
+ })),
76
+ unauthenticatedProbes: [
77
+ { id: 'zte-goform-shape', run: (request) => runtime.fingerprint(request) },
78
+ ],
79
+ authenticatedProfile: {
80
+ algorithm: 'firmware-selected-zte-goform',
81
+ attemptLimit: 1,
82
+ authenticate: (request, candidates) => runtime.authenticateProfile(request, candidates),
83
+ },
84
+ capabilityReaders: [runtime.capability()],
85
+ observe: (context) => runtime.observe(context),
86
+ operations: (profile) => runtime.operations(profile),
87
+ contractFixtures: ZTE_PROFILES.map((profile) => ({
88
+ profile: profile.id,
89
+ request: {
90
+ method: 'POST',
91
+ path: ZTE_PATHS.set,
92
+ goformId: profile.algorithm === 'legacy-base64' ? 'LOGIN' : 'LOGIN_MULTI_USER',
93
+ interfaceBound: true,
94
+ redirects: 'disabled',
95
+ },
96
+ response: profile.algorithm === 'salted-sha256'
97
+ ? { status: 'matched', sessionMaterial: '[redacted]', ad: '[redacted]' }
98
+ : { status: 'matched', sessionMaterial: '[redacted]' },
99
+ })),
100
+ };
101
+ }
@@ -0,0 +1,17 @@
1
+ import type { AuthenticatedProfileResult, ProviderMatchRequest } from '../contracts.js';
2
+ import { type ZteOptions } from './provider.js';
3
+ import type { ZteHttpResponse } from './transport.js';
4
+ export declare function parseZteRecord(body: string): Readonly<Record<string, string | number>> | undefined;
5
+ export declare class ZteSessionRuntime {
6
+ #private;
7
+ protected readonly options: ZteOptions;
8
+ constructor(options: ZteOptions);
9
+ authenticateProfile(request: ProviderMatchRequest, candidates: readonly string[]): Promise<AuthenticatedProfileResult>;
10
+ protected sessionCookie(request: ProviderMatchRequest): string | undefined;
11
+ protected get(cmd: string, cookie?: string, multiData?: boolean): Promise<ZteHttpResponse>;
12
+ private loginLegacy;
13
+ private loginSalted;
14
+ private sessionKey;
15
+ private post;
16
+ private request;
17
+ }
@@ -0,0 +1,133 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { z } from 'zod';
3
+ import { ZTE_PATHS, ZTE_UNKNOWN_PROFILE, zteProfileById, zteProfileForFirmware, } from './provider.js';
4
+ const flatRecordSchema = z.record(z.string(), z.union([z.string(), z.number()]));
5
+ export function parseZteRecord(body) {
6
+ const result = z
7
+ .string()
8
+ .transform((value, context) => {
9
+ try {
10
+ return JSON.parse(value);
11
+ }
12
+ catch (error) {
13
+ if (!(error instanceof SyntaxError))
14
+ throw error;
15
+ context.addIssue({ code: 'custom', message: 'invalid JSON' });
16
+ return z.NEVER;
17
+ }
18
+ })
19
+ .pipe(flatRecordSchema)
20
+ .safeParse(body);
21
+ return result.success ? result.data : undefined;
22
+ }
23
+ function stokCookie(response) {
24
+ const expected = 'set-cookie';
25
+ const header = Object.entries(response.headers ?? {}).find(([key]) => key.toLowerCase() === expected)?.[1];
26
+ const cookie = header?.split(';', 1)[0]?.trim();
27
+ return cookie?.startsWith('stok=') ? cookie : undefined;
28
+ }
29
+ function sha256(value) {
30
+ return createHash('sha256').update(value).digest('hex').toUpperCase();
31
+ }
32
+ export class ZteSessionRuntime {
33
+ options;
34
+ #sessions = new Map();
35
+ constructor(options) {
36
+ this.options = options;
37
+ }
38
+ async authenticateProfile(request, candidates) {
39
+ const candidate = candidates.length === 1 ? candidates[0] : undefined;
40
+ if (candidate === ZTE_UNKNOWN_PROFILE) {
41
+ return { status: 'matched', profile: ZTE_UNKNOWN_PROFILE, detail: 'read-only-fingerprint' };
42
+ }
43
+ const profile = zteProfileById(candidate ?? '');
44
+ if (profile === undefined || zteProfileForFirmware(request.firmware)?.id !== profile.id) {
45
+ return { status: 'refused', detail: 'profile-mismatch' };
46
+ }
47
+ return profile.algorithm === 'legacy-base64'
48
+ ? this.loginLegacy(request, profile)
49
+ : this.loginSalted(request, profile);
50
+ }
51
+ sessionCookie(request) {
52
+ return this.#sessions.get(this.sessionKey(request))?.cookie;
53
+ }
54
+ get(cmd, cookie, multiData = false) {
55
+ const query = new URLSearchParams({ isTest: 'false', cmd });
56
+ if (multiData)
57
+ query.set('multi_data', '1');
58
+ return this.request('GET', `${ZTE_PATHS.get}?${query.toString()}`, undefined, cookie);
59
+ }
60
+ async loginLegacy(request, profile) {
61
+ const response = await this.post(new URLSearchParams({
62
+ goformId: 'LOGIN',
63
+ isTest: 'false',
64
+ password: Buffer.from(this.options.credentials.password).toString('base64'),
65
+ }).toString());
66
+ const record = parseZteRecord(response.body);
67
+ const cookie = stokCookie(response);
68
+ if (response.status !== 200 || record?.result !== '0' || cookie === undefined) {
69
+ return {
70
+ status: 'refused',
71
+ detail: record?.LD === undefined ? 'auth-rejection' : 'protocol-mismatch',
72
+ };
73
+ }
74
+ this.#sessions.set(this.sessionKey(request), { cookie });
75
+ return { status: 'matched', profile: profile.id, detail: 'login-ok' };
76
+ }
77
+ async loginSalted(request, profile) {
78
+ const ldResponse = await this.get('LD');
79
+ const ld = parseZteRecord(ldResponse.body)?.LD;
80
+ if (ldResponse.status !== 200 || typeof ld !== 'string') {
81
+ return { status: 'refused', detail: 'protocol-mismatch' };
82
+ }
83
+ const login = await this.post(new URLSearchParams({
84
+ goformId: 'LOGIN_MULTI_USER',
85
+ isTest: 'false',
86
+ password: sha256(`${sha256(this.options.credentials.password)}${ld}`),
87
+ IP: 'localhost',
88
+ user: this.options.credentials.username,
89
+ }).toString());
90
+ const cookie = stokCookie(login);
91
+ if (login.status !== 200 ||
92
+ parseZteRecord(login.body)?.result !== '0' ||
93
+ cookie === undefined) {
94
+ return { status: 'refused', detail: 'auth-rejection' };
95
+ }
96
+ const versions = parseZteRecord((await this.get('wa_inner_version,cr_version', cookie, true)).body);
97
+ const rd = parseZteRecord((await this.get('RD', cookie)).body)?.RD;
98
+ const waVersion = versions?.wa_inner_version;
99
+ const crVersion = versions?.cr_version;
100
+ if (typeof waVersion !== 'string' || typeof crVersion !== 'string' || typeof rd !== 'string') {
101
+ return { status: 'refused', detail: 'protocol-mismatch' };
102
+ }
103
+ this.#sessions.set(this.sessionKey(request), {
104
+ cookie,
105
+ ad: sha256(`${sha256(`${waVersion}${crVersion}`)}${rd}`),
106
+ });
107
+ return { status: 'matched', profile: profile.id, detail: 'login-ok' };
108
+ }
109
+ sessionKey(request) {
110
+ return `${request.physicalModemId}:${request.generation}`;
111
+ }
112
+ post(body) {
113
+ return this.request('POST', ZTE_PATHS.set, body);
114
+ }
115
+ request(method, path, body, cookie) {
116
+ const headers = [
117
+ ...(method === 'POST'
118
+ ? ['Content-Type: application/x-www-form-urlencoded; charset=UTF-8']
119
+ : []),
120
+ ...(cookie === undefined ? [] : [`Cookie: ${cookie}`]),
121
+ `Origin: ${this.options.adminUrl}`,
122
+ `Referer: ${this.options.adminUrl}/index.html`,
123
+ ];
124
+ return this.options.transport.request({
125
+ method,
126
+ url: `${this.options.adminUrl}${path}`,
127
+ ...(body === undefined ? {} : { body }),
128
+ headers,
129
+ interfaceName: this.options.interfaceName,
130
+ redirect: 'error',
131
+ });
132
+ }
133
+ }
@@ -0,0 +1,16 @@
1
+ export type ZteHttpRequest = {
2
+ readonly method: 'GET' | 'POST';
3
+ readonly url: string;
4
+ readonly body?: string;
5
+ readonly headers: readonly string[];
6
+ readonly interfaceName: string;
7
+ readonly redirect: 'error';
8
+ };
9
+ export type ZteHttpResponse = {
10
+ readonly status: number;
11
+ readonly body: string;
12
+ readonly headers?: Readonly<Record<string, string>>;
13
+ };
14
+ export interface ZteTransport {
15
+ request(request: ZteHttpRequest): Promise<ZteHttpResponse>;
16
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,50 @@
1
+ import { type BandCertificationEntry, type BandName } from '../band/index.js';
2
+ import { type OperationDescriptor } from '../domain/index.js';
3
+ import type { ModemBands } from '../ports/index.js';
4
+ export declare const BAND_WRITE_OPERATION_ID = "modemmanager.bands";
5
+ /** The refusal reasons a band write may carry. Certification is the first gate. */
6
+ export declare const BAND_WRITE_REFUSAL = "band-certification-required";
7
+ export declare const BAND_READ_REFUSAL = "band-read-unsupported";
8
+ export declare const BAND_NONE_OFFERABLE_REFUSAL = "no-offerable-certified-bands";
9
+ /**
10
+ * The band-lock certification decision for one device, as a descriptor consumer sees it.
11
+ *
12
+ * `required` is `true` unconditionally and is typed as the literal, so no code path can
13
+ * produce a band-write decision that does not require certification. This is the one
14
+ * capability module documented as STRICTER than `support-claim.ts`'s `capable` floor.
15
+ */
16
+ export type BandWriteCertification = {
17
+ readonly required: true;
18
+ readonly satisfied: boolean;
19
+ readonly reason: 'band-certification-proven' | typeof BAND_WRITE_REFUSAL;
20
+ /** What the catalog proves is settable, intersected with what the modem advertises. */
21
+ readonly offerable: readonly BandName[];
22
+ };
23
+ export declare function describeBandWriteCertification(input: {
24
+ readonly entry: BandCertificationEntry | undefined;
25
+ readonly supported: readonly BandName[];
26
+ }): BandWriteCertification;
27
+ export type BandWriteDescriptorInput = {
28
+ readonly provider: string;
29
+ readonly profile: string;
30
+ readonly certification: BandWriteCertification;
31
+ readonly readSupported: boolean;
32
+ };
33
+ /**
34
+ * Whether a band readback confirms the write.
35
+ *
36
+ * A NARROWING lock must match exactly — a superset is a different lock from the one
37
+ * that was asked for. The reset (`['any']`) is the exception ModemManager forces:
38
+ * releasing a lock is `SetCurrentBands([ANY])`, and a modem afterwards reports either
39
+ * `any` or its whole supported set, both of which mean "no lock is in force".
40
+ */
41
+ export declare function bandWriteReadbackMatches(requested: readonly BandName[], observed: ModemBands): boolean;
42
+ /**
43
+ * The live band-write descriptor.
44
+ *
45
+ * `mutationImpact: 'disruptive'` because `SetCurrentBands` re-registers the radio and
46
+ * drops the bearer underneath NetworkManager; `readback` is REQUIRED because an
47
+ * accepted-but-ignored band write is the failure mode the catalog's own `readback`
48
+ * proof exists to catch, and it looks like success from the call site alone.
49
+ */
50
+ export declare function buildBandWriteDescriptor(input: BandWriteDescriptorInput): OperationDescriptor<readonly BandName[], ModemBands>;