lager-cli 0.32.0__tar.gz → 0.32.2__tar.gz

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 (202) hide show
  1. {lager_cli-0.32.0/lager_cli.egg-info → lager_cli-0.32.2}/PKG-INFO +3 -3
  2. {lager_cli-0.32.0 → lager_cli-0.32.2}/README.md +2 -2
  3. {lager_cli-0.32.0 → lager_cli-0.32.2}/__init__.py +1 -1
  4. {lager_cli-0.32.0 → lager_cli-0.32.2}/box_storage.py +53 -8
  5. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/box/_host_ops.py +8 -8
  6. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/box/_mount_prep.py +2 -2
  7. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/box/_pip_validation.py +1 -1
  8. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/box/_ssh.py +3 -3
  9. lager_cli-0.32.2/commands/box/box_group.py +36 -0
  10. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/box/config.py +42 -42
  11. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/box/diagnose.py +1 -1
  12. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/box/dut.py +2 -2
  13. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/box/hello.py +2 -2
  14. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/box/instruments.py +1 -1
  15. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/box/net_tui.py +2 -2
  16. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/box/nets.py +5 -9
  17. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/box/ssh.py +1 -1
  18. lager_cli-0.32.0/commands/box/authorize.py → lager_cli-0.32.2/commands/box/ssh_setup.py +21 -4
  19. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/communication/router.py +1 -1
  20. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/communication/uart.py +1 -1
  21. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/communication/usb.py +7 -8
  22. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/power/battery.py +5 -8
  23. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/power/supply.py +4 -7
  24. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/utility/__init__.py +4 -3
  25. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/utility/binaries.py +4 -4
  26. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/utility/install.py +10 -10
  27. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/utility/login.py +35 -0
  28. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/utility/uninstall.py +3 -3
  29. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/utility/update.py +6 -6
  30. {lager_cli-0.32.0 → lager_cli-0.32.2}/core/net_group.py +1 -1
  31. {lager_cli-0.32.0 → lager_cli-0.32.2}/core/net_helpers.py +36 -16
  32. {lager_cli-0.32.0 → lager_cli-0.32.2}/errors.py +1 -1
  33. {lager_cli-0.32.0 → lager_cli-0.32.2}/gateway_auth.py +53 -8
  34. {lager_cli-0.32.0 → lager_cli-0.32.2/lager_cli.egg-info}/PKG-INFO +3 -3
  35. {lager_cli-0.32.0 → lager_cli-0.32.2}/lager_cli.egg-info/SOURCES.txt +2 -2
  36. {lager_cli-0.32.0 → lager_cli-0.32.2}/main.py +13 -4
  37. {lager_cli-0.32.0 → lager_cli-0.32.2}/tests/test_gateway_auth.py +83 -0
  38. lager_cli-0.32.0/commands/box/box_group.py +0 -21
  39. {lager_cli-0.32.0 → lager_cli-0.32.2}/LICENSE +0 -0
  40. {lager_cli-0.32.0 → lager_cli-0.32.2}/MANIFEST.in +0 -0
  41. {lager_cli-0.32.0 → lager_cli-0.32.2}/__main__.py +0 -0
  42. {lager_cli-0.32.0 → lager_cli-0.32.2}/address_utils.py +0 -0
  43. {lager_cli-0.32.0 → lager_cli-0.32.2}/battery/__init__.py +0 -0
  44. {lager_cli-0.32.0 → lager_cli-0.32.2}/battery/battery_tui.py +0 -0
  45. {lager_cli-0.32.0 → lager_cli-0.32.2}/battery/websocket_client.py +0 -0
  46. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/__init__.py +0 -0
  47. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/box/__init__.py +0 -0
  48. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/box/_shim_verbs.py +0 -0
  49. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/box/boxes.py +0 -0
  50. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/box/labjack_pins.py +0 -0
  51. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/box/lock.py +0 -0
  52. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/communication/__init__.py +0 -0
  53. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/communication/ble.py +0 -0
  54. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/communication/blufi.py +0 -0
  55. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/communication/i2c.py +0 -0
  56. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/communication/spi.py +0 -0
  57. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/communication/websocket_client.py +0 -0
  58. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/communication/wifi.py +0 -0
  59. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/development/__init__.py +0 -0
  60. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/development/arm.py +0 -0
  61. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/development/debug/__init__.py +0 -0
  62. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/development/debug/commands.py +0 -0
  63. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/development/debug/gdb.py +0 -0
  64. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/development/debug/net_cache.py +0 -0
  65. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/development/debug/service_client.py +0 -0
  66. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/development/debug/service_helper.py +0 -0
  67. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/development/debug/tunnel.py +0 -0
  68. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/development/devenv.py +0 -0
  69. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/development/python.py +0 -0
  70. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/measurement/__init__.py +0 -0
  71. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/measurement/adc.py +0 -0
  72. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/measurement/dac.py +0 -0
  73. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/measurement/energy.py +0 -0
  74. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/measurement/gpi.py +0 -0
  75. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/measurement/gpo.py +0 -0
  76. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/measurement/logic.py +0 -0
  77. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/measurement/scope.py +0 -0
  78. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/measurement/thermocouple.py +0 -0
  79. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/measurement/watt.py +0 -0
  80. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/power/__init__.py +0 -0
  81. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/power/battery_model_csv.py +0 -0
  82. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/power/eload.py +0 -0
  83. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/power/solar.py +0 -0
  84. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/utility/defaults.py +0 -0
  85. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/utility/exec_.py +0 -0
  86. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/utility/install_wheel.py +0 -0
  87. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/utility/logs.py +0 -0
  88. {lager_cli-0.32.0 → lager_cli-0.32.2}/commands/utility/webcam.py +0 -0
  89. {lager_cli-0.32.0 → lager_cli-0.32.2}/config.py +0 -0
  90. {lager_cli-0.32.0 → lager_cli-0.32.2}/context/__init__.py +0 -0
  91. {lager_cli-0.32.0 → lager_cli-0.32.2}/context/ci_detection.py +0 -0
  92. {lager_cli-0.32.0 → lager_cli-0.32.2}/context/constants.py +0 -0
  93. {lager_cli-0.32.0 → lager_cli-0.32.2}/context/core.py +0 -0
  94. {lager_cli-0.32.0 → lager_cli-0.32.2}/context/error_handlers.py +0 -0
  95. {lager_cli-0.32.0 → lager_cli-0.32.2}/context/session.py +0 -0
  96. {lager_cli-0.32.0 → lager_cli-0.32.2}/core/__init__.py +0 -0
  97. {lager_cli-0.32.0 → lager_cli-0.32.2}/core/group_usage.py +0 -0
  98. {lager_cli-0.32.0 → lager_cli-0.32.2}/core/matchers.py +0 -0
  99. {lager_cli-0.32.0 → lager_cli-0.32.2}/core/net_storage.py +0 -0
  100. {lager_cli-0.32.0 → lager_cli-0.32.2}/core/param_types.py +0 -0
  101. {lager_cli-0.32.0 → lager_cli-0.32.2}/core/ssh_utils.py +0 -0
  102. {lager_cli-0.32.0 → lager_cli-0.32.2}/core/utils.py +0 -0
  103. {lager_cli-0.32.0 → lager_cli-0.32.2}/core/version_skew.py +0 -0
  104. {lager_cli-0.32.0 → lager_cli-0.32.2}/core/ws_diagnose.py +0 -0
  105. {lager_cli-0.32.0 → lager_cli-0.32.2}/deployment/__init__.py +0 -0
  106. {lager_cli-0.32.0 → lager_cli-0.32.2}/deployment/scripts/__init__.py +0 -0
  107. {lager_cli-0.32.0 → lager_cli-0.32.2}/deployment/scripts/configure_docker_dns.py +0 -0
  108. {lager_cli-0.32.0 → lager_cli-0.32.2}/deployment/scripts/configure_docker_dns.sh +0 -0
  109. {lager_cli-0.32.0 → lager_cli-0.32.2}/deployment/scripts/convert_to_sparse_checkout.sh +0 -0
  110. {lager_cli-0.32.0 → lager_cli-0.32.2}/deployment/scripts/setup_and_deploy_box.sh +0 -0
  111. {lager_cli-0.32.0 → lager_cli-0.32.2}/deployment/scripts/setup_ssh_key.sh +0 -0
  112. {lager_cli-0.32.0 → lager_cli-0.32.2}/deployment/security/__init__.py +0 -0
  113. {lager_cli-0.32.0 → lager_cli-0.32.2}/deployment/security/secure_box_firewall.sh +0 -0
  114. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/__init__.py +0 -0
  115. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/common/__init__.py +0 -0
  116. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/common/construct_utils.py +0 -0
  117. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/common/exceptions.py +0 -0
  118. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/common/py3compat.py +0 -0
  119. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/common/utils.py +0 -0
  120. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/construct/__init__.py +0 -0
  121. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/construct/adapters.py +0 -0
  122. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/construct/core.py +0 -0
  123. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/construct/debug.py +0 -0
  124. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/construct/macros.py +0 -0
  125. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/dwarf/__init__.py +0 -0
  126. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/dwarf/abbrevtable.py +0 -0
  127. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/dwarf/aranges.py +0 -0
  128. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/dwarf/callframe.py +0 -0
  129. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/dwarf/compileunit.py +0 -0
  130. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/dwarf/constants.py +0 -0
  131. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/dwarf/descriptions.py +0 -0
  132. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/dwarf/die.py +0 -0
  133. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/dwarf/dwarf_expr.py +0 -0
  134. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/dwarf/dwarfinfo.py +0 -0
  135. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/dwarf/enums.py +0 -0
  136. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/dwarf/lineprogram.py +0 -0
  137. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/dwarf/locationlists.py +0 -0
  138. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/dwarf/namelut.py +0 -0
  139. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/dwarf/ranges.py +0 -0
  140. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/dwarf/structs.py +0 -0
  141. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/ehabi/__init__.py +0 -0
  142. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/ehabi/constants.py +0 -0
  143. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/ehabi/decoder.py +0 -0
  144. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/ehabi/ehabiinfo.py +0 -0
  145. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/ehabi/structs.py +0 -0
  146. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/elf/__init__.py +0 -0
  147. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/elf/constants.py +0 -0
  148. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/elf/descriptions.py +0 -0
  149. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/elf/dynamic.py +0 -0
  150. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/elf/elffile.py +0 -0
  151. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/elf/enums.py +0 -0
  152. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/elf/gnuversions.py +0 -0
  153. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/elf/hash.py +0 -0
  154. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/elf/notes.py +0 -0
  155. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/elf/relocation.py +0 -0
  156. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/elf/sections.py +0 -0
  157. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/elf/segments.py +0 -0
  158. {lager_cli-0.32.0 → lager_cli-0.32.2}/elftools/elf/structs.py +0 -0
  159. {lager_cli-0.32.0 → lager_cli-0.32.2}/exceptions.py +0 -0
  160. {lager_cli-0.32.0 → lager_cli-0.32.2}/impl/__init__.py +0 -0
  161. {lager_cli-0.32.0 → lager_cli-0.32.2}/impl/box_config.py +0 -0
  162. {lager_cli-0.32.0 → lager_cli-0.32.2}/impl/measurement/__init__.py +0 -0
  163. {lager_cli-0.32.0 → lager_cli-0.32.2}/impl/measurement/scope.py +0 -0
  164. {lager_cli-0.32.0 → lager_cli-0.32.2}/impl/measurement/scope_stream.py +0 -0
  165. {lager_cli-0.32.0 → lager_cli-0.32.2}/impl/power/__init__.py +0 -0
  166. {lager_cli-0.32.0 → lager_cli-0.32.2}/impl/power/enable_disable.py +0 -0
  167. {lager_cli-0.32.0 → lager_cli-0.32.2}/lager_cli.egg-info/dependency_links.txt +0 -0
  168. {lager_cli-0.32.0 → lager_cli-0.32.2}/lager_cli.egg-info/entry_points.txt +0 -0
  169. {lager_cli-0.32.0 → lager_cli-0.32.2}/lager_cli.egg-info/requires.txt +0 -0
  170. {lager_cli-0.32.0 → lager_cli-0.32.2}/lager_cli.egg-info/top_level.txt +0 -0
  171. {lager_cli-0.32.0 → lager_cli-0.32.2}/pyproject.toml +0 -0
  172. {lager_cli-0.32.0 → lager_cli-0.32.2}/safe_unpickle.py +0 -0
  173. {lager_cli-0.32.0 → lager_cli-0.32.2}/setup.cfg +0 -0
  174. {lager_cli-0.32.0 → lager_cli-0.32.2}/setup.py +0 -0
  175. {lager_cli-0.32.0 → lager_cli-0.32.2}/simple_hdlc.py +0 -0
  176. {lager_cli-0.32.0 → lager_cli-0.32.2}/sort_utils.py +0 -0
  177. {lager_cli-0.32.0 → lager_cli-0.32.2}/status.py +0 -0
  178. {lager_cli-0.32.0 → lager_cli-0.32.2}/supply/__init__.py +0 -0
  179. {lager_cli-0.32.0 → lager_cli-0.32.2}/supply/supply_tui.py +0 -0
  180. {lager_cli-0.32.0 → lager_cli-0.32.2}/supply/websocket_client.py +0 -0
  181. {lager_cli-0.32.0 → lager_cli-0.32.2}/terminal/__init__.py +0 -0
  182. {lager_cli-0.32.0 → lager_cli-0.32.2}/terminal/core/__init__.py +0 -0
  183. {lager_cli-0.32.0 → lager_cli-0.32.2}/terminal/core/executor.py +0 -0
  184. {lager_cli-0.32.0 → lager_cli-0.32.2}/terminal/main.py +0 -0
  185. {lager_cli-0.32.0 → lager_cli-0.32.2}/terminal/ui/__init__.py +0 -0
  186. {lager_cli-0.32.0 → lager_cli-0.32.2}/terminal/ui/completer.py +0 -0
  187. {lager_cli-0.32.0 → lager_cli-0.32.2}/terminal/ui/display.py +0 -0
  188. {lager_cli-0.32.0 → lager_cli-0.32.2}/terminal/ui/logo.py +0 -0
  189. {lager_cli-0.32.0 → lager_cli-0.32.2}/terminal/ui/repl.py +0 -0
  190. {lager_cli-0.32.0 → lager_cli-0.32.2}/terminal/ui/themes.py +0 -0
  191. {lager_cli-0.32.0 → lager_cli-0.32.2}/tests/test_box_lager_imports.py +0 -0
  192. {lager_cli-0.32.0 → lager_cli-0.32.2}/tests/test_box_storage.py +0 -0
  193. {lager_cli-0.32.0 → lager_cli-0.32.2}/tests/test_io_imports.py +0 -0
  194. {lager_cli-0.32.0 → lager_cli-0.32.2}/update_check.py +0 -0
  195. {lager_cli-0.32.0 → lager_cli-0.32.2}/vendor/PyCRC/CRC16.py +0 -0
  196. {lager_cli-0.32.0 → lager_cli-0.32.2}/vendor/PyCRC/CRC16DNP.py +0 -0
  197. {lager_cli-0.32.0 → lager_cli-0.32.2}/vendor/PyCRC/CRC16Kermit.py +0 -0
  198. {lager_cli-0.32.0 → lager_cli-0.32.2}/vendor/PyCRC/CRC16SICK.py +0 -0
  199. {lager_cli-0.32.0 → lager_cli-0.32.2}/vendor/PyCRC/CRC32.py +0 -0
  200. {lager_cli-0.32.0 → lager_cli-0.32.2}/vendor/PyCRC/CRCCCITT.py +0 -0
  201. {lager_cli-0.32.0 → lager_cli-0.32.2}/vendor/PyCRC/__init__.py +0 -0
  202. {lager_cli-0.32.0 → lager_cli-0.32.2}/vendor/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: lager-cli
3
- Version: 0.32.0
3
+ Version: 0.32.2
4
4
  Summary: Lager CLI - Box and Docker connectivity
5
5
  Home-page: https://github.com/lagerdata/lager
6
6
  Author: Lager Data
@@ -274,10 +274,10 @@ lager devenv terminal
274
274
  ### Package Management
275
275
  ```bash
276
276
  # Add a package to the box's declarative config
277
- lager box config pip add numpy
277
+ lager box-config pip add numpy
278
278
 
279
279
  # Apply the change (restarts the lager container, runs pip install)
280
- lager box config apply
280
+ lager box-config apply
281
281
  ```
282
282
 
283
283
  ## Supported Hardware
@@ -221,10 +221,10 @@ lager devenv terminal
221
221
  ### Package Management
222
222
  ```bash
223
223
  # Add a package to the box's declarative config
224
- lager box config pip add numpy
224
+ lager box-config pip add numpy
225
225
 
226
226
  # Apply the change (restarts the lager container, runs pip install)
227
- lager box config apply
227
+ lager box-config apply
228
228
  ```
229
229
 
230
230
  ## Supported Hardware
@@ -7,4 +7,4 @@ Lager CLI
7
7
  A Command Line Interface for Lager Data
8
8
  """
9
9
 
10
- __version__ = '0.32.0'
10
+ __version__ = '0.32.2'
@@ -458,7 +458,7 @@ def _check_box_lock(ip, box_name):
458
458
 
459
459
  try:
460
460
  resp = requests.get(f'http://{ip}:9000/lock', timeout=3, **_gateway_kwargs(ip))
461
- _check_gateway(resp, ip)
461
+ resp = _check_gateway(resp, ip)
462
462
  if resp.status_code == 404:
463
463
  # :9000 answered but has no /lock route: the box image predates
464
464
  # the lock API on :9000. Locks can't be enforced against it, so
@@ -581,12 +581,57 @@ def _gateway_kwargs(ip):
581
581
  return {'headers': headers} if headers else {}
582
582
 
583
583
 
584
+ def _resend_with_auth(prepared, headers):
585
+ """Re-send an already-prepared request with extra headers merged in.
586
+ Returns the new response, or None if the resend itself failed."""
587
+ import requests
588
+ req = prepared.copy()
589
+ for key, value in headers.items():
590
+ req.headers[key] = value
591
+ try:
592
+ return requests.Session().send(req, timeout=30, stream=False)
593
+ except requests.RequestException:
594
+ return None
595
+
596
+
584
597
  def _check_gateway(resp, ip):
585
- """Turn a gateway denial into the actionable `lager login` error and
586
- record the box→auth-server mapping so the next call authenticates."""
587
- from .gateway_auth import DISCOVERY_HEADER, handle_gateway_denial
588
- if resp.status_code in (401, 403, 503) and DISCOVERY_HEADER in resp.headers:
589
- handle_gateway_denial(resp, ip)
598
+ """Resolve a gateway response, returning the response the caller should use.
599
+
600
+ On a plain (un-gated) box this is a passthrough. On a gated box:
601
+
602
+ - First contact — we sent no token (the box→auth-server link is only
603
+ learned from this very 401's discovery header) but we already hold a
604
+ session for that server: record the mapping, attach the token, and
605
+ retry the request once. The caller gets the authenticated response and
606
+ never sees the round trip. A plain box never receives the token,
607
+ because only a gateway sends the discovery header.
608
+ - Genuine denials (revoked session, no access grant, auth server down)
609
+ raise the actionable `lager login` / "ask your admin" error as before.
610
+
611
+ Callers should adopt the return value: ``resp = _check_gateway(resp, ip)``.
612
+ """
613
+ from .gateway_auth import (
614
+ DISCOVERY_HEADER, handle_gateway_denial,
615
+ record_box_auth_server, auth_headers_for_box,
616
+ )
617
+ if not (resp.status_code in (401, 403, 503) and DISCOVERY_HEADER in resp.headers):
618
+ return resp
619
+
620
+ sent_auth = 'Authorization' in getattr(resp.request, 'headers', {})
621
+ if resp.status_code == 401 and not sent_auth:
622
+ record_box_auth_server(ip, resp.headers[DISCOVERY_HEADER])
623
+ headers = auth_headers_for_box(ip)
624
+ if headers:
625
+ retried = _resend_with_auth(resp.request, headers)
626
+ if retried is not None:
627
+ gated = (retried.status_code in (401, 403, 503)
628
+ and DISCOVERY_HEADER in retried.headers)
629
+ if not gated:
630
+ return retried # transparently authenticated
631
+ resp = retried # still refused — report on the retry
632
+
633
+ handle_gateway_denial(resp, ip) # raises the actionable error
634
+ return resp
590
635
 
591
636
 
592
637
  def _lock_url(ip, suffix=''):
@@ -637,7 +682,7 @@ def acquire_box_lock(
637
682
  # the pre-existing lock as freshly acquired — and release it on exit.
638
683
  try:
639
684
  pre = requests.get(_lock_url(ip), timeout=5, **_gateway_kwargs(ip))
640
- _check_gateway(pre, ip)
685
+ pre = _check_gateway(pre, ip)
641
686
  if pre.status_code == 200:
642
687
  try:
643
688
  pre_data = pre.json()
@@ -663,7 +708,7 @@ def acquire_box_lock(
663
708
  while True:
664
709
  try:
665
710
  resp = requests.post(_lock_url(ip), json=payload, timeout=5, **_gateway_kwargs(ip))
666
- _check_gateway(resp, ip)
711
+ resp = _check_gateway(resp, ip)
667
712
  except requests.exceptions.RequestException as exc:
668
713
  if not quiet:
669
714
  click.secho(
@@ -2,7 +2,7 @@
2
2
  # SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  """
5
- Host-side SSH helpers for `lager box config apply`:
5
+ Host-side SSH helpers for `lager box-config apply`:
6
6
  - apt_install: `sudo apt-get install -y` over SSH
7
7
  - sysctl_apply: write /etc/sysctl.d/99-lager-box-config.conf + sysctl --system
8
8
 
@@ -25,7 +25,7 @@ from ._ssh import SshRunner, default_ssh_runner, sudo_error_message
25
25
 
26
26
  SYSCTL_CONF_PATH = "/etc/sysctl.d/99-lager-box-config.conf"
27
27
  _SYSCTL_HEADER = (
28
- "# Managed by `lager box config sysctl`; manual edits are overwritten on apply.\n"
28
+ "# Managed by `lager box-config sysctl`; manual edits are overwritten on apply.\n"
29
29
  )
30
30
 
31
31
  # User udev rules live in their own file so they never collide with the
@@ -37,13 +37,13 @@ UDEV_RULES_DIR = "/etc/udev/rules.d/"
37
37
  UDEV_RULES_PATH = UDEV_RULES_DIR + UDEV_RULES_FILENAME
38
38
  _UDEV_TMP_PATH = "/tmp/" + UDEV_RULES_FILENAME
39
39
  _UDEV_HEADER = (
40
- "# Managed by `lager box config udev`; manual edits are overwritten on apply.\n"
40
+ "# Managed by `lager box-config udev`; manual edits are overwritten on apply.\n"
41
41
  )
42
42
 
43
43
  # --- Box-config sudoers rule: single source of truth ------------------------
44
44
  #
45
45
  # `lager install` and `lager update` write this rule to BOXCFG_SUDOERS_PATH
46
- # so `lager box config apply` can run apt-get/sysctl/mkdir/chown over
46
+ # so `lager box-config apply` can run apt-get/sysctl/mkdir/chown over
47
47
  # BatchMode SSH. The rule must name the actual login user: it used to
48
48
  # hardcode `lagerdata`, so on boxes with a different login user the grant
49
49
  # never matched — install ended with "Sudoers file installed
@@ -69,7 +69,7 @@ def is_valid_unix_username(user: Optional[str]) -> bool:
69
69
 
70
70
 
71
71
  def boxcfg_sudoers_rules(user: str = "lagerdata") -> List[str]:
72
- """The NOPASSWD rule lines for `lager box config apply`. tee/rm/sysctl
72
+ """The NOPASSWD rule lines for `lager box-config apply`. tee/rm/sysctl
73
73
  are path-scoped so a compromised account cannot escalate via them;
74
74
  apt-get and mkdir/chown are unscoped because the package list and host
75
75
  paths are user-defined. SETENV on apt-get is required so
@@ -127,7 +127,7 @@ def udev_sudoers_bootstrap(user: str = "lagerdata") -> str:
127
127
  " SUDOERS\n"
128
128
  " sudo chmod 440 /etc/sudoers.d/lagerdata-udev\n"
129
129
  "\n"
130
- "Then re-run `lager box config apply`."
130
+ "Then re-run `lager box-config apply`."
131
131
  )
132
132
 
133
133
 
@@ -150,7 +150,7 @@ def sudoers_bootstrap(user: str = "lagerdata") -> str:
150
150
  f" sudo chmod 440 {BOXCFG_SUDOERS_PATH}\n"
151
151
  f" sudo touch {BOXCFG_SUDOERS_MARKER} && sudo chmod 644 {BOXCFG_SUDOERS_MARKER}\n"
152
152
  "\n"
153
- "Then re-run `lager box config apply`. tee/rm/sysctl are path-scoped so a "
153
+ "Then re-run `lager box-config apply`. tee/rm/sysctl are path-scoped so a "
154
154
  f"compromised {user} account cannot escalate to root via them; apt-get "
155
155
  "and mkdir/chown are unscoped because the package list and host paths are "
156
156
  "user-defined. SETENV on apt-get is required so "
@@ -264,7 +264,7 @@ def render_udev_rules_file(rules: List[Dict[str, object]]) -> str:
264
264
  vid = str(r.get("vid", ""))
265
265
  pid = str(r.get("pid", ""))
266
266
  mode = str(r.get("mode", "0660"))
267
- body += f"# vid:pid {vid}:{pid} (added via `lager box config udev`)\n"
267
+ body += f"# vid:pid {vid}:{pid} (added via `lager box-config udev`)\n"
268
268
  body += (
269
269
  f'SUBSYSTEM=="usb", ATTRS{{idVendor}}=="{vid}", '
270
270
  f'ATTRS{{idProduct}}=="{pid}", MODE="{mode}", GROUP="lager"\n'
@@ -2,7 +2,7 @@
2
2
  # SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  """
5
- Auto-prep helper for `lager box config` mounts.
5
+ Auto-prep helper for `lager box-config` mounts.
6
6
 
7
7
  The lager container runs as uid 33 (www-data). For every host->container
8
8
  bind mount, the host path must (a) exist and (b) for read-write mounts,
@@ -49,7 +49,7 @@ def sudoers_bootstrap(user: str = "lagerdata") -> str:
49
49
  " | sudo tee /etc/sudoers.d/lager-box-config\n"
50
50
  " sudo chmod 440 /etc/sudoers.d/lager-box-config\n"
51
51
  "\n"
52
- "Then re-run `lager box config mount add ...` (or `apply`). The "
52
+ "Then re-run `lager box-config mount add ...` (or `apply`). The "
53
53
  "rule is narrow-scoped (mkdir + chown only)."
54
54
  )
55
55
 
@@ -2,7 +2,7 @@
2
2
  # SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  """
5
- Host-side helpers for `lager box config pip add`:
5
+ Host-side helpers for `lager box-config pip add`:
6
6
  - PyPI existence check (best-effort, network-error tolerant)
7
7
 
8
8
  Format validation lives box-side only (see box/lager/box_config/config.py).
@@ -2,7 +2,7 @@
2
2
  # SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  """
5
- Shared SSH transport for `lager box config` host-side operations.
5
+ Shared SSH transport for `lager box-config` host-side operations.
6
6
 
7
7
  A single default runner (with optional stdin), and a parameterized
8
8
  sudo-error helper. Callers (mount prep, apply-time host ops, container
@@ -23,7 +23,7 @@ from ...errors import LagerError
23
23
  SshRunner = Callable[..., Tuple[int, str, str]]
24
24
 
25
25
  # Dedicated key used by `lager install` / `lager update`. When present,
26
- # it's tried first for all `lager box config` SSH calls — on boxes
26
+ # it's tried first for all `lager box-config` SSH calls — on boxes
27
27
  # provisioned by lager it's the only key authorized for the box user.
28
28
  # Passing `-i` replaces ssh's default identity list, so when this key
29
29
  # is NOT authorized for the box's user (customer-managed users), a key
@@ -46,7 +46,7 @@ def ensure_lager_box_keypair(key_path: str = _LAGER_BOX_KEY) -> bool:
46
46
 
47
47
  Returns True if a new key was generated, False if one already existed.
48
48
  Raises :class:`LagerError` if ssh-keygen fails. This is the single
49
- definition of how the lager_box key is created — ``lager authorize``
49
+ definition of how the lager_box key is created — ``lager ssh-setup``
50
50
  and ``lager update`` both call it, so the key type and comment can
51
51
  never drift apart between the two provisioning paths.
52
52
  """
@@ -0,0 +1,36 @@
1
+ # Copyright 2024-2026 Lager Data
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """
5
+ Deprecated `lager box` group. The subcommands moved to top level —
6
+ `lager box config` became `lager box-config`, and `lager box dut`
7
+ became `lager dut`. This hidden group keeps the old spellings working
8
+ with a deprecation warning until they are removed.
9
+ """
10
+ import click
11
+
12
+ from .config import box_config
13
+ from .dut import box_dut
14
+ from ...core.group_usage import LagerGroup
15
+
16
+ _RENAMES = {
17
+ "config": "lager box-config",
18
+ "dut": "lager dut",
19
+ }
20
+
21
+
22
+ @click.group(name="box", cls=LagerGroup, hidden=True)
23
+ @click.pass_context
24
+ def box(ctx: click.Context) -> None:
25
+ """Deprecated aliases for `lager box-config` and `lager dut`."""
26
+ new = _RENAMES.get(ctx.invoked_subcommand)
27
+ if new:
28
+ click.secho(
29
+ f"DEPRECATED: `lager box {ctx.invoked_subcommand}` is now `{new}`. "
30
+ "The old spelling still works but will be removed in a future release.",
31
+ fg="yellow", err=True,
32
+ )
33
+
34
+
35
+ box.add_command(box_config, name="config")
36
+ box.add_command(box_dut, name="dut")
@@ -2,7 +2,7 @@
2
2
  # SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  """
5
- `lager box config` — declarative per-box provisioning.
5
+ `lager box-config` — declarative per-box provisioning.
6
6
 
7
7
  Reads/writes /etc/lager/box_config.json on the box (via the
8
8
  in-container shim cli/impl/box_config.py) and ships an `apply` verb
@@ -141,7 +141,7 @@ def _list_field(
141
141
  ) -> None:
142
142
  """Resolve box, fetch `show`, pluck `key`, print formatted entries.
143
143
 
144
- Shared by every `lager box config <group> list` command. List-valued
144
+ Shared by every `lager box-config <group> list` command. List-valued
145
145
  fields (mounts/volumes/pip_packages/...) iterate items; the one dict
146
146
  field (sysctl) iterates key/value pairs — the formatter takes a tuple
147
147
  in that case, mirroring the diff printer's convention.
@@ -166,7 +166,7 @@ def _list_field(
166
166
  click.echo(formatter(item))
167
167
 
168
168
 
169
- @click.group(name="config", cls=LagerGroup, invoke_without_command=True, help="Manage declarative box provisioning")
169
+ @click.group(name="box-config", cls=LagerGroup, invoke_without_command=True, help="Manage declarative box provisioning")
170
170
  @click.option("--box", help="Lagerbox name or IP")
171
171
  @click.pass_context
172
172
  def box_config(ctx: click.Context, box: Optional[str]) -> None:
@@ -200,7 +200,7 @@ def show_cmd(
200
200
  if i > 0:
201
201
  click.echo()
202
202
  click.secho(
203
- f"No box_config.json on {resolved}. Run `lager box config init`.",
203
+ f"No box_config.json on {resolved}. Run `lager box-config init`.",
204
204
  fg="yellow",
205
205
  )
206
206
  continue
@@ -495,7 +495,7 @@ def init_cmd(ctx: click.Context, box: Optional[str], force: bool) -> None:
495
495
  + ", ".join(imported),
496
496
  fg="green",
497
497
  )
498
- click.echo("Run `lager box config apply` to (re)install them in the container.")
498
+ click.echo("Run `lager box-config apply` to (re)install them in the container.")
499
499
  skipped = payload.get("skipped") or []
500
500
  if skipped:
501
501
  click.secho(f"Skipped {len(skipped)} legacy package(s):", fg="yellow")
@@ -653,7 +653,7 @@ def diff_cmd(ctx: click.Context, box: Optional[str], as_json: bool) -> None:
653
653
  def status_cmd(ctx: click.Context, box: Optional[str], as_json: bool) -> None:
654
654
  """Quick health check: clean vs. drifted, field counts, last audit entry.
655
655
 
656
- Designed for `lager box config status --box <BOX>,<BOX>,<BOX>` —
656
+ Designed for `lager box-config status --box <BOX>,<BOX>,<BOX>` —
657
657
  one line of signal per box without needing to read full `show` output.
658
658
  """
659
659
  targets = _resolve_boxes(ctx, box)
@@ -694,7 +694,7 @@ def _print_status_human(s: dict) -> None:
694
694
  state = click.style("clean", fg="green") if s["clean"] else click.style("DRIFT", fg="yellow")
695
695
  click.echo(f"{box}: {state}")
696
696
  if not s["clean"]:
697
- click.echo(f" run `lager box config diff --box {box}` to see pending changes")
697
+ click.echo(f" run `lager box-config diff --box {box}` to see pending changes")
698
698
  nonzero = {k: n for k, n in s["counts"].items() if n}
699
699
  if nonzero:
700
700
  # Compact rendering: "2 mounts, 3 pip, 1 cargo"
@@ -758,7 +758,7 @@ def import_cmd(ctx: click.Context, path: str, box: Optional[str], yes: bool) ->
758
758
  _print_errors(response.get("errors") or [response.get("error", "unknown error")])
759
759
  ctx.exit(1)
760
760
  click.secho(f"Imported {path} to {resolved}.", fg="green")
761
- click.echo("Run `lager box config apply` to put the new config into effect.")
761
+ click.echo("Run `lager box-config apply` to put the new config into effect.")
762
762
 
763
763
 
764
764
  @box_config.command("edit", help="Open the box's config in $EDITOR for live editing.")
@@ -827,7 +827,7 @@ def edit_cmd(ctx: click.Context, box: Optional[str]) -> None:
827
827
  )
828
828
  if response.get("ok"):
829
829
  click.secho(f"Saved on {resolved}.", fg="green")
830
- click.echo("Run `lager box config apply` to put the new config into effect.")
830
+ click.echo("Run `lager box-config apply` to put the new config into effect.")
831
831
  return
832
832
  click.secho("Config has errors:", fg="red", err=True)
833
833
  _print_errors(response.get("errors") or [])
@@ -883,7 +883,7 @@ def copy_cmd(ctx: click.Context, src: str, dst: str, yes: bool) -> None:
883
883
  click.secho(
884
884
  f"Copied config from {src_resolved} to {dst_resolved}.", fg="green",
885
885
  )
886
- click.echo(f"Run `lager box config apply --box {dst}` to put the new config into effect.")
886
+ click.echo(f"Run `lager box-config apply --box {dst}` to put the new config into effect.")
887
887
 
888
888
 
889
889
  @box_config.command(
@@ -897,11 +897,11 @@ def repair_cmd(ctx: click.Context, box: Optional[str], yes: bool) -> None:
897
897
  """Recover a box whose box_config.json is broken or whose container
898
898
  is wedged on a bad config.
899
899
 
900
- Does what `lager box config apply`'s rollback path does automatically
900
+ Does what `lager box-config apply`'s rollback path does automatically
901
901
  when a bounce fails — but exposed as a manual verb for situations
902
902
  that don't trigger automatic rollback:
903
903
  - Operator hand-edited /etc/lager/box_config.json into invalid JSON
904
- (bypassing `lager box config edit`'s validation)
904
+ (bypassing `lager box-config edit`'s validation)
905
905
  - First-apply bounce failed AND there's no snapshot yet (rollback
906
906
  can't fire) — in that case this command also exits with no-snapshot
907
907
  - Container is up but the renderer soft-failed on a stale config and
@@ -925,9 +925,9 @@ def repair_cmd(ctx: click.Context, box: Optional[str], yes: bool) -> None:
925
925
  if rc != 0:
926
926
  click.secho(
927
927
  f"No applied snapshot on {resolved}; cannot repair. This box has "
928
- "never had a successful `lager box config apply`. Manual fix: ssh "
928
+ "never had a successful `lager box-config apply`. Manual fix: ssh "
929
929
  "in, delete or hand-edit /etc/lager/box_config.json, then re-run "
930
- "`lager box config init`.",
930
+ "`lager box-config init`.",
931
931
  fg="red", err=True,
932
932
  )
933
933
  ctx.exit(1)
@@ -1084,7 +1084,7 @@ def reset_cmd(ctx: click.Context, box: Optional[str], yes: bool, do_apply: bool)
1084
1084
  # config, even if it happened to already be empty.
1085
1085
  ctx.invoke(apply_cmd, box=box, yes=True, force=True)
1086
1086
  else:
1087
- click.echo("Run `lager box config apply` to restart the container on the empty config.")
1087
+ click.echo("Run `lager box-config apply` to restart the container on the empty config.")
1088
1088
 
1089
1089
 
1090
1090
  @box_config.command(
@@ -1153,7 +1153,7 @@ def _apply_one(
1153
1153
  payload = _parse_response(raw, ctx)
1154
1154
  if not payload.get("exists", True):
1155
1155
  click.secho(
1156
- f"No box_config.json on {resolved}. Run `lager box config init` first.",
1156
+ f"No box_config.json on {resolved}. Run `lager box-config init` first.",
1157
1157
  fg="yellow",
1158
1158
  )
1159
1159
  return False
@@ -1254,7 +1254,7 @@ def _apply_one(
1254
1254
  click.secho(
1255
1255
  f"Box config was NOT applied on {resolved} (the container is up and "
1256
1256
  "running the previous config). The applied-hash was left unchanged, "
1257
- "so `lager box config apply` will retry this config once the box is "
1257
+ "so `lager box-config apply` will retry this config once the box is "
1258
1258
  "repaired — typically with `lager update --box <BOX>`.",
1259
1259
  fg="red",
1260
1260
  err=True,
@@ -1274,7 +1274,7 @@ def _apply_one(
1274
1274
  click.secho(
1275
1275
  "Rolled back to the previously applied config; the new config "
1276
1276
  "was rejected. The container is up on the previous config — fix "
1277
- "/etc/lager/box_config.json and re-run `lager box config apply`.",
1277
+ "/etc/lager/box_config.json and re-run `lager box-config apply`.",
1278
1278
  fg="yellow",
1279
1279
  err=True,
1280
1280
  )
@@ -1342,7 +1342,7 @@ def _post_apply_consistency_ok(
1342
1342
  )
1343
1343
  _print_errors(post_validate.get("errors") or [])
1344
1344
  click.secho(
1345
- "Not updating applied-hash. Fix the file and re-run `lager box config apply`.",
1345
+ "Not updating applied-hash. Fix the file and re-run `lager box-config apply`.",
1346
1346
  fg="yellow",
1347
1347
  err=True,
1348
1348
  )
@@ -1356,7 +1356,7 @@ def _post_apply_consistency_ok(
1356
1356
  "Warning: box_config.json was modified during apply. Container is "
1357
1357
  "running with the snapshot captured at the start of apply; the "
1358
1358
  "latest on-disk changes have NOT been applied. Re-run "
1359
- "`lager box config apply` to pick them up.",
1359
+ "`lager box-config apply` to pick them up.",
1360
1360
  fg="yellow",
1361
1361
  err=True,
1362
1362
  )
@@ -1602,7 +1602,7 @@ def _preflight_mounts(ctx: click.Context, resolved: str, *, recursive: bool) ->
1602
1602
  "Warning: could not verify mount host paths over SSH; continuing with "
1603
1603
  "apply. If the container fails to start or a mounted path is missing "
1604
1604
  "or unwritable, fix SSH access to the box host (see above) and re-run "
1605
- "`lager box config apply`.",
1605
+ "`lager box-config apply`.",
1606
1606
  fg="yellow",
1607
1607
  err=True,
1608
1608
  )
@@ -1686,7 +1686,7 @@ def _bounce_container_rc(ctx: click.Context, resolved_box: str) -> int:
1686
1686
  click.secho(
1687
1687
  f"SSH command timed out after {_BOUNCE_TIMEOUT_SECONDS // 60} minutes. "
1688
1688
  "start_box.sh may still be running on the box (cargo build, etc.); "
1689
- "re-run `lager box config apply` once it finishes.",
1689
+ "re-run `lager box-config apply` once it finishes.",
1690
1690
  fg="red", err=True,
1691
1691
  )
1692
1692
  return _BOUNCE_FAILED
@@ -1823,7 +1823,7 @@ def mount_add_cmd(
1823
1823
  # let apply's pre-flight re-check once SSH access is fixed.
1824
1824
  click.secho(f"Warning: {prep_result.message}", fg="yellow", err=True)
1825
1825
  click.secho(
1826
- "Adding the mount to box_config.json anyway; `lager box config "
1826
+ "Adding the mount to box_config.json anyway; `lager box-config "
1827
1827
  "apply` re-checks the host path before restarting the container.",
1828
1828
  fg="yellow",
1829
1829
  )
@@ -1868,7 +1868,7 @@ def mount_add_cmd(
1868
1868
  else:
1869
1869
  click.secho(f"Host path: {prep_result.message}", fg="green")
1870
1870
 
1871
- click.echo("Run `lager box config apply` to restart the container.")
1871
+ click.echo("Run `lager box-config apply` to restart the container.")
1872
1872
 
1873
1873
 
1874
1874
  @mount_group.command("remove", help="Remove a bind mount by host+container path.")
@@ -1892,7 +1892,7 @@ def mount_remove_cmd(
1892
1892
  payload = _parse_response(raw, ctx)
1893
1893
  if payload.get("removed"):
1894
1894
  click.secho(f"Removed mount {host} -> {container} on {resolved}.", fg="green")
1895
- click.echo("Run `lager box config apply` to restart the container.")
1895
+ click.echo("Run `lager box-config apply` to restart the container.")
1896
1896
  else:
1897
1897
  click.secho(f"No matching mount on {resolved}.", fg="yellow")
1898
1898
 
@@ -1933,7 +1933,7 @@ def volume_add_cmd(
1933
1933
  _print_errors(payload.get("errors") or [payload.get("error", "unknown error")])
1934
1934
  ctx.exit(1)
1935
1935
  click.secho(f"Added volume {name} -> {container} on {resolved}.", fg="green")
1936
- click.echo("Run `lager box config apply` to restart the container.")
1936
+ click.echo("Run `lager box-config apply` to restart the container.")
1937
1937
 
1938
1938
 
1939
1939
  @volume_group.command("remove", help="Remove a named docker volume by name.")
@@ -1955,7 +1955,7 @@ def volume_remove_cmd(
1955
1955
  payload = _parse_response(raw, ctx)
1956
1956
  if payload.get("removed"):
1957
1957
  click.secho(f"Removed volume {name} on {resolved}.", fg="green")
1958
- click.echo("Run `lager box config apply` to restart the container.")
1958
+ click.echo("Run `lager box-config apply` to restart the container.")
1959
1959
  else:
1960
1960
  click.secho(f"No volume named {name} on {resolved}.", fg="yellow")
1961
1961
 
@@ -2022,7 +2022,7 @@ def pip_add_cmd(
2022
2022
  ctx.exit(1)
2023
2023
  added = payload.get("added") or list(packages)
2024
2024
  click.secho(f"Added {len(added)} pip package(s) on {resolved}: " + ", ".join(added), fg="green")
2025
- click.echo("Run `lager box config apply` to install them in the container.")
2025
+ click.echo("Run `lager box-config apply` to install them in the container.")
2026
2026
 
2027
2027
 
2028
2028
  @pip_group.command("remove", help="Remove one or more pip packages from the box config.")
@@ -2040,7 +2040,7 @@ def pip_remove_cmd(ctx: click.Context, packages: tuple, box: Optional[str]) -> N
2040
2040
  removed = payload.get("removed") or []
2041
2041
  if removed:
2042
2042
  click.secho(f"Removed {len(removed)} pip package(s): " + ", ".join(removed), fg="green")
2043
- click.echo("Run `lager box config apply` to update the running container.")
2043
+ click.echo("Run `lager box-config apply` to update the running container.")
2044
2044
  else:
2045
2045
  click.secho("No matching pip packages were configured.", fg="yellow")
2046
2046
 
@@ -2063,7 +2063,7 @@ def pip_import_legacy_cmd(ctx: click.Context, box: Optional[str]) -> None:
2063
2063
  skipped = payload.get("skipped") or []
2064
2064
  if imported:
2065
2065
  click.secho(f"Imported {len(imported)} package(s): " + ", ".join(imported), fg="green")
2066
- click.echo("Run `lager box config apply` to (re)install them.")
2066
+ click.echo("Run `lager box-config apply` to (re)install them.")
2067
2067
  else:
2068
2068
  click.secho("No new packages to import.", fg="yellow")
2069
2069
  if skipped:
@@ -2109,7 +2109,7 @@ def apt_add_cmd(ctx: click.Context, packages: tuple, box: Optional[str]) -> None
2109
2109
  ctx.exit(1)
2110
2110
  added = payload.get("added") or list(packages)
2111
2111
  click.secho(f"Added {len(added)} apt package(s) on {resolved}: " + ", ".join(added), fg="green")
2112
- click.echo("Run `lager box config apply` to install them on the box host.")
2112
+ click.echo("Run `lager box-config apply` to install them on the box host.")
2113
2113
 
2114
2114
 
2115
2115
  @apt_group.command("remove", help="Remove one or more apt packages from the box config.")
@@ -2128,7 +2128,7 @@ def apt_remove_cmd(ctx: click.Context, packages: tuple, box: Optional[str]) -> N
2128
2128
  if removed:
2129
2129
  click.secho(f"Removed {len(removed)} apt package(s): " + ", ".join(removed), fg="green")
2130
2130
  click.echo(
2131
- "Run `lager box config apply` to record the change. Note: existing "
2131
+ "Run `lager box-config apply` to record the change. Note: existing "
2132
2132
  "installs are not auto-uninstalled."
2133
2133
  )
2134
2134
  else:
@@ -2199,7 +2199,7 @@ def udev_add_cmd(
2199
2199
  ctx.exit(1)
2200
2200
  added = payload.get("added") or [f"{r['vid']}:{r['pid']}" for r in rules]
2201
2201
  click.secho(f"Added {len(added)} udev rule(s) on {resolved}: " + ", ".join(added), fg="green")
2202
- click.echo("Run `lager box config apply` to install them on the box host.")
2202
+ click.echo("Run `lager box-config apply` to install them on the box host.")
2203
2203
 
2204
2204
 
2205
2205
  @udev_group.command("remove", help="Remove udev rules by VID:PID.")
@@ -2218,7 +2218,7 @@ def udev_remove_cmd(ctx: click.Context, devices: tuple, box: Optional[str]) -> N
2218
2218
  removed = payload.get("removed") or []
2219
2219
  if removed:
2220
2220
  click.secho(f"Removed {len(removed)} udev rule(s): " + ", ".join(removed), fg="green")
2221
- click.echo("Run `lager box config apply` to update udev on the box host.")
2221
+ click.echo("Run `lager box-config apply` to update udev on the box host.")
2222
2222
  else:
2223
2223
  click.secho("No matching udev rules were configured.", fg="yellow")
2224
2224
 
@@ -2281,7 +2281,7 @@ def sysctl_set_cmd(ctx: click.Context, entries: tuple, box: Optional[str]) -> No
2281
2281
  f"Set {len(set_keys)} sysctl key(s) on {resolved}: " + ", ".join(set_keys),
2282
2282
  fg="green",
2283
2283
  )
2284
- click.echo("Run `lager box config apply` to write /etc/sysctl.d/ and reload.")
2284
+ click.echo("Run `lager box-config apply` to write /etc/sysctl.d/ and reload.")
2285
2285
 
2286
2286
 
2287
2287
  @sysctl_group.command("unset", help="Remove one or more sysctl keys from the box config.")
@@ -2299,7 +2299,7 @@ def sysctl_unset_cmd(ctx: click.Context, keys: tuple, box: Optional[str]) -> Non
2299
2299
  removed = payload.get("removed") or []
2300
2300
  if removed:
2301
2301
  click.secho(f"Removed {len(removed)} sysctl key(s): " + ", ".join(removed), fg="green")
2302
- click.echo("Run `lager box config apply` to update /etc/sysctl.d/ and reload.")
2302
+ click.echo("Run `lager box-config apply` to update /etc/sysctl.d/ and reload.")
2303
2303
  else:
2304
2304
  click.secho("No matching sysctl keys were configured.", fg="yellow")
2305
2305
 
@@ -2362,7 +2362,7 @@ def env_set_cmd(ctx: click.Context, entries: tuple, box: Optional[str]) -> None:
2362
2362
  f"Set {len(set_keys)} env var(s) on {resolved}: " + ", ".join(set_keys),
2363
2363
  fg="green",
2364
2364
  )
2365
- click.echo("Run `lager box config apply` to put the new env vars into effect.")
2365
+ click.echo("Run `lager box-config apply` to put the new env vars into effect.")
2366
2366
 
2367
2367
 
2368
2368
  @env_group.command("unset", help="Remove one or more env vars from the box config.")
@@ -2380,7 +2380,7 @@ def env_unset_cmd(ctx: click.Context, keys: tuple, box: Optional[str]) -> None:
2380
2380
  removed = payload.get("removed") or []
2381
2381
  if removed:
2382
2382
  click.secho(f"Removed {len(removed)} env var(s): " + ", ".join(removed), fg="green")
2383
- click.echo("Run `lager box config apply` to update the running container.")
2383
+ click.echo("Run `lager box-config apply` to update the running container.")
2384
2384
  else:
2385
2385
  click.secho("No matching env vars were configured.", fg="yellow")
2386
2386
 
@@ -2420,7 +2420,7 @@ def cargo_add_cmd(ctx: click.Context, packages: tuple, box: Optional[str]) -> No
2420
2420
  ctx.exit(1)
2421
2421
  added = payload.get("added") or list(packages)
2422
2422
  click.secho(f"Added {len(added)} cargo crate(s) on {resolved}: " + ", ".join(added), fg="green")
2423
- click.echo("Run `lager box config apply` to install them in the container.")
2423
+ click.echo("Run `lager box-config apply` to install them in the container.")
2424
2424
 
2425
2425
 
2426
2426
  @cargo_group.command("remove", help="Remove one or more cargo crates from the box config.")
@@ -2439,7 +2439,7 @@ def cargo_remove_cmd(ctx: click.Context, packages: tuple, box: Optional[str]) ->
2439
2439
  if removed:
2440
2440
  click.secho(f"Removed {len(removed)} cargo crate(s): " + ", ".join(removed), fg="green")
2441
2441
  click.echo(
2442
- "Run `lager box config apply` to record the change. Note: existing "
2442
+ "Run `lager box-config apply` to record the change. Note: existing "
2443
2443
  "installs in the container are not auto-uninstalled."
2444
2444
  )
2445
2445
  else:
@@ -2481,7 +2481,7 @@ def npm_add_cmd(ctx: click.Context, packages: tuple, box: Optional[str]) -> None
2481
2481
  ctx.exit(1)
2482
2482
  added = payload.get("added") or list(packages)
2483
2483
  click.secho(f"Added {len(added)} npm package(s) on {resolved}: " + ", ".join(added), fg="green")
2484
- click.echo("Run `lager box config apply` to install them in the container.")
2484
+ click.echo("Run `lager box-config apply` to install them in the container.")
2485
2485
 
2486
2486
 
2487
2487
  @npm_group.command("remove", help="Remove one or more npm packages from the box config.")
@@ -2500,7 +2500,7 @@ def npm_remove_cmd(ctx: click.Context, packages: tuple, box: Optional[str]) -> N
2500
2500
  if removed:
2501
2501
  click.secho(f"Removed {len(removed)} npm package(s): " + ", ".join(removed), fg="green")
2502
2502
  click.echo(
2503
- "Run `lager box config apply` to record the change. Note: existing "
2503
+ "Run `lager box-config apply` to record the change. Note: existing "
2504
2504
  "installs in the container are not auto-uninstalled."
2505
2505
  )
2506
2506
  else: