@ceralive/modem-control 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (480) hide show
  1. package/README.md +341 -0
  2. package/dist/backend/at-lease.d.ts +59 -0
  3. package/dist/backend/at-lease.js +118 -0
  4. package/dist/backend/cell-info.d.ts +46 -0
  5. package/dist/backend/cell-info.js +124 -0
  6. package/dist/backend/constants.d.ts +24 -0
  7. package/{src/backend/constants.ts → dist/backend/constants.js} +4 -9
  8. package/dist/backend/device-classifier.d.ts +46 -0
  9. package/dist/backend/device-classifier.js +258 -0
  10. package/dist/backend/enrichment.d.ts +28 -0
  11. package/dist/backend/enrichment.js +59 -0
  12. package/dist/backend/features.d.ts +61 -0
  13. package/dist/backend/features.js +109 -0
  14. package/dist/backend/identity-ladder.d.ts +57 -0
  15. package/dist/backend/identity-ladder.js +158 -0
  16. package/dist/backend/identity-registry.d.ts +51 -0
  17. package/dist/backend/identity-registry.js +101 -0
  18. package/dist/backend/index.d.ts +30 -0
  19. package/dist/backend/index.js +35 -0
  20. package/dist/backend/lifecycle-interlock.d.ts +25 -0
  21. package/dist/backend/lifecycle-interlock.js +18 -0
  22. package/dist/backend/managed-objects.d.ts +39 -0
  23. package/dist/backend/managed-objects.js +82 -0
  24. package/dist/backend/mapping.d.ts +11 -0
  25. package/dist/backend/mapping.js +148 -0
  26. package/dist/backend/mm-backend.d.ts +44 -0
  27. package/dist/backend/mm-backend.js +154 -0
  28. package/dist/backend/mm-location.d.ts +21 -0
  29. package/dist/backend/mm-location.js +237 -0
  30. package/dist/backend/mm-mutations.d.ts +42 -0
  31. package/dist/backend/mm-mutations.js +259 -0
  32. package/dist/backend/modem-actor.d.ts +41 -0
  33. package/dist/backend/modem-actor.js +78 -0
  34. package/dist/backend/nm-auto-apn.d.ts +54 -0
  35. package/dist/backend/nm-auto-apn.js +124 -0
  36. package/dist/backend/nm-gsm-fields.d.ts +23 -0
  37. package/dist/backend/nm-gsm-fields.js +107 -0
  38. package/dist/backend/nmcli-nm-port.d.ts +28 -0
  39. package/dist/backend/nmcli-nm-port.js +173 -0
  40. package/dist/backend/nmcli-runner.d.ts +24 -0
  41. package/dist/backend/nmcli-runner.js +35 -0
  42. package/dist/backend/observer.d.ts +37 -0
  43. package/dist/backend/observer.js +219 -0
  44. package/dist/backend/power-contract.d.ts +49 -0
  45. package/dist/backend/power-contract.js +34 -0
  46. package/dist/backend/recovery-attribution.d.ts +32 -0
  47. package/dist/backend/recovery-attribution.js +57 -0
  48. package/dist/backend/recovery-budget.d.ts +45 -0
  49. package/dist/backend/recovery-budget.js +44 -0
  50. package/dist/backend/recovery-ladder.d.ts +94 -0
  51. package/dist/backend/recovery-ladder.js +116 -0
  52. package/dist/backend/router-ethernet.d.ts +19 -0
  53. package/dist/backend/router-ethernet.js +66 -0
  54. package/dist/backend/row-store.d.ts +13 -0
  55. package/dist/backend/row-store.js +82 -0
  56. package/dist/backend/signal-setup.d.ts +30 -0
  57. package/dist/backend/signal-setup.js +92 -0
  58. package/dist/backend/sim-unlock.d.ts +13 -0
  59. package/dist/backend/sim-unlock.js +153 -0
  60. package/dist/backend/transition-preconditions.d.ts +101 -0
  61. package/dist/backend/transition-preconditions.js +132 -0
  62. package/dist/backend/usage/accounting.d.ts +39 -0
  63. package/dist/backend/usage/accounting.js +73 -0
  64. package/dist/backend/usage/billing-cycle.d.ts +11 -0
  65. package/dist/backend/usage/billing-cycle.js +39 -0
  66. package/dist/backend/usage/boot-id.d.ts +6 -0
  67. package/{src/backend/usage/boot-id.ts → dist/backend/usage/boot-id.js} +7 -7
  68. package/dist/backend/usage/index.d.ts +8 -0
  69. package/dist/backend/usage/index.js +11 -0
  70. package/dist/backend/usage/policy-store.d.ts +50 -0
  71. package/dist/backend/usage/policy-store.js +161 -0
  72. package/dist/backend/usage/policy-write.d.ts +65 -0
  73. package/dist/backend/usage/policy-write.js +112 -0
  74. package/dist/backend/usage/proc-net-dev.d.ts +18 -0
  75. package/{src/backend/usage/proc-net-dev.ts → dist/backend/usage/proc-net-dev.js} +39 -47
  76. package/dist/backend/usage/sampler.d.ts +75 -0
  77. package/dist/backend/usage/sampler.js +211 -0
  78. package/dist/backend/usage/store.d.ts +38 -0
  79. package/dist/backend/usage/store.js +126 -0
  80. package/dist/backend/usb-device-snapshot.d.ts +31 -0
  81. package/dist/backend/usb-device-snapshot.js +1 -0
  82. package/dist/backend/usb-enumerator.d.ts +21 -0
  83. package/dist/backend/usb-enumerator.js +153 -0
  84. package/dist/backend/usb-mode-transition.d.ts +29 -0
  85. package/dist/backend/usb-mode-transition.js +216 -0
  86. package/dist/band/band-names.d.ts +42 -0
  87. package/dist/band/band-names.js +150 -0
  88. package/dist/band/certification.d.ts +84 -0
  89. package/dist/band/certification.js +127 -0
  90. package/dist/band/certified-bands.json +4 -0
  91. package/dist/band/index.d.ts +2 -0
  92. package/dist/band/index.js +8 -0
  93. package/dist/capability/detect.d.ts +52 -0
  94. package/dist/capability/detect.js +86 -0
  95. package/dist/capability/five-g-preference.d.ts +104 -0
  96. package/dist/capability/five-g-preference.js +171 -0
  97. package/dist/capability/index.d.ts +3 -0
  98. package/dist/capability/index.js +10 -0
  99. package/dist/capability/support-claim.d.ts +39 -0
  100. package/dist/capability/support-claim.js +81 -0
  101. package/dist/domain/brand.d.ts +10 -0
  102. package/dist/domain/brand.js +21 -0
  103. package/dist/domain/errors.d.ts +32 -0
  104. package/dist/domain/errors.js +48 -0
  105. package/dist/domain/generation.d.ts +8 -0
  106. package/dist/domain/generation.js +12 -0
  107. package/dist/domain/guards.d.ts +6 -0
  108. package/dist/domain/guards.js +127 -0
  109. package/dist/domain/identity.d.ts +109 -0
  110. package/dist/domain/identity.js +86 -0
  111. package/dist/domain/index.d.ts +14 -0
  112. package/dist/domain/index.js +18 -0
  113. package/dist/domain/mm-enums.d.ts +12 -0
  114. package/dist/domain/mm-enums.js +139 -0
  115. package/dist/domain/modem-presentation.d.ts +10 -0
  116. package/dist/domain/modem-presentation.js +39 -0
  117. package/dist/domain/observation.d.ts +39 -0
  118. package/dist/domain/observation.js +4 -0
  119. package/dist/domain/operation.d.ts +118 -0
  120. package/dist/domain/operation.js +85 -0
  121. package/dist/domain/physical-identity.d.ts +43 -0
  122. package/dist/domain/physical-identity.js +113 -0
  123. package/{src/domain/policy.ts → dist/domain/policy.d.ts} +34 -69
  124. package/dist/domain/policy.js +38 -0
  125. package/dist/domain/shadow-divergence.d.ts +27 -0
  126. package/dist/domain/shadow-divergence.js +70 -0
  127. package/dist/domain/snapshot.d.ts +53 -0
  128. package/dist/domain/snapshot.js +75 -0
  129. package/{src/domain/state.ts → dist/domain/state.d.ts} +23 -122
  130. package/dist/domain/state.js +38 -0
  131. package/dist/fcc/coverage.d.ts +63 -0
  132. package/dist/fcc/coverage.js +102 -0
  133. package/dist/fcc/index.d.ts +3 -0
  134. package/dist/fcc/index.js +12 -0
  135. package/dist/fcc/policy-store.d.ts +40 -0
  136. package/dist/fcc/policy-store.js +146 -0
  137. package/dist/fcc/policy-write.d.ts +32 -0
  138. package/dist/fcc/policy-write.js +37 -0
  139. package/dist/hardware/hilink-protocol.d.ts +39 -0
  140. package/dist/hardware/hilink-protocol.js +58 -0
  141. package/dist/hardware/index.d.ts +3 -0
  142. package/dist/hardware/index.js +15 -0
  143. package/dist/hardware/router-parsers.d.ts +89 -0
  144. package/dist/hardware/router-parsers.js +234 -0
  145. package/dist/index.d.ts +20 -0
  146. package/dist/index.js +27 -0
  147. package/dist/journal/codec.d.ts +44 -0
  148. package/dist/journal/codec.js +198 -0
  149. package/dist/journal/engine.d.ts +28 -0
  150. package/dist/journal/engine.js +68 -0
  151. package/dist/journal/entry.d.ts +74 -0
  152. package/dist/journal/entry.js +56 -0
  153. package/dist/journal/index.d.ts +6 -0
  154. package/dist/journal/index.js +6 -0
  155. package/dist/journal/legacy-ceraui.d.ts +73 -0
  156. package/dist/journal/legacy-ceraui.js +227 -0
  157. package/dist/journal/recovery.d.ts +58 -0
  158. package/dist/journal/recovery.js +117 -0
  159. package/dist/journal/store.d.ts +55 -0
  160. package/dist/journal/store.js +150 -0
  161. package/dist/location/fix-state.d.ts +53 -0
  162. package/dist/location/fix-state.js +75 -0
  163. package/dist/location/index.d.ts +2 -0
  164. package/dist/location/index.js +8 -0
  165. package/dist/location/nmea.d.ts +10 -0
  166. package/dist/location/nmea.js +89 -0
  167. package/dist/observations/envelope.d.ts +63 -0
  168. package/dist/observations/envelope.js +77 -0
  169. package/dist/observations/freshness.d.ts +28 -0
  170. package/dist/observations/freshness.js +76 -0
  171. package/dist/observations/index.d.ts +13 -0
  172. package/dist/observations/index.js +24 -0
  173. package/dist/observations/metric.d.ts +61 -0
  174. package/dist/observations/metric.js +73 -0
  175. package/dist/observations/model.d.ts +80 -0
  176. package/dist/observations/model.js +11 -0
  177. package/dist/observations/provenance.d.ts +94 -0
  178. package/dist/observations/provenance.js +67 -0
  179. package/dist/observations/raw.d.ts +42 -0
  180. package/dist/observations/raw.js +146 -0
  181. package/dist/observations/reading.d.ts +51 -0
  182. package/dist/observations/reading.js +65 -0
  183. package/dist/observations/sources/hilink.d.ts +10 -0
  184. package/dist/observations/sources/hilink.js +84 -0
  185. package/dist/observations/sources/modemmanager.d.ts +18 -0
  186. package/dist/observations/sources/modemmanager.js +239 -0
  187. package/dist/observations/sources/router-shared.d.ts +29 -0
  188. package/dist/observations/sources/router-shared.js +68 -0
  189. package/dist/observations/sources/ufi.d.ts +10 -0
  190. package/dist/observations/sources/ufi.js +111 -0
  191. package/dist/observations/sources/zte.d.ts +7 -0
  192. package/dist/observations/sources/zte.js +71 -0
  193. package/dist/observations/state-separation.d.ts +64 -0
  194. package/dist/observations/state-separation.js +52 -0
  195. package/dist/operation-ids.d.ts +2 -0
  196. package/dist/operation-ids.js +26 -0
  197. package/dist/operations/contracts.d.ts +72 -0
  198. package/dist/operations/contracts.js +1 -0
  199. package/dist/operations/index.d.ts +1 -0
  200. package/dist/operations/index.js +1 -0
  201. package/dist/operations/operation-engine.d.ts +16 -0
  202. package/dist/operations/operation-engine.js +194 -0
  203. package/dist/ports/index.d.ts +12 -0
  204. package/{src/ports/index.ts → dist/ports/index.js} +12 -8
  205. package/dist/ports/location.d.ts +87 -0
  206. package/dist/ports/location.js +36 -0
  207. package/dist/ports/modem-manager.d.ts +89 -0
  208. package/dist/ports/modem-manager.js +9 -0
  209. package/dist/ports/mutation-admission.d.ts +27 -0
  210. package/dist/ports/mutation-admission.js +9 -0
  211. package/dist/ports/network-manager.d.ts +68 -0
  212. package/dist/ports/network-manager.js +13 -0
  213. package/{src/ports/observation.ts → dist/ports/observation.d.ts} +16 -27
  214. package/dist/ports/observation.js +7 -0
  215. package/dist/ports/ops.d.ts +44 -0
  216. package/dist/ports/ops.js +16 -0
  217. package/{src/ports/receipts.ts → dist/ports/receipts.d.ts} +5 -27
  218. package/dist/ports/receipts.js +9 -0
  219. package/dist/ports/reconcile.d.ts +33 -0
  220. package/dist/ports/reconcile.js +200 -0
  221. package/dist/ports/resource-ownership.d.ts +29 -0
  222. package/dist/ports/resource-ownership.js +1 -0
  223. package/dist/ports/router.d.ts +19 -0
  224. package/dist/ports/router.js +7 -0
  225. package/dist/ports/sms.d.ts +64 -0
  226. package/dist/ports/sms.js +24 -0
  227. package/dist/ports/uhubctl.d.ts +6 -0
  228. package/dist/ports/uhubctl.js +1 -0
  229. package/dist/providers/contracts.d.ts +124 -0
  230. package/dist/providers/contracts.js +10 -0
  231. package/dist/providers/huawei-hilink/index.d.ts +2 -0
  232. package/dist/providers/huawei-hilink/index.js +2 -0
  233. package/dist/providers/huawei-hilink/operations.d.ts +20 -0
  234. package/dist/providers/huawei-hilink/operations.js +56 -0
  235. package/dist/providers/huawei-hilink/provider.d.ts +52 -0
  236. package/dist/providers/huawei-hilink/provider.js +76 -0
  237. package/dist/providers/huawei-hilink/runtime.d.ts +22 -0
  238. package/dist/providers/huawei-hilink/runtime.js +171 -0
  239. package/dist/providers/huawei-hilink/session.d.ts +28 -0
  240. package/dist/providers/huawei-hilink/session.js +120 -0
  241. package/dist/providers/huawei-hilink/transport.d.ts +19 -0
  242. package/dist/providers/huawei-hilink/transport.js +1 -0
  243. package/dist/providers/index.d.ts +8 -0
  244. package/dist/providers/index.js +8 -0
  245. package/dist/providers/matcher.d.ts +3 -0
  246. package/dist/providers/matcher.js +205 -0
  247. package/dist/providers/modem-manager/errors.d.ts +7 -0
  248. package/dist/providers/modem-manager/errors.js +37 -0
  249. package/dist/providers/modem-manager/generic-operations.d.ts +10 -0
  250. package/dist/providers/modem-manager/generic-operations.js +209 -0
  251. package/dist/providers/modem-manager/index.d.ts +4 -0
  252. package/dist/providers/modem-manager/index.js +4 -0
  253. package/dist/providers/modem-manager/module-operations.d.ts +21 -0
  254. package/dist/providers/modem-manager/module-operations.js +118 -0
  255. package/dist/providers/modem-manager/provider.d.ts +41 -0
  256. package/dist/providers/modem-manager/provider.js +155 -0
  257. package/dist/providers/modem-manager/runtime-composition-operation.d.ts +32 -0
  258. package/dist/providers/modem-manager/runtime-composition-operation.js +151 -0
  259. package/dist/providers/modem-manager/snapshot.d.ts +5 -0
  260. package/dist/providers/modem-manager/snapshot.js +204 -0
  261. package/dist/providers/modem-manager/types.d.ts +137 -0
  262. package/dist/providers/modem-manager/types.js +1 -0
  263. package/dist/providers/network-manager/adapter.d.ts +71 -0
  264. package/dist/providers/network-manager/adapter.js +348 -0
  265. package/dist/providers/network-manager/index.d.ts +2 -0
  266. package/dist/providers/network-manager/index.js +2 -0
  267. package/dist/providers/network-manager/types.d.ts +171 -0
  268. package/dist/providers/network-manager/types.js +77 -0
  269. package/dist/providers/registry.d.ts +13 -0
  270. package/dist/providers/registry.js +33 -0
  271. package/dist/providers/ufi-himi/index.d.ts +6 -0
  272. package/dist/providers/ufi-himi/index.js +6 -0
  273. package/dist/providers/ufi-himi/operations.d.ts +41 -0
  274. package/dist/providers/ufi-himi/operations.js +66 -0
  275. package/dist/providers/ufi-himi/prohibitions.d.ts +62 -0
  276. package/dist/providers/ufi-himi/prohibitions.js +88 -0
  277. package/dist/providers/ufi-himi/provider.d.ts +41 -0
  278. package/dist/providers/ufi-himi/provider.js +204 -0
  279. package/dist/providers/ufi-himi/qualcomm-evidence.d.ts +32 -0
  280. package/dist/providers/ufi-himi/qualcomm-evidence.js +51 -0
  281. package/dist/providers/ufi-himi/session.d.ts +46 -0
  282. package/dist/providers/ufi-himi/session.js +92 -0
  283. package/dist/providers/ufi-himi/transport.d.ts +29 -0
  284. package/dist/providers/ufi-himi/transport.js +25 -0
  285. package/dist/providers/zte-goform/index.d.ts +2 -0
  286. package/dist/providers/zte-goform/index.js +2 -0
  287. package/dist/providers/zte-goform/provider.d.ts +56 -0
  288. package/dist/providers/zte-goform/provider.js +101 -0
  289. package/dist/providers/zte-goform/session.d.ts +23 -0
  290. package/dist/providers/zte-goform/session.js +197 -0
  291. package/dist/providers/zte-goform/transport.d.ts +16 -0
  292. package/dist/providers/zte-goform/transport.js +1 -0
  293. package/dist/radio/band-truth.d.ts +50 -0
  294. package/dist/radio/band-truth.js +92 -0
  295. package/dist/radio/index.d.ts +3 -0
  296. package/dist/radio/index.js +10 -0
  297. package/dist/radio/mode-combinations.d.ts +88 -0
  298. package/dist/radio/mode-combinations.js +198 -0
  299. package/dist/radio/mode-truth.d.ts +67 -0
  300. package/dist/radio/mode-truth.js +112 -0
  301. package/dist/redact.d.ts +15 -0
  302. package/dist/redact.js +189 -0
  303. package/dist/safety/composition-root.d.ts +28 -0
  304. package/dist/safety/composition-root.js +65 -0
  305. package/dist/safety/flock-resource-ownership.d.ts +11 -0
  306. package/dist/safety/flock-resource-ownership.js +138 -0
  307. package/dist/safety/index.d.ts +2 -0
  308. package/dist/safety/index.js +2 -0
  309. package/dist/sms/dbus-messaging.d.ts +17 -0
  310. package/dist/sms/dbus-messaging.js +185 -0
  311. package/dist/sms/inbox-store.d.ts +10 -0
  312. package/dist/sms/inbox-store.js +82 -0
  313. package/dist/sms/index.d.ts +4 -0
  314. package/dist/sms/index.js +10 -0
  315. package/dist/sms/mmcli-parse.d.ts +54 -0
  316. package/dist/sms/mmcli-parse.js +224 -0
  317. package/dist/sms/normalize.d.ts +42 -0
  318. package/dist/sms/normalize.js +95 -0
  319. package/dist/testing/domain-fakes.d.ts +58 -0
  320. package/dist/testing/domain-fakes.js +98 -0
  321. package/dist/testing/index.d.ts +2 -0
  322. package/dist/testing/index.js +17 -0
  323. package/dist/testing/provider-fakes.d.ts +45 -0
  324. package/dist/testing/provider-fakes.js +64 -0
  325. package/dist/transport/calls.d.ts +9 -0
  326. package/dist/transport/calls.js +88 -0
  327. package/dist/transport/codec.d.ts +3 -0
  328. package/dist/transport/codec.js +207 -0
  329. package/dist/transport/dbus-native.d.ts +57 -0
  330. package/dist/transport/dbus-native.js +17 -0
  331. package/dist/transport/errors.d.ts +21 -0
  332. package/{src/transport/errors.ts → dist/transport/errors.js} +34 -46
  333. package/dist/transport/index.d.ts +4 -0
  334. package/dist/transport/index.js +9 -0
  335. package/dist/transport/signals.d.ts +15 -0
  336. package/dist/transport/signals.js +123 -0
  337. package/dist/transport/signature.d.ts +7 -0
  338. package/dist/transport/signature.js +94 -0
  339. package/dist/transport/transport.d.ts +2 -0
  340. package/dist/transport/transport.js +202 -0
  341. package/dist/transport/types.d.ts +61 -0
  342. package/dist/transport/types.js +19 -0
  343. package/dist/usb-mode/catalog-schema.d.ts +139 -0
  344. package/dist/usb-mode/catalog-schema.js +97 -0
  345. package/dist/usb-mode/catalog.d.ts +21 -0
  346. package/{src/usb-mode/catalog.ts → dist/usb-mode/catalog.js} +10 -32
  347. package/dist/usb-mode/certified-catalog.json +67 -0
  348. package/dist/usb-mode/index.d.ts +6 -0
  349. package/dist/usb-mode/index.js +16 -0
  350. package/dist/usb-mode/ingestion.d.ts +111 -0
  351. package/dist/usb-mode/ingestion.js +187 -0
  352. package/dist/usb-mode/promotion-review.d.ts +21 -0
  353. package/dist/usb-mode/promotion-review.js +87 -0
  354. package/dist/usb-mode/runtime-capability.d.ts +59 -0
  355. package/dist/usb-mode/runtime-capability.js +157 -0
  356. package/dist/usb-mode/usb-devices-parse.d.ts +36 -0
  357. package/dist/usb-mode/usb-devices-parse.js +157 -0
  358. package/dist/ussd/calls.d.ts +32 -0
  359. package/dist/ussd/calls.js +96 -0
  360. package/dist/ussd/index.d.ts +5 -0
  361. package/dist/ussd/index.js +12 -0
  362. package/dist/ussd/mm-ussd.d.ts +37 -0
  363. package/dist/ussd/mm-ussd.js +205 -0
  364. package/dist/ussd/refusal.d.ts +53 -0
  365. package/dist/ussd/refusal.js +154 -0
  366. package/dist/ussd/registration.d.ts +20 -0
  367. package/dist/ussd/registration.js +101 -0
  368. package/dist/ussd/session.d.ts +106 -0
  369. package/dist/ussd/session.js +163 -0
  370. package/package.json +38 -4
  371. package/src/backend/at-lease.test.ts +0 -106
  372. package/src/backend/at-lease.ts +0 -158
  373. package/src/backend/cell-info.test.ts +0 -154
  374. package/src/backend/cell-info.ts +0 -160
  375. package/src/backend/device-classifier.test.ts +0 -168
  376. package/src/backend/device-classifier.ts +0 -248
  377. package/src/backend/enrichment.ts +0 -96
  378. package/src/backend/features.test.ts +0 -162
  379. package/src/backend/features.ts +0 -179
  380. package/src/backend/identity-ladder.test.ts +0 -117
  381. package/src/backend/identity-ladder.ts +0 -221
  382. package/src/backend/identity-registry.test.ts +0 -89
  383. package/src/backend/identity-registry.ts +0 -151
  384. package/src/backend/index.ts +0 -236
  385. package/src/backend/lifecycle-interlock.ts +0 -38
  386. package/src/backend/managed-objects.ts +0 -108
  387. package/src/backend/mapping.ts +0 -160
  388. package/src/backend/mm-backend.ts +0 -191
  389. package/src/backend/mm-mutations.ts +0 -228
  390. package/src/backend/modem-actor.test.ts +0 -95
  391. package/src/backend/modem-actor.ts +0 -112
  392. package/src/backend/nm-auto-apn.ts +0 -161
  393. package/src/backend/nm-gsm-fields.ts +0 -122
  394. package/src/backend/nmcli-nm-port.ts +0 -228
  395. package/src/backend/nmcli-runner.ts +0 -52
  396. package/src/backend/observer.ts +0 -297
  397. package/src/backend/power-contract.test.ts +0 -40
  398. package/src/backend/power-contract.ts +0 -83
  399. package/src/backend/recovery-attribution.test.ts +0 -102
  400. package/src/backend/recovery-attribution.ts +0 -86
  401. package/src/backend/recovery-budget.test.ts +0 -64
  402. package/src/backend/recovery-budget.ts +0 -84
  403. package/src/backend/recovery-ladder.test.ts +0 -257
  404. package/src/backend/recovery-ladder.ts +0 -249
  405. package/src/backend/router-ethernet.test.ts +0 -71
  406. package/src/backend/router-ethernet.ts +0 -90
  407. package/src/backend/row-store.ts +0 -105
  408. package/src/backend/signal-setup.ts +0 -112
  409. package/src/backend/sim-unlock.ts +0 -193
  410. package/src/backend/transition-preconditions.ts +0 -149
  411. package/src/backend/uhubctl-power-hook.test.ts +0 -274
  412. package/src/backend/uhubctl-power-hook.ts +0 -377
  413. package/src/backend/usage/accounting.test.ts +0 -147
  414. package/src/backend/usage/accounting.ts +0 -123
  415. package/src/backend/usage/billing-cycle.test.ts +0 -62
  416. package/src/backend/usage/billing-cycle.ts +0 -45
  417. package/src/backend/usage/index.ts +0 -60
  418. package/src/backend/usage/policy-store.test.ts +0 -164
  419. package/src/backend/usage/policy-store.ts +0 -216
  420. package/src/backend/usage/policy-write.test.ts +0 -198
  421. package/src/backend/usage/policy-write.ts +0 -207
  422. package/src/backend/usage/proc-net-dev.test.ts +0 -56
  423. package/src/backend/usage/sampler.test.ts +0 -327
  424. package/src/backend/usage/sampler.ts +0 -282
  425. package/src/backend/usage/store.test.ts +0 -148
  426. package/src/backend/usage/store.ts +0 -177
  427. package/src/backend/usb-enumerator.test.ts +0 -87
  428. package/src/backend/usb-enumerator.ts +0 -181
  429. package/src/backend/usb-mode-transition.test.ts +0 -323
  430. package/src/backend/usb-mode-transition.ts +0 -253
  431. package/src/domain/brand.ts +0 -29
  432. package/src/domain/errors.ts +0 -77
  433. package/src/domain/guards.test.ts +0 -218
  434. package/src/domain/guards.ts +0 -144
  435. package/src/domain/identity.test.ts +0 -83
  436. package/src/domain/identity.ts +0 -165
  437. package/src/domain/index.ts +0 -12
  438. package/src/domain/snapshot.test.ts +0 -266
  439. package/src/domain/snapshot.ts +0 -120
  440. package/src/index.test.ts +0 -6
  441. package/src/index.ts +0 -15
  442. package/src/ports/README.md +0 -61
  443. package/src/ports/forbidden-surface.test.ts +0 -241
  444. package/src/ports/modem-manager.ts +0 -72
  445. package/src/ports/network-manager.ts +0 -87
  446. package/src/ports/ops.ts +0 -60
  447. package/src/ports/ops.type-test.ts +0 -39
  448. package/src/ports/receipts.test.ts +0 -153
  449. package/src/ports/reconcile.test.ts +0 -152
  450. package/src/ports/reconcile.ts +0 -338
  451. package/src/ports/router.ts +0 -29
  452. package/src/redact.test.ts +0 -82
  453. package/src/redact.ts +0 -73
  454. package/src/transport/README.md +0 -65
  455. package/src/transport/calls.ts +0 -113
  456. package/src/transport/characterization.test.ts +0 -260
  457. package/src/transport/codec.test.ts +0 -118
  458. package/src/transport/codec.ts +0 -240
  459. package/src/transport/conformance-python.test.ts +0 -152
  460. package/src/transport/conformance-same-lib.test.ts +0 -115
  461. package/src/transport/dbus-native-lib.d.ts +0 -19
  462. package/src/transport/dbus-native.ts +0 -85
  463. package/src/transport/index.ts +0 -30
  464. package/src/transport/no-library-leak.test.ts +0 -61
  465. package/src/transport/reliability.test.ts +0 -173
  466. package/src/transport/signals.ts +0 -150
  467. package/src/transport/signature.ts +0 -110
  468. package/src/transport/test-support/fake-service.ts +0 -168
  469. package/src/transport/test-support/independent-producer.py +0 -110
  470. package/src/transport/test-support/private-bus.ts +0 -66
  471. package/src/transport/transport.ts +0 -250
  472. package/src/transport/types.ts +0 -118
  473. package/src/usb-mode/catalog-schema.test.ts +0 -181
  474. package/src/usb-mode/catalog-schema.ts +0 -113
  475. package/src/usb-mode/certified-catalog.json +0 -67
  476. package/src/usb-mode/index.ts +0 -58
  477. package/src/usb-mode/ingestion.test.ts +0 -268
  478. package/src/usb-mode/ingestion.ts +0 -297
  479. package/src/usb-mode/promotion-review.ts +0 -117
  480. package/src/usb-mode/usb-devices-parse.ts +0 -196
@@ -0,0 +1,204 @@
1
+ // Qualcomm UFI / HIMI — a READ-ONLY provider, by construction rather than by policy.
2
+ //
3
+ // This provider normalizes the telemetry the pure parsers already own
4
+ // (`hardware/router-parsers.ts` → `observations/sources/ufi.ts`, migrated in todo 17).
5
+ // It adds a session and a transport around them and nothing else: there is no write
6
+ // descriptor, no write command in the transport vocabulary, and no operation id outside
7
+ // the closed read set that resolves to anything but a typed refusal.
8
+ //
9
+ // The Qualcomm safety model it encodes lives in two neighbouring modules —
10
+ // `qualcomm-evidence.ts` (what a USB id proves) and `prohibitions.ts` (what may never be
11
+ // attempted). Both are inert data; neither can act.
12
+ import { epochMillis, sourceEpoch, stableKeyFromPhysicalModemId } from '../../domain/index.js';
13
+ import { parseUfiDetails, parseUfiSignal } from '../../hardware/router-parsers.js';
14
+ import { normalizeUfiObservation } from '../../observations/sources/ufi.js';
15
+ import { planUfiOperation, UFI_PROFILE, ufiReadDescriptor, ufiReadResult, ufiRefusedResult, } from './operations.js';
16
+ import { UFI_PROHIBITED_OPERATION_IDS } from './prohibitions.js';
17
+ import { UFI_DIAG_PRODUCTION_ACCESS, UFI_MATCHED_USB_IDS } from './qualcomm-evidence.js';
18
+ import { parseUfiReply, UFI_SESSION_REFUSAL, UfiSessionRuntime } from './session.js';
19
+ import { UFI_API_PATH } from './transport.js';
20
+ export class UfiHimiProvider extends UfiSessionRuntime {
21
+ async fingerprint(request) {
22
+ // The HIMI fingerprint needs a SESSION to read anything, so this probe is the one
23
+ // place a "harmless unauthenticated probe" would spend the provider's single
24
+ // bounded login. On a device that enumerated a USB id and it is not a HIMI id,
25
+ // that login would be aimed at somebody else's dongle — a credential attempt
26
+ // against hardware this provider has positive evidence it does not own. An
27
+ // ABSENT usb fact is still probed (nobody enumerated one; that is not a denial).
28
+ if (!this.usbEvidencePermitsProbe(request)) {
29
+ return {
30
+ signal: 'unknown',
31
+ strength: 'strong',
32
+ profiles: [],
33
+ detail: 'usb-id-is-not-himi',
34
+ };
35
+ }
36
+ const bodies = await this.fetch(request, ['getallstatus']);
37
+ const reply = parseUfiReply(bodies?.getallstatus ?? '');
38
+ const matched = reply?.reply === 'ok' && reply.params !== undefined;
39
+ return {
40
+ signal: matched ? 'match' : 'unknown',
41
+ strength: 'strong',
42
+ profiles: matched ? [UFI_PROFILE] : [],
43
+ detail: matched ? 'himi-json-shape' : 'himi-json-not-proven',
44
+ };
45
+ }
46
+ capability() {
47
+ return {
48
+ id: 'ufi-himi-telemetry',
49
+ read: async (context) => {
50
+ const bodies = await this.fetch(context, ['getsysinfo']);
51
+ const reply = parseUfiReply(bodies?.getsysinfo ?? '');
52
+ const supported = reply?.reply === 'ok' && reply.params !== undefined;
53
+ return {
54
+ signal: supported ? 'match' : 'mismatch',
55
+ strength: 'strong',
56
+ detail: supported ? 'telemetry-reported' : 'telemetry-refused',
57
+ };
58
+ },
59
+ };
60
+ }
61
+ async observe(context) {
62
+ const bodies = await this.fetch(context, [
63
+ 'getsysinfo',
64
+ 'getoverview',
65
+ 'getallstatus',
66
+ 'getproduceinfo',
67
+ ]);
68
+ if (bodies === undefined)
69
+ return [];
70
+ const produceInfo = bodies.getproduceinfo;
71
+ return [
72
+ normalizeUfiObservation({
73
+ sysinfo: bodies.getsysinfo ?? '',
74
+ overview: bodies.getoverview ?? '',
75
+ status: bodies.getallstatus ?? '',
76
+ ...(produceInfo === undefined ? {} : { produceInfo }),
77
+ }, {
78
+ stableKey: stableKeyFromPhysicalModemId(context.physicalModemId),
79
+ generation: context.generation,
80
+ sourceEpoch: sourceEpoch(1),
81
+ observedAt: epochMillis(this.observedAt()),
82
+ }),
83
+ ];
84
+ }
85
+ operations(_profile) {
86
+ return {
87
+ access: 'read-only',
88
+ reads: [
89
+ {
90
+ descriptor: ufiReadDescriptor('ufi.signal.read'),
91
+ read: (context) => this.readSignal(context),
92
+ },
93
+ {
94
+ descriptor: ufiReadDescriptor('ufi.details.read'),
95
+ read: (context) => this.readDetails(context),
96
+ },
97
+ ],
98
+ prohibited: UFI_PROHIBITED_OPERATION_IDS,
99
+ diagAccess: UFI_DIAG_PRODUCTION_ACCESS,
100
+ plan: planUfiOperation,
101
+ };
102
+ }
103
+ async readSignal(context) {
104
+ const bodies = await this.fetch(context, ['getsysinfo', 'getoverview', 'getallstatus']);
105
+ if (bodies === undefined)
106
+ return ufiRefusedResult(context.generation, 'session-unavailable');
107
+ const value = parseUfiSignal({
108
+ sysinfo: bodies.getsysinfo ?? '',
109
+ overview: bodies.getoverview ?? '',
110
+ status: bodies.getallstatus ?? '',
111
+ });
112
+ return ufiReadResult(context.generation, value);
113
+ }
114
+ async readDetails(context) {
115
+ const bodies = await this.fetch(context, ['getoverview', 'getsysinfo', 'getproduceinfo']);
116
+ if (bodies === undefined)
117
+ return ufiRefusedResult(context.generation, 'session-unavailable');
118
+ const details = parseUfiDetails({
119
+ overview: bodies.getoverview ?? '',
120
+ sysinfo: bodies.getsysinfo ?? '',
121
+ produceInfo: bodies.getproduceinfo ?? '',
122
+ });
123
+ return details === undefined
124
+ ? ufiRefusedResult(context.generation, 'not-reported')
125
+ : ufiReadResult(context.generation, details);
126
+ }
127
+ usbEvidencePermitsProbe(request) {
128
+ const enumerated = request.passiveFacts.filter((fact) => fact.kind === 'usb');
129
+ return (enumerated.length === 0 ||
130
+ enumerated.some((fact) => UFI_MATCHED_USB_IDS.some((id) => id === fact.value)));
131
+ }
132
+ /**
133
+ * Reads are issued sequentially on ONE session. A `SessionOut` in any body drops the
134
+ * cached session so the NEXT generation may spend its own single login; it never
135
+ * re-authenticates inside this cycle.
136
+ */
137
+ async fetch(request, commands) {
138
+ const session = await this.session(request);
139
+ if (session === undefined)
140
+ return undefined;
141
+ const bodies = {};
142
+ for (const command of commands) {
143
+ const response = await this.read(command, session);
144
+ bodies[command] = response.status === 200 ? response.body : '';
145
+ }
146
+ const refused = Object.values(bodies).some((body) => parseUfiReply(body)?.reply === UFI_SESSION_REFUSAL);
147
+ if (refused)
148
+ this.forgetSession(request);
149
+ return bodies;
150
+ }
151
+ }
152
+ export function createUfiHimiDefinition(options) {
153
+ const runtime = new UfiHimiProvider(options);
154
+ return {
155
+ id: 'ufi-himi',
156
+ profileVersion: '1',
157
+ eligibleTransports: ['network'],
158
+ passiveMatchers: [
159
+ {
160
+ // ONE matcher over both ids. `9024` is the RNDIS+ADB composition and `9091` is
161
+ // a firmware-chosen product id; both identify a HIMI stick, and NEITHER is
162
+ // evidence of a DIAG channel (see qualcomm-evidence.ts).
163
+ id: 'usb-qualcomm-himi',
164
+ fact: 'usb',
165
+ expected: UFI_MATCHED_USB_IDS,
166
+ profiles: [UFI_PROFILE],
167
+ strength: 'strong',
168
+ required: true,
169
+ },
170
+ ],
171
+ unauthenticatedProbes: [
172
+ { id: 'ufi-himi-shape', run: (request) => runtime.fingerprint(request) },
173
+ ],
174
+ capabilityReaders: [runtime.capability()],
175
+ observe: (context) => runtime.observe(context),
176
+ operations: (profile) => runtime.operations(profile),
177
+ contractFixtures: [
178
+ {
179
+ profile: UFI_PROFILE,
180
+ request: {
181
+ method: 'POST',
182
+ path: UFI_API_PATH,
183
+ cmdid: 'login',
184
+ credentials: '[redacted]',
185
+ interfaceBound: true,
186
+ redirects: 'disabled',
187
+ },
188
+ response: { reply: 'ok', session: '[redacted]' },
189
+ },
190
+ {
191
+ profile: UFI_PROFILE,
192
+ request: {
193
+ method: 'POST',
194
+ path: UFI_API_PATH,
195
+ cmdid: 'getsysinfo',
196
+ authorization: '[redacted]',
197
+ interfaceBound: true,
198
+ redirects: 'disabled',
199
+ },
200
+ response: { reply: 'ok', params: '[verbatim-diagnostics]' },
201
+ },
202
+ ],
203
+ };
204
+ }
@@ -0,0 +1,32 @@
1
+ export declare const UFI_RNDIS_ADB_USB_ID = "05c6:9024";
2
+ export declare const UFI_FIRMWARE_SPECIFIC_USB_ID = "05c6:9091";
3
+ export type UfiUsbClaim = 'rndis-network' | 'adb-interface';
4
+ export declare const UFI_USB_EVIDENCE: {
5
+ readonly "05c6:9024": readonly ["rndis-network", "adb-interface"];
6
+ /** Deliberately empty: a firmware-chosen product id claims nothing by itself. */
7
+ readonly "05c6:9091": readonly [];
8
+ };
9
+ export declare const UFI_MATCHED_USB_IDS: readonly ["05c6:9024", "05c6:9091"];
10
+ export declare function ufiUsbClaims(usbId: string): readonly UfiUsbClaim[];
11
+ export type UfiUsbInterfaceDescriptor = {
12
+ readonly number: number;
13
+ readonly interfaceClass: number;
14
+ readonly interfaceSubClass: number;
15
+ readonly interfaceProtocol: number;
16
+ };
17
+ export type UfiDiagEvidence = {
18
+ readonly state: 'descriptor-confirmed';
19
+ readonly interfaceNumber: number;
20
+ } | {
21
+ readonly state: 'not-proven';
22
+ readonly reason: 'product-id-is-not-evidence' | 'no-diag-interface-descriptor';
23
+ };
24
+ export declare function classifyUfiDiagEvidence(input: {
25
+ readonly usbId: string;
26
+ readonly interfaces: readonly UfiUsbInterfaceDescriptor[];
27
+ }): UfiDiagEvidence;
28
+ /**
29
+ * Unconditional. A descriptor-confirmed DIAG channel raises what a SUPERVISED BENCH
30
+ * operator may attempt by hand; it never raises what this package may do on its own.
31
+ */
32
+ export declare const UFI_DIAG_PRODUCTION_ACCESS: 'prohibited';
@@ -0,0 +1,51 @@
1
+ // What a Qualcomm USB id does and does not prove.
2
+ //
3
+ // `05c6:9024` is the composition the bench UFI sticks enumerate in: RNDIS networking
4
+ // plus an ADB interface. That is EVIDENCE OF A COMPOSITION, not a permission — the ADB
5
+ // interface being present is exactly why production must be explicit that it never uses
6
+ // it (see `shell.transport-fallback` in prohibitions.ts).
7
+ //
8
+ // `05c6:9091` is a FIRMWARE-SPECIFIC product id. It is routinely read as "DIAG is
9
+ // available", and that reading is wrong: `05c6` is Qualcomm's generic vendor id and the
10
+ // product id is chosen by whoever built the firmware image, so it says nothing about
11
+ // which interfaces the device actually exposes. Treating it as proof of DIAG is how a
12
+ // tool ends up opening a channel that is not there, or worse, one that is there and
13
+ // belongs to something else.
14
+ //
15
+ // The only thing that proves a DIAG channel is a DIAG INTERFACE DESCRIPTOR. And even a
16
+ // confirmed one buys nothing in production: `UFI_DIAG_PRODUCTION_ACCESS` is `prohibited`
17
+ // unconditionally, and the supervised read-only info probe is a bench runbook
18
+ // (docs/UFI-DIAG-PROBE.md), never an operation this package can perform.
19
+ export const UFI_RNDIS_ADB_USB_ID = '05c6:9024';
20
+ export const UFI_FIRMWARE_SPECIFIC_USB_ID = '05c6:9091';
21
+ export const UFI_USB_EVIDENCE = {
22
+ [UFI_RNDIS_ADB_USB_ID]: ['rndis-network', 'adb-interface'],
23
+ /** Deliberately empty: a firmware-chosen product id claims nothing by itself. */
24
+ [UFI_FIRMWARE_SPECIFIC_USB_ID]: [],
25
+ };
26
+ export const UFI_MATCHED_USB_IDS = [UFI_RNDIS_ADB_USB_ID, UFI_FIRMWARE_SPECIFIC_USB_ID];
27
+ export function ufiUsbClaims(usbId) {
28
+ return Object.hasOwn(UFI_USB_EVIDENCE, usbId)
29
+ ? UFI_USB_EVIDENCE[usbId]
30
+ : [];
31
+ }
32
+ /** Qualcomm's DIAG interface: vendor-specific class/subclass with protocol 0x30. */
33
+ const DIAG_INTERFACE_CLASS = 0xff;
34
+ const DIAG_INTERFACE_SUBCLASS = 0xff;
35
+ const DIAG_INTERFACE_PROTOCOL = 0x30;
36
+ export function classifyUfiDiagEvidence(input) {
37
+ const diag = input.interfaces.find((descriptor) => descriptor.interfaceClass === DIAG_INTERFACE_CLASS &&
38
+ descriptor.interfaceSubClass === DIAG_INTERFACE_SUBCLASS &&
39
+ descriptor.interfaceProtocol === DIAG_INTERFACE_PROTOCOL);
40
+ if (diag !== undefined) {
41
+ return { state: 'descriptor-confirmed', interfaceNumber: diag.number };
42
+ }
43
+ return input.interfaces.length === 0
44
+ ? { state: 'not-proven', reason: 'product-id-is-not-evidence' }
45
+ : { state: 'not-proven', reason: 'no-diag-interface-descriptor' };
46
+ }
47
+ /**
48
+ * Unconditional. A descriptor-confirmed DIAG channel raises what a SUPERVISED BENCH
49
+ * operator may attempt by hand; it never raises what this package may do on its own.
50
+ */
51
+ export const UFI_DIAG_PRODUCTION_ACCESS = 'prohibited';
@@ -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,56 @@
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 declare const ZTE_EVIDENCE_CMD = "LD,psw_fail_num_str,login_lock_time,wa_inner_version,cr_version";
11
+ export type ZteProfile = {
12
+ readonly id: 'mf79u-legacy' | 'mf79u-ld-salted' | 'mf266-salted';
13
+ readonly firmware: 'MF79U' | 'MF266';
14
+ readonly algorithm: 'legacy-base64' | 'login-salted-sha256' | 'multi-user-salted-sha256';
15
+ };
16
+ export declare const ZTE_PROFILES: readonly [{
17
+ readonly id: 'mf79u-legacy';
18
+ readonly firmware: 'MF79U';
19
+ readonly algorithm: 'legacy-base64';
20
+ }, {
21
+ readonly id: 'mf79u-ld-salted';
22
+ readonly firmware: 'MF79U';
23
+ readonly algorithm: 'login-salted-sha256';
24
+ }, {
25
+ readonly id: 'mf266-salted';
26
+ readonly firmware: 'MF266';
27
+ readonly algorithm: 'multi-user-salted-sha256';
28
+ }];
29
+ export declare const ZTE_UNKNOWN_PROFILE = "zte-unknown-read-only";
30
+ export type ZteCredentials = {
31
+ readonly username: string;
32
+ readonly password: string;
33
+ };
34
+ export type ZteOptions = {
35
+ readonly interfaceName: string;
36
+ readonly adminUrl: string;
37
+ readonly transport: ZteTransport;
38
+ readonly credentials: ZteCredentials;
39
+ readonly now?: () => number;
40
+ };
41
+ type ZteOperations = ProviderOperationsSurface;
42
+ export declare function zteProfilesForFirmware(firmware: string | undefined): readonly ZteProfile[];
43
+ export declare function zteProfileById(profileId: string): ZteProfile | undefined;
44
+ export declare class ZteGoformProvider extends ZteSessionRuntime {
45
+ fingerprint(request: ProviderMatchRequest): Promise<{
46
+ signal: "match" | "unknown";
47
+ strength: 'strong';
48
+ profiles: string[];
49
+ detail: string;
50
+ }>;
51
+ capability(): CapabilityReader;
52
+ observe(context: ProviderExecutionContext): Promise<readonly ObservationEnvelope<NormalizedModemObservation>[]>;
53
+ operations(_profile: string): ZteOperations;
54
+ }
55
+ export declare function createZteGoformDefinition(options: ZteOptions): ProviderDefinition<NormalizedModemObservation, ZteOperations>;
56
+ 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_EVIDENCE_CMD = 'LD,psw_fail_num_str,login_lock_time,wa_inner_version,cr_version';
8
+ export const ZTE_PROFILES = [
9
+ { id: 'mf79u-legacy', firmware: 'MF79U', algorithm: 'legacy-base64' },
10
+ { id: 'mf79u-ld-salted', firmware: 'MF79U', algorithm: 'login-salted-sha256' },
11
+ { id: 'mf266-salted', firmware: 'MF266', algorithm: 'multi-user-salted-sha256' },
12
+ ];
13
+ export const ZTE_UNKNOWN_PROFILE = 'zte-unknown-read-only';
14
+ export function zteProfilesForFirmware(firmware) {
15
+ return ZTE_PROFILES.filter((profile) => profile.firmware === firmware);
16
+ }
17
+ export function zteProfileById(profileId) {
18
+ return ZTE_PROFILES.find((profile) => profile.id === profileId);
19
+ }
20
+ export class ZteGoformProvider extends ZteSessionRuntime {
21
+ async fingerprint(request) {
22
+ const evidence = await this.probeEvidence(request);
23
+ const matches = evidence !== undefined;
24
+ return {
25
+ signal: matches ? 'match' : 'unknown',
26
+ strength: 'strong',
27
+ profiles: matches ? [evidence.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: '2',
67
+ eligibleTransports: ['network'],
68
+ passiveMatchers: ['MF79U', 'MF266'].map((firmware) => ({
69
+ id: `firmware-${firmware}`,
70
+ fact: 'firmware',
71
+ expected: [firmware],
72
+ profiles: zteProfilesForFirmware(firmware).map((profile) => 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: 'evidence-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 === 'multi-user-salted-sha256' ? 'LOGIN_MULTI_USER' : 'LOGIN',
93
+ interfaceBound: true,
94
+ redirects: 'disabled',
95
+ },
96
+ response: profile.algorithm === 'multi-user-salted-sha256'
97
+ ? { status: 'matched', sessionMaterial: '[redacted]', ad: '[redacted]' }
98
+ : { status: 'matched', sessionMaterial: '[redacted]' },
99
+ })),
100
+ };
101
+ }