@ceralive/modem-control 0.2.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (464) hide show
  1. package/README.md +317 -0
  2. package/dist/backend/at-lease.d.ts +57 -0
  3. package/dist/backend/at-lease.js +109 -0
  4. package/dist/backend/cell-info.d.ts +46 -0
  5. package/dist/backend/cell-info.js +124 -0
  6. package/dist/backend/constants.d.ts +24 -0
  7. package/{src/backend/constants.ts → dist/backend/constants.js} +4 -9
  8. package/dist/backend/device-classifier.d.ts +46 -0
  9. package/dist/backend/device-classifier.js +258 -0
  10. package/dist/backend/enrichment.d.ts +28 -0
  11. package/dist/backend/enrichment.js +59 -0
  12. package/dist/backend/features.d.ts +61 -0
  13. package/dist/backend/features.js +109 -0
  14. package/dist/backend/identity-ladder.d.ts +57 -0
  15. package/dist/backend/identity-ladder.js +158 -0
  16. package/dist/backend/identity-registry.d.ts +51 -0
  17. package/dist/backend/identity-registry.js +101 -0
  18. package/dist/backend/index.d.ts +30 -0
  19. package/dist/backend/index.js +35 -0
  20. package/dist/backend/lifecycle-interlock.d.ts +25 -0
  21. package/dist/backend/lifecycle-interlock.js +18 -0
  22. package/dist/backend/managed-objects.d.ts +39 -0
  23. package/dist/backend/managed-objects.js +82 -0
  24. package/dist/backend/mapping.d.ts +11 -0
  25. package/dist/backend/mapping.js +148 -0
  26. package/dist/backend/mm-backend.d.ts +44 -0
  27. package/dist/backend/mm-backend.js +154 -0
  28. package/dist/backend/mm-location.d.ts +21 -0
  29. package/dist/backend/mm-location.js +237 -0
  30. package/dist/backend/mm-mutations.d.ts +42 -0
  31. package/dist/backend/mm-mutations.js +259 -0
  32. package/dist/backend/modem-actor.d.ts +41 -0
  33. package/dist/backend/modem-actor.js +78 -0
  34. package/dist/backend/nm-auto-apn.d.ts +54 -0
  35. package/dist/backend/nm-auto-apn.js +124 -0
  36. package/dist/backend/nm-gsm-fields.d.ts +23 -0
  37. package/dist/backend/nm-gsm-fields.js +107 -0
  38. package/dist/backend/nmcli-nm-port.d.ts +28 -0
  39. package/dist/backend/nmcli-nm-port.js +173 -0
  40. package/dist/backend/nmcli-runner.d.ts +24 -0
  41. package/dist/backend/nmcli-runner.js +35 -0
  42. package/dist/backend/observer.d.ts +37 -0
  43. package/dist/backend/observer.js +219 -0
  44. package/dist/backend/power-contract.d.ts +49 -0
  45. package/dist/backend/power-contract.js +34 -0
  46. package/dist/backend/recovery-attribution.d.ts +32 -0
  47. package/dist/backend/recovery-attribution.js +57 -0
  48. package/dist/backend/recovery-budget.d.ts +45 -0
  49. package/dist/backend/recovery-budget.js +44 -0
  50. package/dist/backend/recovery-ladder.d.ts +94 -0
  51. package/dist/backend/recovery-ladder.js +116 -0
  52. package/dist/backend/router-ethernet.d.ts +19 -0
  53. package/dist/backend/router-ethernet.js +66 -0
  54. package/dist/backend/row-store.d.ts +13 -0
  55. package/dist/backend/row-store.js +82 -0
  56. package/dist/backend/signal-setup.d.ts +30 -0
  57. package/dist/backend/signal-setup.js +92 -0
  58. package/dist/backend/sim-unlock.d.ts +13 -0
  59. package/dist/backend/sim-unlock.js +153 -0
  60. package/dist/backend/transition-preconditions.d.ts +76 -0
  61. package/dist/backend/transition-preconditions.js +64 -0
  62. package/dist/backend/usage/accounting.d.ts +39 -0
  63. package/dist/backend/usage/accounting.js +73 -0
  64. package/dist/backend/usage/billing-cycle.d.ts +11 -0
  65. package/dist/backend/usage/billing-cycle.js +39 -0
  66. package/dist/backend/usage/boot-id.d.ts +6 -0
  67. package/{src/backend/usage/boot-id.ts → dist/backend/usage/boot-id.js} +7 -7
  68. package/dist/backend/usage/index.d.ts +8 -0
  69. package/dist/backend/usage/index.js +11 -0
  70. package/dist/backend/usage/policy-store.d.ts +50 -0
  71. package/dist/backend/usage/policy-store.js +161 -0
  72. package/dist/backend/usage/policy-write.d.ts +65 -0
  73. package/dist/backend/usage/policy-write.js +112 -0
  74. package/dist/backend/usage/proc-net-dev.d.ts +18 -0
  75. package/{src/backend/usage/proc-net-dev.ts → dist/backend/usage/proc-net-dev.js} +39 -47
  76. package/dist/backend/usage/sampler.d.ts +75 -0
  77. package/dist/backend/usage/sampler.js +211 -0
  78. package/dist/backend/usage/store.d.ts +38 -0
  79. package/dist/backend/usage/store.js +126 -0
  80. package/dist/backend/usb-device-snapshot.d.ts +31 -0
  81. package/dist/backend/usb-device-snapshot.js +1 -0
  82. package/dist/backend/usb-enumerator.d.ts +21 -0
  83. package/dist/backend/usb-enumerator.js +153 -0
  84. package/dist/backend/usb-mode-transition.d.ts +29 -0
  85. package/dist/backend/usb-mode-transition.js +196 -0
  86. package/dist/band/band-names.d.ts +42 -0
  87. package/dist/band/band-names.js +150 -0
  88. package/dist/band/certification.d.ts +84 -0
  89. package/dist/band/certification.js +127 -0
  90. package/dist/band/certified-bands.json +4 -0
  91. package/dist/band/index.d.ts +2 -0
  92. package/dist/band/index.js +8 -0
  93. package/dist/capability/detect.d.ts +52 -0
  94. package/dist/capability/detect.js +86 -0
  95. package/dist/capability/five-g-preference.d.ts +104 -0
  96. package/dist/capability/five-g-preference.js +171 -0
  97. package/dist/capability/index.d.ts +3 -0
  98. package/dist/capability/index.js +10 -0
  99. package/dist/capability/support-claim.d.ts +39 -0
  100. package/dist/capability/support-claim.js +81 -0
  101. package/dist/domain/brand.d.ts +10 -0
  102. package/dist/domain/brand.js +21 -0
  103. package/dist/domain/errors.d.ts +32 -0
  104. package/dist/domain/errors.js +48 -0
  105. package/dist/domain/generation.d.ts +8 -0
  106. package/dist/domain/generation.js +12 -0
  107. package/dist/domain/guards.d.ts +6 -0
  108. package/dist/domain/guards.js +127 -0
  109. package/dist/domain/identity.d.ts +109 -0
  110. package/dist/domain/identity.js +86 -0
  111. package/dist/domain/index.d.ts +14 -0
  112. package/dist/domain/index.js +18 -0
  113. package/dist/domain/mm-enums.d.ts +12 -0
  114. package/dist/domain/mm-enums.js +139 -0
  115. package/dist/domain/modem-presentation.d.ts +10 -0
  116. package/dist/domain/modem-presentation.js +39 -0
  117. package/dist/domain/observation.d.ts +39 -0
  118. package/dist/domain/observation.js +4 -0
  119. package/dist/domain/operation.d.ts +118 -0
  120. package/dist/domain/operation.js +85 -0
  121. package/dist/domain/physical-identity.d.ts +43 -0
  122. package/dist/domain/physical-identity.js +113 -0
  123. package/{src/domain/policy.ts → dist/domain/policy.d.ts} +34 -69
  124. package/dist/domain/policy.js +38 -0
  125. package/dist/domain/shadow-divergence.d.ts +27 -0
  126. package/dist/domain/shadow-divergence.js +70 -0
  127. package/dist/domain/snapshot.d.ts +53 -0
  128. package/dist/domain/snapshot.js +75 -0
  129. package/{src/domain/state.ts → dist/domain/state.d.ts} +23 -122
  130. package/dist/domain/state.js +38 -0
  131. package/dist/fcc/coverage.d.ts +63 -0
  132. package/dist/fcc/coverage.js +102 -0
  133. package/dist/fcc/index.d.ts +3 -0
  134. package/dist/fcc/index.js +12 -0
  135. package/dist/fcc/policy-store.d.ts +40 -0
  136. package/dist/fcc/policy-store.js +146 -0
  137. package/dist/fcc/policy-write.d.ts +32 -0
  138. package/dist/fcc/policy-write.js +37 -0
  139. package/dist/hardware/hilink-protocol.d.ts +39 -0
  140. package/dist/hardware/hilink-protocol.js +58 -0
  141. package/dist/hardware/index.d.ts +3 -0
  142. package/dist/hardware/index.js +15 -0
  143. package/dist/hardware/router-parsers.d.ts +89 -0
  144. package/dist/hardware/router-parsers.js +234 -0
  145. package/dist/index.d.ts +19 -0
  146. package/dist/index.js +26 -0
  147. package/dist/journal/codec.d.ts +44 -0
  148. package/dist/journal/codec.js +198 -0
  149. package/dist/journal/engine.d.ts +28 -0
  150. package/dist/journal/engine.js +68 -0
  151. package/dist/journal/entry.d.ts +74 -0
  152. package/dist/journal/entry.js +56 -0
  153. package/dist/journal/index.d.ts +6 -0
  154. package/dist/journal/index.js +6 -0
  155. package/dist/journal/legacy-ceraui.d.ts +73 -0
  156. package/dist/journal/legacy-ceraui.js +227 -0
  157. package/dist/journal/recovery.d.ts +58 -0
  158. package/dist/journal/recovery.js +117 -0
  159. package/dist/journal/store.d.ts +55 -0
  160. package/dist/journal/store.js +150 -0
  161. package/dist/location/fix-state.d.ts +53 -0
  162. package/dist/location/fix-state.js +75 -0
  163. package/dist/location/index.d.ts +2 -0
  164. package/dist/location/index.js +8 -0
  165. package/dist/location/nmea.d.ts +10 -0
  166. package/dist/location/nmea.js +89 -0
  167. package/dist/observations/envelope.d.ts +63 -0
  168. package/dist/observations/envelope.js +77 -0
  169. package/dist/observations/freshness.d.ts +28 -0
  170. package/dist/observations/freshness.js +76 -0
  171. package/dist/observations/index.d.ts +13 -0
  172. package/dist/observations/index.js +24 -0
  173. package/dist/observations/metric.d.ts +47 -0
  174. package/dist/observations/metric.js +73 -0
  175. package/dist/observations/model.d.ts +80 -0
  176. package/dist/observations/model.js +11 -0
  177. package/dist/observations/provenance.d.ts +94 -0
  178. package/dist/observations/provenance.js +67 -0
  179. package/dist/observations/raw.d.ts +42 -0
  180. package/dist/observations/raw.js +146 -0
  181. package/dist/observations/reading.d.ts +51 -0
  182. package/dist/observations/reading.js +65 -0
  183. package/dist/observations/sources/hilink.d.ts +10 -0
  184. package/dist/observations/sources/hilink.js +84 -0
  185. package/dist/observations/sources/modemmanager.d.ts +18 -0
  186. package/dist/observations/sources/modemmanager.js +239 -0
  187. package/dist/observations/sources/router-shared.d.ts +29 -0
  188. package/dist/observations/sources/router-shared.js +68 -0
  189. package/dist/observations/sources/ufi.d.ts +10 -0
  190. package/dist/observations/sources/ufi.js +111 -0
  191. package/dist/observations/sources/zte.d.ts +7 -0
  192. package/dist/observations/sources/zte.js +71 -0
  193. package/dist/observations/state-separation.d.ts +64 -0
  194. package/dist/observations/state-separation.js +52 -0
  195. package/dist/operations/contracts.d.ts +72 -0
  196. package/dist/operations/contracts.js +1 -0
  197. package/dist/operations/index.d.ts +1 -0
  198. package/dist/operations/index.js +1 -0
  199. package/dist/operations/operation-engine.d.ts +16 -0
  200. package/dist/operations/operation-engine.js +194 -0
  201. package/dist/ports/index.d.ts +12 -0
  202. package/{src/ports/index.ts → dist/ports/index.js} +12 -8
  203. package/dist/ports/location.d.ts +87 -0
  204. package/dist/ports/location.js +36 -0
  205. package/dist/ports/modem-manager.d.ts +89 -0
  206. package/dist/ports/modem-manager.js +9 -0
  207. package/dist/ports/mutation-admission.d.ts +27 -0
  208. package/dist/ports/mutation-admission.js +9 -0
  209. package/dist/ports/network-manager.d.ts +68 -0
  210. package/dist/ports/network-manager.js +13 -0
  211. package/{src/ports/observation.ts → dist/ports/observation.d.ts} +16 -27
  212. package/dist/ports/observation.js +7 -0
  213. package/dist/ports/ops.d.ts +44 -0
  214. package/dist/ports/ops.js +16 -0
  215. package/{src/ports/receipts.ts → dist/ports/receipts.d.ts} +5 -27
  216. package/dist/ports/receipts.js +9 -0
  217. package/dist/ports/reconcile.d.ts +33 -0
  218. package/dist/ports/reconcile.js +200 -0
  219. package/dist/ports/resource-ownership.d.ts +29 -0
  220. package/dist/ports/resource-ownership.js +1 -0
  221. package/dist/ports/router.d.ts +19 -0
  222. package/dist/ports/router.js +7 -0
  223. package/dist/ports/sms.d.ts +64 -0
  224. package/dist/ports/sms.js +24 -0
  225. package/dist/ports/uhubctl.d.ts +6 -0
  226. package/dist/ports/uhubctl.js +1 -0
  227. package/dist/providers/contracts.d.ts +124 -0
  228. package/dist/providers/contracts.js +10 -0
  229. package/dist/providers/huawei-hilink/index.d.ts +2 -0
  230. package/dist/providers/huawei-hilink/index.js +2 -0
  231. package/dist/providers/huawei-hilink/operations.d.ts +20 -0
  232. package/dist/providers/huawei-hilink/operations.js +56 -0
  233. package/dist/providers/huawei-hilink/provider.d.ts +52 -0
  234. package/dist/providers/huawei-hilink/provider.js +76 -0
  235. package/dist/providers/huawei-hilink/runtime.d.ts +22 -0
  236. package/dist/providers/huawei-hilink/runtime.js +171 -0
  237. package/dist/providers/huawei-hilink/session.d.ts +28 -0
  238. package/dist/providers/huawei-hilink/session.js +120 -0
  239. package/dist/providers/huawei-hilink/transport.d.ts +19 -0
  240. package/dist/providers/huawei-hilink/transport.js +1 -0
  241. package/dist/providers/index.d.ts +8 -0
  242. package/dist/providers/index.js +8 -0
  243. package/dist/providers/matcher.d.ts +3 -0
  244. package/dist/providers/matcher.js +205 -0
  245. package/dist/providers/modem-manager/errors.d.ts +7 -0
  246. package/dist/providers/modem-manager/errors.js +37 -0
  247. package/dist/providers/modem-manager/generic-operations.d.ts +8 -0
  248. package/dist/providers/modem-manager/generic-operations.js +207 -0
  249. package/dist/providers/modem-manager/index.d.ts +3 -0
  250. package/dist/providers/modem-manager/index.js +3 -0
  251. package/dist/providers/modem-manager/module-operations.d.ts +21 -0
  252. package/dist/providers/modem-manager/module-operations.js +118 -0
  253. package/dist/providers/modem-manager/provider.d.ts +39 -0
  254. package/dist/providers/modem-manager/provider.js +152 -0
  255. package/dist/providers/modem-manager/snapshot.d.ts +5 -0
  256. package/dist/providers/modem-manager/snapshot.js +204 -0
  257. package/dist/providers/modem-manager/types.d.ts +135 -0
  258. package/dist/providers/modem-manager/types.js +1 -0
  259. package/dist/providers/network-manager/adapter.d.ts +71 -0
  260. package/dist/providers/network-manager/adapter.js +348 -0
  261. package/dist/providers/network-manager/index.d.ts +2 -0
  262. package/dist/providers/network-manager/index.js +2 -0
  263. package/dist/providers/network-manager/types.d.ts +163 -0
  264. package/dist/providers/network-manager/types.js +77 -0
  265. package/dist/providers/registry.d.ts +13 -0
  266. package/dist/providers/registry.js +33 -0
  267. package/dist/providers/ufi-himi/index.d.ts +6 -0
  268. package/dist/providers/ufi-himi/index.js +6 -0
  269. package/dist/providers/ufi-himi/operations.d.ts +41 -0
  270. package/dist/providers/ufi-himi/operations.js +66 -0
  271. package/dist/providers/ufi-himi/prohibitions.d.ts +62 -0
  272. package/dist/providers/ufi-himi/prohibitions.js +88 -0
  273. package/dist/providers/ufi-himi/provider.d.ts +41 -0
  274. package/dist/providers/ufi-himi/provider.js +204 -0
  275. package/dist/providers/ufi-himi/qualcomm-evidence.d.ts +32 -0
  276. package/dist/providers/ufi-himi/qualcomm-evidence.js +51 -0
  277. package/dist/providers/ufi-himi/session.d.ts +46 -0
  278. package/dist/providers/ufi-himi/session.js +92 -0
  279. package/dist/providers/ufi-himi/transport.d.ts +29 -0
  280. package/dist/providers/ufi-himi/transport.js +25 -0
  281. package/dist/providers/zte-goform/index.d.ts +2 -0
  282. package/dist/providers/zte-goform/index.js +2 -0
  283. package/dist/providers/zte-goform/provider.d.ts +51 -0
  284. package/dist/providers/zte-goform/provider.js +101 -0
  285. package/dist/providers/zte-goform/session.d.ts +17 -0
  286. package/dist/providers/zte-goform/session.js +133 -0
  287. package/dist/providers/zte-goform/transport.d.ts +16 -0
  288. package/dist/providers/zte-goform/transport.js +1 -0
  289. package/dist/radio/band-truth.d.ts +50 -0
  290. package/dist/radio/band-truth.js +92 -0
  291. package/dist/radio/index.d.ts +3 -0
  292. package/dist/radio/index.js +10 -0
  293. package/dist/radio/mode-combinations.d.ts +78 -0
  294. package/dist/radio/mode-combinations.js +198 -0
  295. package/dist/radio/mode-truth.d.ts +67 -0
  296. package/dist/radio/mode-truth.js +112 -0
  297. package/dist/redact.d.ts +15 -0
  298. package/dist/redact.js +189 -0
  299. package/dist/safety/composition-root.d.ts +28 -0
  300. package/dist/safety/composition-root.js +65 -0
  301. package/dist/safety/flock-resource-ownership.d.ts +11 -0
  302. package/dist/safety/flock-resource-ownership.js +151 -0
  303. package/dist/safety/index.d.ts +2 -0
  304. package/dist/safety/index.js +2 -0
  305. package/dist/sms/dbus-messaging.d.ts +17 -0
  306. package/dist/sms/dbus-messaging.js +185 -0
  307. package/dist/sms/inbox-store.d.ts +10 -0
  308. package/dist/sms/inbox-store.js +82 -0
  309. package/dist/sms/index.d.ts +4 -0
  310. package/dist/sms/index.js +10 -0
  311. package/dist/sms/mmcli-parse.d.ts +54 -0
  312. package/dist/sms/mmcli-parse.js +224 -0
  313. package/dist/sms/normalize.d.ts +42 -0
  314. package/dist/sms/normalize.js +95 -0
  315. package/dist/testing/domain-fakes.d.ts +58 -0
  316. package/dist/testing/domain-fakes.js +98 -0
  317. package/dist/testing/index.d.ts +2 -0
  318. package/dist/testing/index.js +17 -0
  319. package/dist/testing/provider-fakes.d.ts +45 -0
  320. package/dist/testing/provider-fakes.js +64 -0
  321. package/dist/transport/calls.d.ts +9 -0
  322. package/dist/transport/calls.js +88 -0
  323. package/dist/transport/codec.d.ts +3 -0
  324. package/dist/transport/codec.js +207 -0
  325. package/dist/transport/dbus-native.d.ts +57 -0
  326. package/dist/transport/dbus-native.js +17 -0
  327. package/dist/transport/errors.d.ts +21 -0
  328. package/{src/transport/errors.ts → dist/transport/errors.js} +34 -46
  329. package/dist/transport/index.d.ts +4 -0
  330. package/dist/transport/index.js +9 -0
  331. package/dist/transport/signals.d.ts +15 -0
  332. package/dist/transport/signals.js +123 -0
  333. package/dist/transport/signature.d.ts +7 -0
  334. package/dist/transport/signature.js +94 -0
  335. package/dist/transport/transport.d.ts +2 -0
  336. package/dist/transport/transport.js +202 -0
  337. package/dist/transport/types.d.ts +61 -0
  338. package/dist/transport/types.js +19 -0
  339. package/dist/usb-mode/catalog-schema.d.ts +139 -0
  340. package/dist/usb-mode/catalog-schema.js +97 -0
  341. package/dist/usb-mode/catalog.d.ts +21 -0
  342. package/{src/usb-mode/catalog.ts → dist/usb-mode/catalog.js} +10 -32
  343. package/dist/usb-mode/certified-catalog.json +67 -0
  344. package/dist/usb-mode/index.d.ts +5 -0
  345. package/dist/usb-mode/index.js +15 -0
  346. package/dist/usb-mode/ingestion.d.ts +111 -0
  347. package/dist/usb-mode/ingestion.js +187 -0
  348. package/dist/usb-mode/promotion-review.d.ts +21 -0
  349. package/dist/usb-mode/promotion-review.js +87 -0
  350. package/dist/usb-mode/usb-devices-parse.d.ts +36 -0
  351. package/dist/usb-mode/usb-devices-parse.js +157 -0
  352. package/dist/ussd/calls.d.ts +32 -0
  353. package/dist/ussd/calls.js +96 -0
  354. package/dist/ussd/index.d.ts +5 -0
  355. package/dist/ussd/index.js +12 -0
  356. package/dist/ussd/mm-ussd.d.ts +37 -0
  357. package/dist/ussd/mm-ussd.js +205 -0
  358. package/dist/ussd/refusal.d.ts +53 -0
  359. package/dist/ussd/refusal.js +154 -0
  360. package/dist/ussd/registration.d.ts +20 -0
  361. package/dist/ussd/registration.js +101 -0
  362. package/dist/ussd/session.d.ts +84 -0
  363. package/dist/ussd/session.js +163 -0
  364. package/package.json +38 -4
  365. package/src/backend/at-lease.test.ts +0 -106
  366. package/src/backend/at-lease.ts +0 -158
  367. package/src/backend/cell-info.test.ts +0 -154
  368. package/src/backend/cell-info.ts +0 -160
  369. package/src/backend/device-classifier.test.ts +0 -168
  370. package/src/backend/device-classifier.ts +0 -248
  371. package/src/backend/enrichment.ts +0 -96
  372. package/src/backend/features.test.ts +0 -162
  373. package/src/backend/features.ts +0 -179
  374. package/src/backend/identity-ladder.test.ts +0 -117
  375. package/src/backend/identity-ladder.ts +0 -221
  376. package/src/backend/identity-registry.test.ts +0 -89
  377. package/src/backend/identity-registry.ts +0 -151
  378. package/src/backend/index.ts +0 -221
  379. package/src/backend/lifecycle-interlock.ts +0 -38
  380. package/src/backend/managed-objects.ts +0 -108
  381. package/src/backend/mapping.ts +0 -160
  382. package/src/backend/mm-backend.ts +0 -191
  383. package/src/backend/mm-mutations.ts +0 -228
  384. package/src/backend/modem-actor.test.ts +0 -95
  385. package/src/backend/modem-actor.ts +0 -112
  386. package/src/backend/nm-auto-apn.ts +0 -161
  387. package/src/backend/nm-gsm-fields.ts +0 -122
  388. package/src/backend/nmcli-nm-port.ts +0 -228
  389. package/src/backend/nmcli-runner.ts +0 -52
  390. package/src/backend/observer.ts +0 -297
  391. package/src/backend/power-contract.test.ts +0 -40
  392. package/src/backend/power-contract.ts +0 -83
  393. package/src/backend/recovery-attribution.test.ts +0 -102
  394. package/src/backend/recovery-attribution.ts +0 -86
  395. package/src/backend/recovery-budget.test.ts +0 -64
  396. package/src/backend/recovery-budget.ts +0 -84
  397. package/src/backend/recovery-ladder.test.ts +0 -257
  398. package/src/backend/recovery-ladder.ts +0 -249
  399. package/src/backend/router-ethernet.test.ts +0 -71
  400. package/src/backend/router-ethernet.ts +0 -90
  401. package/src/backend/row-store.ts +0 -105
  402. package/src/backend/signal-setup.ts +0 -112
  403. package/src/backend/sim-unlock.ts +0 -193
  404. package/src/backend/transition-preconditions.ts +0 -149
  405. package/src/backend/usage/accounting.test.ts +0 -147
  406. package/src/backend/usage/accounting.ts +0 -123
  407. package/src/backend/usage/billing-cycle.test.ts +0 -62
  408. package/src/backend/usage/billing-cycle.ts +0 -45
  409. package/src/backend/usage/index.ts +0 -37
  410. package/src/backend/usage/proc-net-dev.test.ts +0 -56
  411. package/src/backend/usage/sampler.test.ts +0 -219
  412. package/src/backend/usage/sampler.ts +0 -228
  413. package/src/backend/usage/store.test.ts +0 -148
  414. package/src/backend/usage/store.ts +0 -177
  415. package/src/backend/usb-enumerator.test.ts +0 -87
  416. package/src/backend/usb-enumerator.ts +0 -181
  417. package/src/backend/usb-mode-transition.test.ts +0 -323
  418. package/src/backend/usb-mode-transition.ts +0 -253
  419. package/src/domain/brand.ts +0 -29
  420. package/src/domain/errors.ts +0 -77
  421. package/src/domain/guards.test.ts +0 -218
  422. package/src/domain/guards.ts +0 -144
  423. package/src/domain/identity.test.ts +0 -83
  424. package/src/domain/identity.ts +0 -165
  425. package/src/domain/index.ts +0 -12
  426. package/src/domain/snapshot.test.ts +0 -266
  427. package/src/domain/snapshot.ts +0 -120
  428. package/src/index.test.ts +0 -6
  429. package/src/index.ts +0 -15
  430. package/src/ports/README.md +0 -61
  431. package/src/ports/forbidden-surface.test.ts +0 -241
  432. package/src/ports/modem-manager.ts +0 -72
  433. package/src/ports/network-manager.ts +0 -87
  434. package/src/ports/ops.ts +0 -60
  435. package/src/ports/ops.type-test.ts +0 -39
  436. package/src/ports/receipts.test.ts +0 -153
  437. package/src/ports/reconcile.test.ts +0 -152
  438. package/src/ports/reconcile.ts +0 -338
  439. package/src/ports/router.ts +0 -29
  440. package/src/redact.test.ts +0 -82
  441. package/src/redact.ts +0 -73
  442. package/src/transport/README.md +0 -65
  443. package/src/transport/calls.ts +0 -113
  444. package/src/transport/characterization.test.ts +0 -260
  445. package/src/transport/codec.test.ts +0 -118
  446. package/src/transport/codec.ts +0 -240
  447. package/src/transport/conformance-python.test.ts +0 -152
  448. package/src/transport/conformance-same-lib.test.ts +0 -115
  449. package/src/transport/dbus-native-lib.d.ts +0 -19
  450. package/src/transport/dbus-native.ts +0 -85
  451. package/src/transport/index.ts +0 -30
  452. package/src/transport/no-library-leak.test.ts +0 -61
  453. package/src/transport/reliability.test.ts +0 -173
  454. package/src/transport/signals.ts +0 -150
  455. package/src/transport/signature.ts +0 -110
  456. package/src/transport/test-support/fake-service.ts +0 -168
  457. package/src/transport/test-support/independent-producer.py +0 -110
  458. package/src/transport/test-support/private-bus.ts +0 -66
  459. package/src/transport/transport.ts +0 -250
  460. package/src/transport/types.ts +0 -118
  461. package/src/usb-mode/catalog-schema.test.ts +0 -181
  462. package/src/usb-mode/catalog-schema.ts +0 -113
  463. package/src/usb-mode/certified-catalog.json +0 -67
  464. package/src/usb-mode/index.ts +0 -27
@@ -1,266 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
- import { ImpossibleStateError, RevisionMonotonicityError } from './errors';
3
- import { imeiEquipmentId, type ModemIdentity, runtimePath } from './identity';
4
- import {
5
- applyTransition,
6
- type CellularSnapshot,
7
- createSnapshot,
8
- initialSnapshot,
9
- markSourceUnavailable,
10
- revision,
11
- supersede,
12
- } from './snapshot';
13
- import {
14
- type DataInterface,
15
- epochMillis,
16
- isRegistered,
17
- MM_STATES_REQUIRING_RADIO,
18
- type MmState,
19
- type NmActivation,
20
- type RadioAccessTechnology,
21
- type RadioPower,
22
- type RecoveryStage,
23
- type RecoveryState,
24
- type RegistrationStatus,
25
- SIM_LOCK_REQUIRES_CARD,
26
- type SimLock,
27
- type SimSlot,
28
- } from './state';
29
-
30
- const IDENTITY: ModemIdentity = {
31
- equipmentId: imeiEquipmentId('490154203237518'),
32
- runtimePath: runtimePath('/org/freedesktop/ModemManager1/Modem/0'),
33
- };
34
-
35
- describe('reducers', () => {
36
- test('initialSnapshot is a valid absent baseline at revision 0', () => {
37
- const snapshot = initialSnapshot(IDENTITY);
38
- expect(snapshot.presence).toBe('absent');
39
- expect(snapshot.revision).toBe(revision(0));
40
- expect(() => createSnapshot(snapshot)).not.toThrow();
41
- });
42
-
43
- test('applyTransition bumps the revision by exactly one', () => {
44
- const start = initialSnapshot(IDENTITY);
45
- const next = applyTransition(start, { reconcileStatus: 'reconciling' });
46
- expect(next.revision).toBe(revision(1));
47
- expect(next.reconcileStatus).toBe('reconciling');
48
- });
49
-
50
- test('applyTransition re-validates and throws on an impossible patch', () => {
51
- const start = applyTransition(initialSnapshot(IDENTITY), {
52
- presence: 'present',
53
- radioPower: 'on',
54
- mmState: 'registered',
55
- registration: { status: 'home', activeRats: new Set(['lte']) },
56
- });
57
- expect(() => applyTransition(start, { presence: 'absent' })).toThrow(ImpossibleStateError);
58
- });
59
-
60
- test('markSourceUnavailable keeps presence and marks the source, bumping revision', () => {
61
- const present = applyTransition(initialSnapshot(IDENTITY), { presence: 'present' });
62
- const unavailable = markSourceUnavailable(present);
63
- expect(unavailable.presence).toBe('present');
64
- expect(unavailable.sourceHealth).toBe('sourceUnavailable');
65
- expect(unavailable.revision).toBe(revision(2));
66
- });
67
-
68
- test('supersede rejects a stale or equal revision', () => {
69
- const current = applyTransition(initialSnapshot(IDENTITY), { reconcileStatus: 'converged' });
70
- const stale: CellularSnapshot = { ...initialSnapshot(IDENTITY), revision: revision(0) };
71
- expect(() => supersede(current, stale)).toThrow(RevisionMonotonicityError);
72
- });
73
-
74
- test('supersede accepts a strictly-newer valid snapshot', () => {
75
- const current = initialSnapshot(IDENTITY);
76
- const newer: CellularSnapshot = { ...initialSnapshot(IDENTITY), revision: revision(9) };
77
- expect(supersede(current, newer).revision).toBe(revision(9));
78
- });
79
- });
80
-
81
- // --- randomized property test ----------------------------------------------
82
-
83
- const MM_STATES: readonly MmState[] = [
84
- 'failed',
85
- 'unknown',
86
- 'initializing',
87
- 'locked',
88
- 'disabled',
89
- 'enabled',
90
- 'searching',
91
- 'registered',
92
- 'connecting',
93
- 'connected',
94
- ];
95
- const REG_STATUSES: readonly RegistrationStatus[] = [
96
- 'idle',
97
- 'home',
98
- 'searching',
99
- 'denied',
100
- 'unknown',
101
- 'roaming',
102
- ];
103
- const NM_STATES: readonly NmActivation[] = [
104
- 'unmanaged',
105
- 'unavailable',
106
- 'disconnected',
107
- 'activating',
108
- 'activated',
109
- 'failed',
110
- ];
111
- const POWERS: readonly RadioPower[] = ['unknown', 'off', 'low', 'on'];
112
- const STAGES: readonly RecoveryStage[] = [
113
- 'idle',
114
- 'attributing',
115
- 'nm-cycle',
116
- 'mm-cycle',
117
- 'reset',
118
- 'power-cycle',
119
- 'cooldown',
120
- 'exhausted',
121
- ];
122
- const LOCKS: readonly SimLock[] = [
123
- 'unknown',
124
- 'none',
125
- 'sim-pin',
126
- 'sim-puk',
127
- 'net-pers',
128
- 'permanently-blocked',
129
- ];
130
- const RATS: readonly RadioAccessTechnology[] = ['gsm', 'umts', 'lte', '5gnr'];
131
-
132
- /** Deterministic xorshift32 PRNG so a failing case is always reproducible. */
133
- function makePrng(seed: number): () => number {
134
- let state = seed >>> 0 || 1;
135
- return () => {
136
- state ^= state << 13;
137
- state >>>= 0;
138
- state ^= state >> 17;
139
- state ^= state << 5;
140
- state >>>= 0;
141
- return state / 0xffffffff;
142
- };
143
- }
144
-
145
- function pick<T>(rng: () => number, values: readonly T[]): T {
146
- const index = Math.min(values.length - 1, Math.floor(rng() * values.length));
147
- return values[index] as T;
148
- }
149
-
150
- function randomSnapshot(rng: () => number): CellularSnapshot {
151
- const rats = new Set<RadioAccessTechnology>();
152
- for (const rat of RATS) {
153
- if (rng() < 0.5) {
154
- rats.add(rat);
155
- }
156
- }
157
-
158
- const slotCount = Math.floor(rng() * 3);
159
- const simSlots: SimSlot[] = [];
160
- for (let i = 0; i < slotCount; i += 1) {
161
- simSlots.push({
162
- index: i + 1,
163
- occupied: rng() < 0.7,
164
- active: rng() < 0.5,
165
- lock: pick(rng, LOCKS),
166
- });
167
- }
168
-
169
- let dataInterface: DataInterface;
170
- if (rng() < 0.6) {
171
- dataInterface = rng() < 0.5 ? { present: true, name: 'wwan0' } : { present: true };
172
- } else {
173
- dataInterface = rng() < 0.5 ? { present: false } : { present: false, name: 'wwan0' };
174
- }
175
-
176
- const stage = pick(rng, STAGES);
177
- const attempts = Math.floor(rng() * 4);
178
- const recoveryState: RecoveryState =
179
- rng() < 0.5
180
- ? { stage, attempts, cooldownUntil: epochMillis(1_700_000_000_000) }
181
- : { stage, attempts };
182
-
183
- return {
184
- identity: IDENTITY,
185
- presence: rng() < 0.5 ? 'present' : 'absent',
186
- sourceHealth: pick(rng, ['live', 'stale', 'sourceUnavailable'] as const),
187
- simSlots,
188
- radioPower: pick(rng, POWERS),
189
- mmState: pick(rng, MM_STATES),
190
- registration: { status: pick(rng, REG_STATUSES), activeRats: rats },
191
- nmActivation: pick(rng, NM_STATES),
192
- dataInterface,
193
- reconcileStatus: pick(rng, [
194
- 'converged',
195
- 'reconciling',
196
- 'pending',
197
- 'divergent',
198
- 'unsupported',
199
- ] as const),
200
- recoveryState,
201
- revision: revision(Math.floor(rng() * 1_000_000)),
202
- };
203
- }
204
-
205
- /** Independent restatement of the invariants — never calls the guards under test. */
206
- function assertInvariants(s: CellularSnapshot): void {
207
- if (isRegistered(s.registration.status)) {
208
- expect(s.presence).toBe('present');
209
- expect(s.radioPower).not.toBe('off');
210
- expect(s.registration.activeRats.size).toBeGreaterThan(0);
211
- }
212
- if (MM_STATES_REQUIRING_RADIO.has(s.mmState)) {
213
- expect(s.presence).toBe('present');
214
- }
215
- if (s.nmActivation === 'activated') {
216
- expect(s.presence).toBe('present');
217
- expect(s.dataInterface.present).toBe(true);
218
- expect(s.mmState).toBe('connected');
219
- }
220
- if (s.radioPower === 'off') {
221
- const onAir =
222
- MM_STATES_REQUIRING_RADIO.has(s.mmState) ||
223
- s.registration.status === 'searching' ||
224
- s.nmActivation === 'activating' ||
225
- s.nmActivation === 'activated';
226
- expect(onAir).toBe(false);
227
- }
228
- expect(s.simSlots.filter((slot) => slot.active).length).toBeLessThanOrEqual(1);
229
- for (const slot of s.simSlots) {
230
- if (!slot.occupied) {
231
- expect(SIM_LOCK_REQUIRES_CARD.has(slot.lock)).toBe(false);
232
- }
233
- }
234
- if (!s.dataInterface.present) {
235
- expect(s.dataInterface.name).toBeUndefined();
236
- }
237
- expect(s.recoveryState.attempts).toBeGreaterThanOrEqual(0);
238
- expect(s.recoveryState.cooldownUntil !== undefined).toBe(s.recoveryState.stage === 'cooldown');
239
- }
240
-
241
- describe('randomized property test', () => {
242
- test('every constructed snapshot upholds the invariants; every rejection is typed', () => {
243
- const rng = makePrng(0x9e3779b9);
244
- let constructed = 0;
245
- let rejected = 0;
246
-
247
- for (let i = 0; i < 5000; i += 1) {
248
- const candidate = randomSnapshot(rng);
249
- let built: CellularSnapshot | null = null;
250
- try {
251
- built = createSnapshot(candidate);
252
- } catch (error) {
253
- rejected += 1;
254
- expect(error).toBeInstanceOf(ImpossibleStateError);
255
- continue;
256
- }
257
- constructed += 1;
258
- assertInvariants(built);
259
- // The plan's headline invariant, stated on its own.
260
- expect(isRegistered(built.registration.status) && built.presence === 'absent').toBe(false);
261
- }
262
-
263
- expect(constructed).toBeGreaterThan(0);
264
- expect(rejected).toBeGreaterThan(0);
265
- });
266
- });
@@ -1,120 +0,0 @@
1
- // CellularSnapshot — the whole coherent state of one modem at one revision.
2
- //
3
- // A snapshot composes the identity and all eight orthogonal dimensions plus a
4
- // monotonic `revision`. Every snapshot that exists has passed the guards: the
5
- // constructors here are the ONLY sanctioned way to build or advance one, so an
6
- // impossible combination can never be observed downstream. Revisions strictly
7
- // increase, letting the observer (A3.1) and consumers order and dedupe events.
8
-
9
- import type { Brand } from './brand';
10
- import { nonNegativeInteger } from './brand';
11
- import { RevisionMonotonicityError } from './errors';
12
- import { assertSnapshot } from './guards';
13
- import type { ModemIdentity } from './identity';
14
- import type {
15
- DataInterface,
16
- MmState,
17
- NmActivation,
18
- Presence,
19
- RadioPower,
20
- ReconcileStatus,
21
- RecoveryState,
22
- Registration,
23
- SimSlot,
24
- SourceHealth,
25
- } from './state';
26
-
27
- /** A monotonically increasing snapshot revision. */
28
- export type Revision = Brand<number, 'Revision'>;
29
-
30
- /** Construct a `Revision` from a non-negative integer. */
31
- export function revision(value: number): Revision {
32
- return nonNegativeInteger(value, 'revision') as Revision;
33
- }
34
-
35
- /** The revision every fresh identity starts at. */
36
- export const INITIAL_REVISION: Revision = revision(0);
37
-
38
- /** The next revision after `current`. */
39
- export function nextRevision(current: Revision): Revision {
40
- return (current + 1) as Revision;
41
- }
42
-
43
- /** The full coherent state of one modem at one point in time. */
44
- export interface CellularSnapshot {
45
- readonly identity: ModemIdentity;
46
- readonly presence: Presence;
47
- readonly sourceHealth: SourceHealth;
48
- readonly simSlots: readonly SimSlot[];
49
- readonly radioPower: RadioPower;
50
- readonly mmState: MmState;
51
- readonly registration: Registration;
52
- readonly nmActivation: NmActivation;
53
- readonly dataInterface: DataInterface;
54
- readonly reconcileStatus: ReconcileStatus;
55
- readonly recoveryState: RecoveryState;
56
- readonly revision: Revision;
57
- }
58
-
59
- /** A partial update to a snapshot's dimensions; `revision` is managed, not patched. */
60
- export type SnapshotPatch = Partial<Omit<CellularSnapshot, 'revision'>>;
61
-
62
- /**
63
- * Validate and return a snapshot. The guards run here: an impossible combination
64
- * throws `ImpossibleStateError` rather than producing an incoherent value. This
65
- * is the sole sanctioned constructor for an arbitrary snapshot.
66
- */
67
- export function createSnapshot(fields: CellularSnapshot): CellularSnapshot {
68
- assertSnapshot(fields);
69
- return fields;
70
- }
71
-
72
- /** A valid baseline for a freshly-observed-but-absent modem, at revision 0. */
73
- export function initialSnapshot(identity: ModemIdentity): CellularSnapshot {
74
- return {
75
- identity,
76
- presence: 'absent',
77
- sourceHealth: 'live',
78
- simSlots: [],
79
- radioPower: 'unknown',
80
- mmState: 'unknown',
81
- registration: { status: 'unknown', activeRats: new Set() },
82
- nmActivation: 'unavailable',
83
- dataInterface: { present: false },
84
- reconcileStatus: 'pending',
85
- recoveryState: { stage: 'idle', attempts: 0 },
86
- revision: INITIAL_REVISION,
87
- };
88
- }
89
-
90
- /**
91
- * Apply a dimension patch, bump the revision, and re-validate. Monotonicity is
92
- * automatic (revision always advances by one); an impossible result throws.
93
- */
94
- export function applyTransition(prev: CellularSnapshot, patch: SnapshotPatch): CellularSnapshot {
95
- const next: CellularSnapshot = { ...prev, ...patch, revision: nextRevision(prev.revision) };
96
- assertSnapshot(next);
97
- return next;
98
- }
99
-
100
- /**
101
- * Replace a snapshot with a fully-formed successor that carries its own revision
102
- * (the observer path). Enforces strict monotonicity — a stale or equal revision
103
- * throws `RevisionMonotonicityError` — and validates the successor.
104
- */
105
- export function supersede(prev: CellularSnapshot, next: CellularSnapshot): CellularSnapshot {
106
- if (next.revision <= prev.revision) {
107
- throw new RevisionMonotonicityError(prev.revision, next.revision);
108
- }
109
- assertSnapshot(next);
110
- return next;
111
- }
112
-
113
- /**
114
- * The source (MM daemon / bus) dropped: mark the data `sourceUnavailable` while
115
- * KEEPING presence and all other facts. Stale is never removal — only an
116
- * authoritative snapshot confirms absence (draft §Oracle round-1 lifecycle).
117
- */
118
- export function markSourceUnavailable(prev: CellularSnapshot): CellularSnapshot {
119
- return applyTransition(prev, { sourceHealth: 'sourceUnavailable' });
120
- }
package/src/index.test.ts DELETED
@@ -1,6 +0,0 @@
1
- import { expect, test } from 'bun:test';
2
- import { PACKAGE_NAME } from './index';
3
-
4
- test('control package exposes its name', () => {
5
- expect(PACKAGE_NAME).toBe('@ceralive/modem-control');
6
- });
package/src/index.ts DELETED
@@ -1,15 +0,0 @@
1
- // @ceralive/modem-control — package entry point.
2
- //
3
- // Phase A: the domain model (identity + orthogonal state + revisions) under
4
- // `./domain`, the MM / NM / Router port contracts + desired-state planner under
5
- // `./ports`, the redaction module (`./redact`), and the epoch-scoped ModemManager
6
- // D-Bus observer under `./backend`. The NetworkManager adapter, USB composition-mode
7
- // model, and data-usage sampler land in later waves.
8
-
9
- export const PACKAGE_NAME = '@ceralive/modem-control';
10
-
11
- export * from './backend';
12
- export * from './domain';
13
- export * from './ports';
14
- export * from './redact';
15
- export * from './usb-mode';
@@ -1,61 +0,0 @@
1
- # Port contracts + ownership matrix
2
-
3
- The adapter boundaries `@ceralive/modem-control` reconciles across. Every concrete
4
- backend — the A2.3 fake harness, the A3.x ModemManager D-Bus backend, the A4.1
5
- `nmcli` NetworkManager adapter — implements one of these interfaces. Nothing here
6
- performs I/O; these are pure TypeScript contracts.
7
-
8
- ## The ports
9
-
10
- | Port | File | Responsibility |
11
- |------|------|----------------|
12
- | `ModemObservationPort` | [`observation.ts`](./observation.ts) | Read-only: `start()`, `observe()`, `stop()`; emits discriminated `ObservationList` results that **retain rows** on source failure (removal is only ever an authoritative snapshot omission). |
13
- | `ModemManagerPort` | [`modem-manager.ts`](./modem-manager.ts) | **Extends** the observation port with the mutations MM owns: `setRadioModes`, `setPrimarySimSlot`, `sendPin`/`sendPuk`, `scanNetworks`, `inhibit`/`uninhibit`. **NO bearer/connect verb.** |
14
- | `NetworkManagerPort` | [`network-manager.ts`](./network-manager.ts) | GSM profile CRUD; `activate`/`deactivate` taking **both** `(connectionId, deviceIfname)`; quiesce lease. |
15
- | `RouterPort` | [`router.ts`](./router.ts) | Presence + advisory health only, for devices MM cannot control. |
16
-
17
- ## Ownership matrix — one sole writer per resource
18
-
19
- Each resource below has **exactly one** owner. No other component writes it. This is
20
- the NM-owns-bearers architecture the whole package is built around (independent
21
- review correction, draft §Oracle #1): the controller is a reconciler over two
22
- adapters, never a second writer of the same resource.
23
-
24
- | Resource | Sole writer | Port / owner |
25
- |----------|-------------|--------------|
26
- | APN | NetworkManager | `NetworkManagerPort` (`gsm.apn` / auto-config) |
27
- | Connection auth (username / password) | NetworkManager | `NetworkManagerPort` (`gsm.username` / `gsm.password`) |
28
- | Roaming | NetworkManager | `NetworkManagerPort` (`gsm.home-only`) |
29
- | Autoconnect | NetworkManager | `NetworkManagerPort` (`connection.autoconnect`) |
30
- | Activation (bearer lifecycle) | NetworkManager | `NetworkManagerPort` (`activate`/`deactivate`) |
31
- | Radio access-technology modes | ModemManager | `ModemManagerPort.setRadioModes` |
32
- | SIM operations (PIN / PUK / primary slot / scan) | ModemManager | `ModemManagerPort` |
33
- | Recovery ladder | Local controller | policy `recovery` (disabled by default, A3.4) |
34
- | Usage policy (cycle / threshold) | Local controller | policy `usage` (A4.3 sampler) |
35
-
36
- ### The bearer invariant (safety-critical)
37
-
38
- **The controller NEVER calls MM's `Simple.Connect`, `CreateBearer`, or
39
- `Bearer.Connect`.** Bearers, APN, and connection activation belong exclusively to
40
- NetworkManager. `ModemManagerPort` therefore has no `connect`, `createBearer`, or
41
- `bearerConnect` method, and none may ever be added. This is enforced at build time
42
- by [`forbidden-surface.test.ts`](./forbidden-surface.test.ts), which scans every
43
- port source file and fails if any bearer/connect method declaration appears.
44
-
45
- ## Port-tagged ops
46
-
47
- The planner ([`reconcile.ts`](./reconcile.ts)) emits **port-tagged ops**
48
- ([`ops.ts`](./ops.ts)): a discriminated union `{ port: 'mm', op: MmOp } | { port:
49
- 'nm', op: NmOp }` whose op-kind spaces are disjoint. A radio op tagged for the NM
50
- port — `{ port: 'nm', op: { kind: 'setRadioModes', … } }` — is a **compile-time type
51
- error**, proved by [`ops.type-test.ts`](./ops.type-test.ts) (`@ts-expect-error`
52
- lines that `tsc --noEmit` must flag). The ownership matrix is thus enforced by the
53
- type system, not merely by convention.
54
-
55
- ## Receipts
56
-
57
- Reconciliation is honest: each policy dimension yields exactly one
58
- [`Receipt`](./receipts.ts) with a status (`applied | pending | unsupported |
59
- failed`) and a **reason**. Nothing is silently dropped — "prefer 5G" on a 4G-only
60
- modem returns `unsupported` with an explicit reason, never a quiet downgrade to a
61
- 4G-only mode.