rosotacom-dev 2.5.dev7__tar.gz → 2.5.dev10__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 (338) hide show
  1. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/PKG-INFO +29 -1
  2. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/README.md +28 -0
  3. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/cli.py +260 -28
  4. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom_dev.egg-info/PKG-INFO +29 -1
  5. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom_dev.egg-info/scm_version.json +2 -2
  6. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_cli.py +264 -3
  7. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.devcontainer/devcontainer.json.template +0 -0
  8. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.devcontainer/re_generate_devcontainer_json.py +0 -0
  9. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/CODEOWNERS +0 -0
  10. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/ISSUE_TEMPLATE/documentation-audit.md +0 -0
  11. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/ISSUE_TEMPLATE/implementation-cleanup.md +0 -0
  12. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/ISSUE_TEMPLATE/quality-workflow.md +0 -0
  13. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/ISSUE_TEMPLATE/redesign.md +0 -0
  14. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/ISSUE_TEMPLATE/release-process.md +0 -0
  15. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/ISSUE_TEMPLATE/repository-diagnosis.md +0 -0
  16. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/ISSUE_TEMPLATE/test-ci-audit.md +0 -0
  17. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/ISSUE_TEMPLATE/work-item.md +0 -0
  18. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  19. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/dependabot.yml +0 -0
  20. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/workflows/benchmark-calibrate.yml +0 -0
  21. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/workflows/benchmark-gate.yml +0 -0
  22. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/workflows/dev-release.yml +0 -0
  23. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/workflows/image-scan.yml +0 -0
  24. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/workflows/nightly-e2e.yml +0 -0
  25. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/workflows/pr-lightweight.yml +0 -0
  26. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/workflows/pr-merge-gate.yml +0 -0
  27. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.github/workflows/release.yml +0 -0
  28. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/.pre-commit-config.yaml +0 -0
  29. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/AGENTS.md +0 -0
  30. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/CONCURRENCY.md +0 -0
  31. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/CONTRIBUTING.md +0 -0
  32. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/DEVELOPMENT_PRINCIPLES.md +0 -0
  33. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/LICENSE.txt +0 -0
  34. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/MANIFEST.in +0 -0
  35. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/budgets.jsonl +0 -0
  36. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/calibration/README.md +0 -0
  37. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/calibration/capacity-size-tight-cyclone.json +0 -0
  38. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/calibration/probe-loss-gop-tight-cyclone.json +0 -0
  39. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/calibration/probe-loss-nominal-cyclone.json +0 -0
  40. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/calibration/probe-loss-tight-cyclone.json +0 -0
  41. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/calibration/probe-loss-tight-fastdds.json +0 -0
  42. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/calibration/probe-loss-tight-zenoh.json +0 -0
  43. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/codecov.yml +0 -0
  44. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/deployment-configuration.md +0 -0
  45. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/benchmark-ab.md +0 -0
  46. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/benchmark-bag-as-load.md +0 -0
  47. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/ci.md +0 -0
  48. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/ffmpeg-keyframes.md +0 -0
  49. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/findings/README.md +0 -0
  50. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/findings/cyclone-spdp-discovery-bursts.md +0 -0
  51. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/findings/delay-alone-no-loss.md +0 -0
  52. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/findings/head-of-line-irregularity.md +0 -0
  53. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/findings/jitter-loss-bandwidth-latency.md +0 -0
  54. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/findings/lighter-message-loss.md +0 -0
  55. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/findings/zero-loss-boundaries-18kb-20hz.md +0 -0
  56. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/forensics-report.md +0 -0
  57. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/geo-link-map.md +0 -0
  58. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/github-repository-settings.md +0 -0
  59. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/link-trace.md +0 -0
  60. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/owner-runbook.md +0 -0
  61. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/performance-bands.md +0 -0
  62. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/playbooks/README.md +0 -0
  63. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/quality-model.md +0 -0
  64. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/release-notes/TEMPLATE.md +0 -0
  65. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/release-notes/v2.1.md +0 -0
  66. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/release-notes/v2.2.md +0 -0
  67. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/release-notes/v2.3.md +0 -0
  68. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/release-notes/v2.4.md +0 -0
  69. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/release.md +0 -0
  70. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/rfcs/0001-expectation-driven-test-suite.md +0 -0
  71. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/rfcs/0002-expectation-concepts.md +0 -0
  72. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/rfcs/0003-metric-backbone.md +0 -0
  73. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/rfcs/0004-network-profiles.md +0 -0
  74. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/rfcs/0005-benchmark-genres-and-ci.md +0 -0
  75. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/rfcs/0006-dynamic-qos.md +0 -0
  76. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/rfcs/0007-regression-gate.md +0 -0
  77. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/rfcs/README.md +0 -0
  78. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/testing.md +0 -0
  79. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/docs/work-items.md +0 -0
  80. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/install.sh +0 -0
  81. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/justfile +0 -0
  82. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/media/control_center.png +0 -0
  83. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/media/echo_test.png +0 -0
  84. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/media/forensics-report-example.png +0 -0
  85. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/media/host.png +0 -0
  86. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/media/vehicle.png +0 -0
  87. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/pyproject.toml +0 -0
  88. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/requirements.txt +0 -0
  89. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/rosotacom.yaml +0 -0
  90. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/run_session_in_container.py +0 -0
  91. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/session-configuration.md +0 -0
  92. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/setup.cfg +0 -0
  93. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/__init__.py +0 -0
  94. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/__main__.py +0 -0
  95. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/anonymize.py +0 -0
  96. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/bag_ground_truth.py +0 -0
  97. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/benched_set.py +0 -0
  98. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/benchmark.py +0 -0
  99. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/bundle_check.py +0 -0
  100. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/cli_benchmark.py +0 -0
  101. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/deployment.py +0 -0
  102. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/ffmpeg_packet.py +0 -0
  103. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/forensics.py +0 -0
  104. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/geomap.py +0 -0
  105. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/network_profiles.py +0 -0
  106. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/network_shaper.py +0 -0
  107. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/plots.py +0 -0
  108. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/py.typed +0 -0
  109. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/benched-set.yaml +0 -0
  110. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/.gitignore +0 -0
  111. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/README.md +0 -0
  112. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/__init__.py +0 -0
  113. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +0 -0
  114. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/deployment.example.yaml +0 -0
  115. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/profiles.yaml +0 -0
  116. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/ros2docker.json +0 -0
  117. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/rosotacom.yaml +0 -0
  118. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +0 -0
  119. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +0 -0
  120. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +0 -0
  121. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.ros2docker.json +0 -0
  122. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_communication.sh +0 -0
  123. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_external.py +0 -0
  124. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/wait_for_echo_topic.sh +0 -0
  125. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.ros2docker.json +0 -0
  126. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_communication.sh +0 -0
  127. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_external.py +0 -0
  128. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external.ros2docker.json +0 -0
  129. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external_session.yaml +0 -0
  130. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/foxglove.json +0 -0
  131. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_communication.sh +0 -0
  132. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_external.py +0 -0
  133. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  134. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/metadata.yaml +0 -0
  135. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/external.ros2docker.json +0 -0
  136. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_communication.sh +0 -0
  137. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_external.py +0 -0
  138. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external.ros2docker.json +0 -0
  139. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external_session.yaml +0 -0
  140. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/foxglove.json +0 -0
  141. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_communication.sh +0 -0
  142. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_external.py +0 -0
  143. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  144. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/metadata.yaml +0 -0
  145. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/external.ros2docker.json +0 -0
  146. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_communication.sh +0 -0
  147. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_external.py +0 -0
  148. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external.ros2docker.json +0 -0
  149. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external_session.yaml +0 -0
  150. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_communication.sh +0 -0
  151. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_external.py +0 -0
  152. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external.ros2docker.json +0 -0
  153. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external_session.yaml +0 -0
  154. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_communication.sh +0 -0
  155. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_external.py +0 -0
  156. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external.ros2docker.json +0 -0
  157. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external_session.yaml +0 -0
  158. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_communication.sh +0 -0
  159. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_external.py +0 -0
  160. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external.ros2docker.json +0 -0
  161. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external_session.yaml +0 -0
  162. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_communication.sh +0 -0
  163. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_external.py +0 -0
  164. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +0 -0
  165. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +0 -0
  166. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +0 -0
  167. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +0 -0
  168. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +0 -0
  169. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/15_remote_assist_anonymized_costmap/session-definition.yaml +0 -0
  170. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/16_remote_assist_anonymized_camera/session-definition.yaml +0 -0
  171. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +0 -0
  172. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +0 -0
  173. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +0 -0
  174. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +0 -0
  175. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +0 -0
  176. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/4_comp_occ_grid_zen/session-definition.yaml +0 -0
  177. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +0 -0
  178. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +0 -0
  179. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +0 -0
  180. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +0 -0
  181. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +0 -0
  182. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +0 -0
  183. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/bench_1_2_load_sweep/session-definition.yaml +0 -0
  184. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +0 -0
  185. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +0 -0
  186. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +0 -0
  187. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ros2docker.json.example +0 -0
  188. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/.vscode/c_cpp_properties.json +0 -0
  189. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/.vscode/settings.json +0 -0
  190. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +0 -0
  191. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_initial_peer_list.xml.template +0 -0
  192. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +0 -0
  193. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +0 -0
  194. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +0 -0
  195. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +0 -0
  196. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +0 -0
  197. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +0 -0
  198. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +0 -0
  199. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +0 -0
  200. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/get_ota_xml.py +0 -0
  201. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/utils.py +0 -0
  202. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/zenoh.json5.template +0 -0
  203. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/.gitkeep +0 -0
  204. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +0 -0
  205. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
  206. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +0 -0
  207. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_cpp/package.xml +0 -0
  208. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +0 -0
  209. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +0 -0
  210. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +0 -0
  211. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +0 -0
  212. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +0 -0
  213. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +0 -0
  214. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +0 -0
  215. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +0 -0
  216. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +0 -0
  217. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_msgs/package.xml +0 -0
  218. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +0 -0
  219. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
  220. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/base_bridge_relay.py +0 -0
  221. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +0 -0
  222. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +0 -0
  223. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +0 -0
  224. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +0 -0
  225. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_bandwidth_reducer.py +0 -0
  226. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_pixel_capper.py +0 -0
  227. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +0 -0
  228. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +0 -0
  229. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/costmap_remapper.py +0 -0
  230. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/marker_remapper.py +0 -0
  231. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/speed_test.py +0 -0
  232. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/tf_remapper.py +0 -0
  233. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/traffic_monitor.py +0 -0
  234. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/wifi_network_analysis_1.py +0 -0
  235. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +0 -0
  236. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +0 -0
  237. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/local_global_frame_bridge.py +0 -0
  238. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/localization_map_split_reduce_saver.py +0 -0
  239. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +0 -0
  240. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +0 -0
  241. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pub_sub_pair.py +0 -0
  242. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +0 -0
  243. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +0 -0
  244. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +0 -0
  245. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +0 -0
  246. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +0 -0
  247. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +0 -0
  248. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency_core.py +0 -0
  249. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +0 -0
  250. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview_core.py +0 -0
  251. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/tf_map_to_cart_saver.py +0 -0
  252. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +0 -0
  253. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +0 -0
  254. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +0 -0
  255. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +0 -0
  256. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +0 -0
  257. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_unwrapper.py +0 -0
  258. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_wrapper.py +0 -0
  259. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/package.xml +0 -0
  260. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
  261. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/setup.cfg +0 -0
  262. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/setup.py +0 -0
  263. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +0 -0
  264. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +0 -0
  265. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +0 -0
  266. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +0 -0
  267. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/content/network/up_connectivity.py +0 -0
  268. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +0 -0
  269. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/creation/anonymize_bag.py +0 -0
  270. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/creation/catmux_log_setup.sh +0 -0
  271. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/creation/create_session_yaml.py +0 -0
  272. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/creation/generate_session_files.py +0 -0
  273. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/creation/run_session.py +0 -0
  274. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +0 -0
  275. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/creation/strip_ansi.py +0 -0
  276. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/status_eval.py +0 -0
  277. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/stream_stats.py +0 -0
  278. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/trace_profiles.py +0 -0
  279. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/transit.py +0 -0
  280. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom/video_quality.py +0 -0
  281. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom_dev.egg-info/SOURCES.txt +0 -0
  282. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom_dev.egg-info/dependency_links.txt +0 -0
  283. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom_dev.egg-info/entry_points.txt +0 -0
  284. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom_dev.egg-info/requires.txt +0 -0
  285. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom_dev.egg-info/scm_file_list.json +0 -0
  286. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/src/rosotacom_dev.egg-info/top_level.txt +0 -0
  287. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/stop_session_in_container.py +0 -0
  288. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/terminology.md +0 -0
  289. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/contract/test_benched_set_registry.py +0 -0
  290. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/contract/test_findings.py +0 -0
  291. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/contract/test_markdown_links.py +0 -0
  292. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/contract/test_ota_source_bundle.py +0 -0
  293. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/contract/test_package_metadata.py +0 -0
  294. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/contract/test_packaged_resources.py +0 -0
  295. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/contract/test_profile_covers_generated_commands.py +0 -0
  296. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/contract/test_pytest_policy.py +0 -0
  297. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/contract/test_readme_examples.py +0 -0
  298. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/contract/test_security_maintenance_config.py +0 -0
  299. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/contract/test_workflow_contracts.py +0 -0
  300. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/e2e/conftest.py +0 -0
  301. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/e2e/test_anonymize_e2e.py +0 -0
  302. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/e2e/test_benchmark_ab.py +0 -0
  303. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/e2e/test_benchmark_capacity.py +0 -0
  304. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/e2e/test_benchmark_replay.py +0 -0
  305. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/e2e/test_parallel_smoke.py +0 -0
  306. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/e2e/test_smoke.py +0 -0
  307. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/e2e/test_timeline_stepping.py +0 -0
  308. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/e2e/test_video_quality_e2e.py +0 -0
  309. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/sessions/generate_rmw_matrix.py +0 -0
  310. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-local_zenoh-ros2dds-ota/session-definition.yaml +0 -0
  311. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota/session-definition.yaml +0 -0
  312. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota-tuned/session-definition.yaml +0 -0
  313. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/sessions/rmw_matrix/1_heartbeat_fastdds/session-definition.yaml +0 -0
  314. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/sessions/rmw_matrix/1_heartbeat_zen-endpoints/session-definition.yaml +0 -0
  315. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_anonymize.py +0 -0
  316. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_bag_ground_truth.py +0 -0
  317. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_benched_set.py +0 -0
  318. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_benchmark.py +0 -0
  319. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_bundle_check.py +0 -0
  320. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_cli_benchmark.py +0 -0
  321. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_concurrency.py +0 -0
  322. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_deployment.py +0 -0
  323. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_ffmpeg_packet.py +0 -0
  324. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_forensics.py +0 -0
  325. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_geomap.py +0 -0
  326. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_link_bytes.py +0 -0
  327. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_link_trace.py +0 -0
  328. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_network_profiles.py +0 -0
  329. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_network_shaper.py +0 -0
  330. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_ota_xml.py +0 -0
  331. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_plots.py +0 -0
  332. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_stage_latency.py +0 -0
  333. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_status_eval.py +0 -0
  334. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_status_overview.py +0 -0
  335. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_stream_stats.py +0 -0
  336. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_trace_profiles.py +0 -0
  337. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_transit.py +0 -0
  338. {rosotacom_dev-2.5.dev7 → rosotacom_dev-2.5.dev10}/tests/unit/test_video_quality.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rosotacom-dev
3
- Version: 2.5.dev7
3
+ Version: 2.5.dev10
4
4
  Summary: Host CLI for ROS OTA communication sessions.
5
5
  Author: develNor
6
6
  License-Expression: BSD-3-Clause
@@ -494,6 +494,34 @@ in this mode — there is no staging to undo, and the workdir is your repository
494
494
  The run manifest records which mode and which version the peers ran, so a result
495
495
  can be attributed to an artefact rather than to a working copy.
496
496
 
497
+ #### How the peers are reached: `--peer-exec`
498
+
499
+ `--peer-ssh` gives the orchestrator a shell on the peer, which is more than a
500
+ run needs and more than some machines should hand out. `--peer-exec` names the
501
+ transport instead:
502
+
503
+ ```bash
504
+ rosotacom ota-smoke 2_native_chatter --peer a=workstation --peer b=robot \
505
+ --install-mode checkout \
506
+ --peer-checkout a=/home/me/fleet_mgmt --peer-checkout b=/home/robot/fleet_mgmt \
507
+ --peer-exec b='remote-rosotacom robot'
508
+ ```
509
+
510
+ The command is run with the script as its final argument — the contract
511
+ `ssh host <script>` already has — so anything of that shape can carry a peer: a
512
+ wrapper that logs what a run asks for, a container exec, or a forced command
513
+ that accepts some scripts and refuses the rest. rosotacom does not know or care
514
+ which; it only stops assuming that reaching a peer means having a shell on it.
515
+
516
+ Two restrictions, both load-bearing. It applies to `--install-mode checkout`
517
+ only, because the staging modes push a tar stream and a file write by taking
518
+ the ssh client's own argv apart, and a transport that promises no more than
519
+ "run this script" cannot carry them. And a peer takes either `--peer-ssh` or
520
+ `--peer-exec`, never both: leaving it ambiguous hides which one carried the run.
521
+
522
+ `-t` and `-o BatchMode=yes` are dropped rather than forwarded. They are options
523
+ of one client, and a transport that is not ssh never agreed to understand them.
524
+
497
525
  ### Live status / debugging overview
498
526
 
499
527
  Enable a continuously-updated, per-topic pipeline overview by setting
@@ -449,6 +449,34 @@ in this mode — there is no staging to undo, and the workdir is your repository
449
449
  The run manifest records which mode and which version the peers ran, so a result
450
450
  can be attributed to an artefact rather than to a working copy.
451
451
 
452
+ #### How the peers are reached: `--peer-exec`
453
+
454
+ `--peer-ssh` gives the orchestrator a shell on the peer, which is more than a
455
+ run needs and more than some machines should hand out. `--peer-exec` names the
456
+ transport instead:
457
+
458
+ ```bash
459
+ rosotacom ota-smoke 2_native_chatter --peer a=workstation --peer b=robot \
460
+ --install-mode checkout \
461
+ --peer-checkout a=/home/me/fleet_mgmt --peer-checkout b=/home/robot/fleet_mgmt \
462
+ --peer-exec b='remote-rosotacom robot'
463
+ ```
464
+
465
+ The command is run with the script as its final argument — the contract
466
+ `ssh host <script>` already has — so anything of that shape can carry a peer: a
467
+ wrapper that logs what a run asks for, a container exec, or a forced command
468
+ that accepts some scripts and refuses the rest. rosotacom does not know or care
469
+ which; it only stops assuming that reaching a peer means having a shell on it.
470
+
471
+ Two restrictions, both load-bearing. It applies to `--install-mode checkout`
472
+ only, because the staging modes push a tar stream and a file write by taking
473
+ the ssh client's own argv apart, and a transport that promises no more than
474
+ "run this script" cannot carry them. And a peer takes either `--peer-ssh` or
475
+ `--peer-exec`, never both: leaving it ambiguous hides which one carried the run.
476
+
477
+ `-t` and `-o BatchMode=yes` are dropped rather than forwarded. They are options
478
+ of one client, and a transport that is not ssh never agreed to understand them.
479
+
452
480
  ### Live status / debugging overview
453
481
 
454
482
  Enable a continuously-updated, per-topic pipeline overview by setting
@@ -234,6 +234,19 @@ class OtaSmokePeer:
234
234
  #: control centre runs it under a different account than a laptop does — and
235
235
  #: a single workdir would silently be wrong on all but one of them.
236
236
  checkout: str | None = None
237
+ #: How to reach this peer, when `ssh <alias> <script>` is not allowed to be
238
+ #: the answer. The command is given the script as its final argument, the
239
+ #: same contract ssh has, so anything with that shape can carry a peer:
240
+ #: a wrapper that logs, a container exec, or a capability that accepts only
241
+ #: some scripts and refuses the rest.
242
+ #:
243
+ #: This exists because "give the orchestrator a shell on the peer" is a poor
244
+ #: trade for a machine that is only meant to run one repository's test. An
245
+ #: SSH alias grants everything that account can do, forever, to whoever
246
+ #: holds the key; the operations a run actually needs are a short, fixed
247
+ #: list. Naming the transport lets the narrow thing be substituted without
248
+ #: this file having to know what narrow means.
249
+ exec_command: str | None = None
237
250
 
238
251
 
239
252
  @dataclass(frozen=True)
@@ -1027,6 +1040,10 @@ def _parse_peer_checkout_overrides(overrides: list[str] | None) -> dict[str, str
1027
1040
  return parse_assignments(overrides, option="--peer-checkout")
1028
1041
 
1029
1042
 
1043
+ def _parse_peer_exec_overrides(overrides: list[str] | None) -> dict[str, str]:
1044
+ return parse_assignments(overrides, option="--peer-exec")
1045
+
1046
+
1030
1047
  def _deployment_config(runtime: RuntimeConfig) -> DeploymentConfig | None:
1031
1048
  return load_deployment(runtime.deployment)
1032
1049
 
@@ -1563,8 +1580,31 @@ def _ota_plan_from_bindings(
1563
1580
  install_pin: str | None = None,
1564
1581
  checkouts: Mapping[str, str] | None = None,
1565
1582
  project: str | None = None,
1583
+ exec_commands: Mapping[str, str] | None = None,
1566
1584
  ) -> OtaSmokePlan:
1567
1585
  _ota_validate_install_mode(install_mode)
1586
+ if exec_commands:
1587
+ unknown = sorted(set(exec_commands) - set(bindings))
1588
+ if unknown:
1589
+ raise RuntimeError(f"--peer-exec names a peer the session does not have: {', '.join(unknown)}")
1590
+ # Staging modes send a tar stream and a file write, not a command, and
1591
+ # both take the ssh client's own argv apart to do it. A transport that
1592
+ # only promises "run this script" cannot carry them, and quietly
1593
+ # writing the peer's files onto the orchestrator instead — which is
1594
+ # what the local branch of the staging helpers would do — is the worst
1595
+ # of the available outcomes.
1596
+ if install_mode != "checkout":
1597
+ raise RuntimeError(
1598
+ f"--peer-exec requires --install-mode checkout (got {install_mode!r}): the other modes copy a "
1599
+ "tree to the peer, which a command transport cannot carry."
1600
+ )
1601
+ both = sorted(name for name in exec_commands if bindings[name].ssh)
1602
+ if both:
1603
+ raise RuntimeError(
1604
+ "--peer-exec and --peer-ssh both name "
1605
+ + ", ".join(both)
1606
+ + "; a peer is reached one way, and leaving it ambiguous hides which one carried the run."
1607
+ )
1568
1608
  if install_mode == "checkout":
1569
1609
  if not checkouts or not project:
1570
1610
  raise RuntimeError("--install-mode checkout requires --peer-checkout for every peer.")
@@ -1602,6 +1642,7 @@ def _ota_plan_from_bindings(
1602
1642
  ssh=binding.ssh,
1603
1643
  address=binding.address,
1604
1644
  checkout=(checkouts or {}).get(name) if install_mode == "checkout" else None,
1645
+ exec_command=(exec_commands or {}).get(name),
1605
1646
  )
1606
1647
  for name, binding in bindings.items()
1607
1648
  }
@@ -1619,14 +1660,19 @@ def _ota_plan_from_bindings(
1619
1660
  def _ota_write_state(instance: SessionInstance, plan: OtaSmokePlan) -> OtaSmokePlan:
1620
1661
  path = instance.host_dir / "ota-deployment.yaml"
1621
1662
  payload = {
1622
- "schema_version": 3,
1663
+ "schema_version": 4,
1623
1664
  "workdir": plan.workdir,
1624
1665
  "rosotacom": plan.rosotacom,
1625
1666
  "project": plan.project,
1626
1667
  "install_mode": plan.install_mode,
1627
1668
  "install_pin": plan.install_pin,
1628
1669
  "peers": {
1629
- name: {"address": peer.address, "ssh": peer.ssh, "checkout": peer.checkout}
1670
+ name: {
1671
+ "address": peer.address,
1672
+ "ssh": peer.ssh,
1673
+ "checkout": peer.checkout,
1674
+ "exec": peer.exec_command,
1675
+ }
1630
1676
  for name, peer in sorted(plan.peers.items())
1631
1677
  },
1632
1678
  }
@@ -1637,7 +1683,7 @@ def _ota_write_state(instance: SessionInstance, plan: OtaSmokePlan) -> OtaSmokeP
1637
1683
  def _ota_load_state(path_arg: str) -> OtaSmokePlan:
1638
1684
  path = Path(path_arg).expanduser().resolve()
1639
1685
  raw = _load_yaml_file(path)
1640
- if raw.get("schema_version") != 3:
1686
+ if raw.get("schema_version") != 4:
1641
1687
  raise RuntimeError(f"Unsupported OTA deployment state: {path}")
1642
1688
  peers_raw = raw.get("peers")
1643
1689
  if not isinstance(peers_raw, dict) or len(peers_raw) != 2:
@@ -1651,6 +1697,7 @@ def _ota_load_state(path_arg: str) -> OtaSmokePlan:
1651
1697
  address=str(peer_raw["address"]),
1652
1698
  ssh=str(peer_raw["ssh"]) if peer_raw.get("ssh") else None,
1653
1699
  checkout=str(peer_raw["checkout"]) if peer_raw.get("checkout") else None,
1700
+ exec_command=str(peer_raw["exec"]) if peer_raw.get("exec") else None,
1654
1701
  )
1655
1702
  install_pin = raw.get("install_pin")
1656
1703
  return OtaSmokePlan(
@@ -1682,7 +1729,32 @@ def _ota_quote_cmd(parts: list[str]) -> str:
1682
1729
  return shlex.join(parts)
1683
1730
 
1684
1731
 
1732
+ def _ota_peer_is_remote(peer: OtaSmokePeer) -> bool:
1733
+ """Does this peer run somewhere other than the orchestrator?
1734
+
1735
+ Asked as a question about the peer rather than about SSH. Every caller that
1736
+ used to read ``peer.ssh`` for this meant "is it over there", and each one
1737
+ that kept reading it after a second transport existed would have taken the
1738
+ local branch for a machine on the other side of the room.
1739
+ """
1740
+ return bool(peer.ssh or peer.exec_command)
1741
+
1742
+
1743
+ def _ota_peer_target(peer: OtaSmokePeer) -> str:
1744
+ """How to name this peer's location in output meant for a human."""
1745
+ if peer.exec_command:
1746
+ return peer.exec_command
1747
+ return peer.ssh or "local shell"
1748
+
1749
+
1685
1750
  def _ota_remote_argv(peer: OtaSmokePeer, script: str, *, tty: bool = False, batch: bool = False) -> list[str]:
1751
+ # A named transport wins over the SSH alias, and it takes the script as its
1752
+ # last argument because that is the contract `ssh host <script>` already
1753
+ # has. `tty` and `batch` are dropped on purpose rather than guessed at:
1754
+ # they are options of one particular client, and a transport that is not
1755
+ # ssh has no obligation to understand them.
1756
+ if peer.exec_command:
1757
+ return [*shlex.split(peer.exec_command), script]
1686
1758
  if peer.ssh:
1687
1759
  argv = ["ssh"]
1688
1760
  if batch:
@@ -2194,7 +2266,12 @@ def _ota_write_manifest(
2194
2266
  "install_mode": plan.install_mode,
2195
2267
  "install_pin": plan.install_pin,
2196
2268
  "peers": {
2197
- name: {"ssh_configured": bool(peer.ssh), "address": peer.address} for name, peer in plan.peers.items()
2269
+ name: {
2270
+ "ssh_configured": bool(peer.ssh),
2271
+ "exec_configured": bool(peer.exec_command),
2272
+ "address": peer.address,
2273
+ }
2274
+ for name, peer in plan.peers.items()
2198
2275
  },
2199
2276
  },
2200
2277
  )
@@ -2319,6 +2396,30 @@ def _infer_active_ota_smoke_run(
2319
2396
  )
2320
2397
 
2321
2398
 
2399
+ def _ota_latest_state_for(
2400
+ runtime: RuntimeConfig, target: InteractiveSmokeTarget, instance_id: str | None
2401
+ ) -> str | None:
2402
+ """The newest `ota-deployment.yaml` for this target, if one is on disk.
2403
+
2404
+ Instances are laid out `<instances>/<date>/<session>_<time>_<instance-id>/`,
2405
+ so a named instance can be matched by suffix and an unnamed one by
2406
+ modification time. Returns None rather than raising: not finding a state is
2407
+ a normal outcome (nothing was ever started here), and the caller has a
2408
+ command line to fall back to.
2409
+ """
2410
+ root = _session_instances_root(runtime)
2411
+ if not root.is_dir():
2412
+ return None
2413
+ candidates = sorted(
2414
+ (path for path in root.glob("*/*/ota-deployment.yaml")),
2415
+ key=lambda path: path.stat().st_mtime,
2416
+ reverse=True,
2417
+ )
2418
+ if instance_id:
2419
+ candidates = [path for path in candidates if path.parent.name.endswith(f"_{instance_id}")]
2420
+ return str(candidates[0]) if candidates else None
2421
+
2422
+
2322
2423
  def _resolve_ota_smoke_context(
2323
2424
  args: argparse.Namespace,
2324
2425
  ) -> tuple[RuntimeConfig, OtaSmokePlan, InteractiveSmokeTarget]:
@@ -2327,6 +2428,21 @@ def _resolve_ota_smoke_context(
2327
2428
  getattr(args, "target", None), runtime, getattr(args, "target_type", "auto")
2328
2429
  )
2329
2430
  state_file = getattr(args, "state_file", None)
2431
+ if not state_file and getattr(args, "stop", False):
2432
+ # Stopping has to reach the run that exists, and the only record of how
2433
+ # that run was deployed is the state a start wrote. Reconstructing a
2434
+ # plan from the command line instead means guessing the install mode,
2435
+ # and the default guess is `source` — which points the stop at
2436
+ # `/tmp/rosotacom_ota/source/.venv/bin/rosotacom` for peers that were
2437
+ # started from their own checkouts. Observed on 2026-08-11: every peer
2438
+ # stop failed silently (they are best-effort), the containers stayed up,
2439
+ # and the only visible action was a cleanup removing a staging directory
2440
+ # that had never been created. Usually the live tmux session carries the
2441
+ # state path; when it does not — a killed session, a run started from
2442
+ # another terminal — the instance directory still does.
2443
+ state_file = _ota_latest_state_for(runtime, target, getattr(args, "instance_id", None))
2444
+ if state_file:
2445
+ print(f"OTA smoke: stopping the run recorded in {state_file}")
2330
2446
  if state_file:
2331
2447
  plan = _ota_load_state(state_file)
2332
2448
  else:
@@ -2339,6 +2455,7 @@ def _resolve_ota_smoke_context(
2339
2455
  )
2340
2456
  install_mode = str(getattr(args, "install_mode", None) or "source")
2341
2457
  checkouts = _parse_peer_checkout_overrides(getattr(args, "peer_checkout", None))
2458
+ exec_commands = _parse_peer_exec_overrides(getattr(args, "peer_exec", None))
2342
2459
  project: str | None = None
2343
2460
  if install_mode == "checkout":
2344
2461
  if not runtime.rosotacom_config:
@@ -2346,10 +2463,12 @@ def _resolve_ota_smoke_context(
2346
2463
  local_checkout, project = _ota_checkout_project_path(runtime.rosotacom_config)
2347
2464
  for name, path in sorted(checkouts.items()):
2348
2465
  binding = bindings.get(name)
2349
- if binding is not None and not binding.ssh and Path(path).resolve() != local_checkout:
2466
+ remote = binding is not None and (binding.ssh or name in exec_commands)
2467
+ if binding is not None and not remote and Path(path).resolve() != local_checkout:
2350
2468
  raise RuntimeError(
2351
2469
  f"--peer-checkout {name}={path} names the peer that runs on this machine, but the active "
2352
- f"project lives in {local_checkout}. Point it there, or give that peer an SSH target."
2470
+ f"project lives in {local_checkout}. Point it there, or give that peer a transport "
2471
+ "(--peer-ssh or --peer-exec)."
2353
2472
  )
2354
2473
  plan = _ota_plan_from_bindings(
2355
2474
  bindings,
@@ -2358,6 +2477,7 @@ def _resolve_ota_smoke_context(
2358
2477
  install_pin=getattr(args, "install_pin", None),
2359
2478
  checkouts=checkouts,
2360
2479
  project=project,
2480
+ exec_commands=exec_commands,
2361
2481
  )
2362
2482
  plan_peers = set(plan.peers)
2363
2483
  target_peers = set(_peer_keys_from_cfg(target.cfg))
@@ -2389,7 +2509,7 @@ def _ota_network_sudo_passwords(
2389
2509
  return {peer.name: "" for peer in plan.peers.values()}
2390
2510
  passwords: dict[str, str] = {}
2391
2511
  for peer in plan.peers.values():
2392
- target = peer.ssh or "local shell"
2512
+ target = _ota_peer_target(peer)
2393
2513
  passwords[peer.name] = getpass.getpass(f"sudo password for {peer.name} ({target}) [network shaping only]: ")
2394
2514
  return passwords
2395
2515
 
@@ -2489,8 +2609,8 @@ def _ota_preflight(
2489
2609
  sudo_mode = _validate_ota_sudo_mode(sudo_mode)
2490
2610
  sudo_passwords = sudo_passwords or {}
2491
2611
  for peer in plan.peers.values():
2492
- if peer.ssh:
2493
- _ota_run(peer, "true", label=f"{peer.name}: SSH reachable", dry_run=dry_run, batch=True)
2612
+ if _ota_peer_is_remote(peer):
2613
+ _ota_run(peer, "true", label=f"{peer.name}: reachable", dry_run=dry_run, batch=True)
2494
2614
  required = ["python3", "docker", "tar"]
2495
2615
  if require_tmux:
2496
2616
  required.append("tmux")
@@ -2595,6 +2715,12 @@ def _ota_stage_tree(peer: OtaSmokePeer, source: Path, destination: str, *, dry_r
2595
2715
  "-",
2596
2716
  ".",
2597
2717
  ]
2718
+ # Reached only in the staging install modes, which `--peer-exec` refuses.
2719
+ # Kept as a check rather than a comment because the failure it prevents is
2720
+ # silent: the local branch below would copy the peer's tree onto the
2721
+ # orchestrator and report success.
2722
+ if peer.exec_command:
2723
+ raise RuntimeError(f"Cannot stage files to {peer.name} over a command transport; use --install-mode checkout.")
2598
2724
  if not peer.ssh:
2599
2725
  print(f"+ {label}: copy {source} -> {destination}")
2600
2726
  if not dry_run:
@@ -2616,7 +2742,13 @@ def _ota_stage_tree(peer: OtaSmokePeer, source: Path, destination: str, *, dry_r
2616
2742
 
2617
2743
 
2618
2744
  def _ota_stage_text(peer: OtaSmokePeer, text: str, destination: str, *, dry_run: bool, label: str) -> None:
2619
- print(f"+ {label}: write {destination} on {peer.ssh or 'local host'}")
2745
+ # Reached only in the staging install modes, which `--peer-exec` refuses.
2746
+ # Kept as a check rather than a comment because the failure it prevents is
2747
+ # silent: the local branch below would copy the peer's tree onto the
2748
+ # orchestrator and report success.
2749
+ if peer.exec_command:
2750
+ raise RuntimeError(f"Cannot stage files to {peer.name} over a command transport; use --install-mode checkout.")
2751
+ print(f"+ {label}: write {destination} on {_ota_peer_target(peer)}")
2620
2752
  if dry_run:
2621
2753
  return
2622
2754
  if peer.ssh:
@@ -3159,7 +3291,7 @@ def _ota_verify_isolation(
3159
3291
  published = _ota_run(source, publish, label=f"{source_name}: publish isolation probe", dry_run=dry_run, check=False)
3160
3292
  if published.returncode != 0:
3161
3293
  _ota_print_failure_output(f"{source_name}: publish isolation probe", published)
3162
- errors.append(f"{source_name}: isolation probe publisher failed")
3294
+ errors.append(f"{source_name}: isolation probe did not advertise (probe-publish output above names the cause)")
3163
3295
  return errors
3164
3296
  _print_completed_output(published)
3165
3297
  check = _ota_rosotacom_command(plan, _ota_probe_check_parts(target, receiver_name, peer_args), receiver)
@@ -3594,6 +3726,17 @@ def _list_ota_smoke(args: argparse.Namespace) -> int:
3594
3726
 
3595
3727
 
3596
3728
  def _ota_cleanup_hosts(plan: OtaSmokePlan, *, dry_run: bool) -> None:
3729
+ if plan.state_path is None:
3730
+ # No recorded state means this plan came from the command line, so its
3731
+ # install mode is a default rather than a fact about the run. Removing a
3732
+ # workdir on that basis is a destructive act taken on a guess: it is how
3733
+ # a stop that reached no peer still reported doing something. Say what
3734
+ # is missing instead.
3735
+ print(
3736
+ "OTA smoke: skipping cleanup — no deployment state for this run, so its install mode is unknown. "
3737
+ "Pass --state-file if a workdir really needs removing."
3738
+ )
3739
+ return
3597
3740
  if plan.install_mode == "checkout":
3598
3741
  # The workdir is the user's repository here. `rm -rf` on it would delete
3599
3742
  # a checkout with its untracked work — bags, instance artefacts, local
@@ -6282,8 +6425,17 @@ def _stop_smoke_topic_publishers(containers: dict[str, str], specs: list[SmokeTo
6282
6425
  )
6283
6426
 
6284
6427
 
6428
+ # How long the graph may take to report the probe publisher. A loaded graph
6429
+ # (e.g. a control centre with hundreds of topics) can take well over the old
6430
+ # fixed 10 s to discover a new publisher, and that delay is not a defect.
6431
+ _PROBE_ADVERTISE_TIMEOUT_S = 60.0
6432
+ # The probe must outlive the advertise wait plus the peer's absence check, or
6433
+ # the absence check passes trivially against an already-stopped publisher.
6434
+ _PROBE_DURATION_S = 120.0
6435
+
6436
+
6285
6437
  def _publish_isolation_probe(
6286
- container_name: str, ros_setup: str, topic: str, *, rate: float = 5.0, duration: float = 30.0
6438
+ container_name: str, ros_setup: str, topic: str, *, rate: float = 5.0, duration: float = _PROBE_DURATION_S
6287
6439
  ) -> None:
6288
6440
  """Publish a local-only topic in the container's local application domain,
6289
6441
  detached, for `duration` seconds (self-stops via `timeout`)."""
@@ -6312,6 +6464,45 @@ def _topic_present(container_name: str, ros_setup: str, topic: str) -> bool:
6312
6464
  return topic in names
6313
6465
 
6314
6466
 
6467
+ def _isolation_probe_running(container_name: str, topic: str) -> bool:
6468
+ """Whether the probe publisher for `topic` is still alive in the container."""
6469
+ result = subprocess.run(
6470
+ ["docker", "exec", container_name, "pgrep", "-f", f"ros2 topic pub {topic}"],
6471
+ capture_output=True,
6472
+ text=True,
6473
+ check=False,
6474
+ )
6475
+ return result.returncode == 0
6476
+
6477
+
6478
+ def _wait_for_probe_advertisement(
6479
+ container_name: str,
6480
+ ros_setup: str,
6481
+ topic: str,
6482
+ *,
6483
+ timeout_s: float,
6484
+ log_line: Callable[[str], None] | None = None,
6485
+ ) -> str:
6486
+ """Poll until `topic` is reported by the container's local graph.
6487
+
6488
+ Returns "advertised", "publisher-gone" (the probe process exited or never
6489
+ started), or "timeout" (the publisher is alive but the graph did not report
6490
+ the topic within `timeout_s`). Only "publisher-gone" points at a publisher
6491
+ defect; a loaded graph reports late, which is what "timeout" captures.
6492
+ """
6493
+ deadline = time.monotonic() + timeout_s
6494
+ while True:
6495
+ if log_line is not None:
6496
+ log_line(f"Waiting for isolation probe {topic} to advertise in {container_name}")
6497
+ if _topic_present(container_name, ros_setup, topic):
6498
+ return "advertised"
6499
+ if not _isolation_probe_running(container_name, topic):
6500
+ return "publisher-gone"
6501
+ if time.monotonic() >= deadline:
6502
+ return "timeout"
6503
+ time.sleep(1)
6504
+
6505
+
6315
6506
  def _verify_isolation(
6316
6507
  pub_container: str,
6317
6508
  pub_setup: str,
@@ -6326,15 +6517,18 @@ def _verify_isolation(
6326
6517
  log_line(f"Starting isolation probe {topic} in {pub_container}")
6327
6518
  _publish_isolation_probe(pub_container, pub_setup, topic)
6328
6519
  try:
6329
- live = False
6330
- for _ in range(8):
6331
- log_line(f"Waiting for isolation probe {topic} to advertise in {pub_container}")
6332
- if _topic_present(pub_container, pub_setup, topic):
6333
- live = True
6334
- break
6335
- time.sleep(1)
6336
- if not live:
6337
- return [f"isolation check inconclusive: {topic} never advertised in {pub_container}"]
6520
+ outcome = _wait_for_probe_advertisement(
6521
+ pub_container, pub_setup, topic, timeout_s=_PROBE_ADVERTISE_TIMEOUT_S, log_line=log_line
6522
+ )
6523
+ if outcome == "publisher-gone":
6524
+ return [
6525
+ f"isolation check inconclusive: probe publisher for {topic} exited or never started in {pub_container}"
6526
+ ]
6527
+ if outcome == "timeout":
6528
+ return [
6529
+ f"isolation check inconclusive: probe publisher for {topic} is running in {pub_container} "
6530
+ f"but the graph did not report it within {_PROBE_ADVERTISE_TIMEOUT_S:.0f}s"
6531
+ ]
6338
6532
  log_line(f"Checking that isolation probe {topic} is absent in {check_container}")
6339
6533
  if _topic_present(check_container, check_setup, topic):
6340
6534
  return [f"isolation breach: {topic} from {pub_container} leaked to {check_container}"]
@@ -6466,12 +6660,32 @@ def probe_publish_command(args: argparse.Namespace) -> int:
6466
6660
  # Block until the topic is actually advertised so a caller's subsequent
6467
6661
  # absence check on the other peer is meaningful (not a false pass on a
6468
6662
  # publisher that never came up).
6469
- for _ in range(10):
6470
- if _topic_present(container, ros_setup, args.topic):
6471
- print(f"Publishing {args.topic} in {container} (identity {args.identity}); advertised.")
6472
- return 0
6473
- time.sleep(1)
6474
- print(f"ERROR: {args.topic} did not advertise in {container} (identity {args.identity})", file=sys.stderr)
6663
+ started = time.monotonic()
6664
+ outcome = _wait_for_probe_advertisement(container, ros_setup, args.topic, timeout_s=args.advertise_timeout)
6665
+ if outcome == "advertised":
6666
+ print(f"Publishing {args.topic} in {container} (identity {args.identity}); advertised.")
6667
+ return 0
6668
+ if outcome == "publisher-gone":
6669
+ if time.monotonic() - started >= args.duration:
6670
+ print(
6671
+ f"ERROR: probe publisher for {args.topic} in {container} (identity {args.identity}) ran its "
6672
+ f"{args.duration:.0f}s --duration without the graph reporting the topic; keep --duration "
6673
+ f"above --advertise-timeout",
6674
+ file=sys.stderr,
6675
+ )
6676
+ else:
6677
+ print(
6678
+ f"ERROR: probe publisher for {args.topic} exited or never started in {container} "
6679
+ f"(identity {args.identity})",
6680
+ file=sys.stderr,
6681
+ )
6682
+ else:
6683
+ print(
6684
+ f"ERROR: probe publisher for {args.topic} is running in {container} (identity {args.identity}) "
6685
+ f"but the graph did not report it within {args.advertise_timeout:.0f}s; a loaded graph reports "
6686
+ f"late, retry with a larger --advertise-timeout",
6687
+ file=sys.stderr,
6688
+ )
6475
6689
  return 1
6476
6690
 
6477
6691
 
@@ -8445,6 +8659,18 @@ def main(argv: list[str] | None = None) -> int:
8445
8659
  "Required for every peer in that mode; the same repository is at a different path on each machine."
8446
8660
  ),
8447
8661
  )
8662
+ ota_smoke_parser.add_argument(
8663
+ "--peer-exec",
8664
+ action="append",
8665
+ default=[],
8666
+ metavar="PEER=COMMAND",
8667
+ help=(
8668
+ "Run this peer's commands through COMMAND instead of ssh; the script is appended as the final "
8669
+ "argument, the same contract ssh has. For --install-mode checkout only, and mutually exclusive "
8670
+ "with --peer-ssh for the same peer. Use it to put a peer behind a transport narrower than a "
8671
+ "shell, such as a forced command that accepts only this project's own commands."
8672
+ ),
8673
+ )
8448
8674
  ota_smoke_parser.add_argument("--target-type", choices=["auto", "session", "scenario"], default="auto")
8449
8675
  ota_smoke_parser.add_argument("--interactive", action="store_true", help="Open a local control tmux UI.")
8450
8676
  ota_smoke_parser.add_argument("--stop", action="store_true", help="Stop an OTA smoke run.")
@@ -8907,7 +9133,13 @@ def main(argv: list[str] | None = None) -> int:
8907
9133
  probe_publish_parser.add_argument("--instance-id")
8908
9134
  probe_publish_parser.add_argument("--topic", default=ISOLATION_PROBE_TOPIC)
8909
9135
  probe_publish_parser.add_argument("--rate", type=float, default=5.0)
8910
- probe_publish_parser.add_argument("--duration", type=float, default=30.0)
9136
+ probe_publish_parser.add_argument("--duration", type=float, default=_PROBE_DURATION_S)
9137
+ probe_publish_parser.add_argument(
9138
+ "--advertise-timeout",
9139
+ type=float,
9140
+ default=_PROBE_ADVERTISE_TIMEOUT_S,
9141
+ help="Seconds to wait for the local graph to report the probe topic before failing.",
9142
+ )
8911
9143
  probe_publish_parser.add_argument(
8912
9144
  "--stop", action="store_true", help="Stop a running probe publisher instead of starting one."
8913
9145
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rosotacom-dev
3
- Version: 2.5.dev7
3
+ Version: 2.5.dev10
4
4
  Summary: Host CLI for ROS OTA communication sessions.
5
5
  Author: develNor
6
6
  License-Expression: BSD-3-Clause
@@ -494,6 +494,34 @@ in this mode — there is no staging to undo, and the workdir is your repository
494
494
  The run manifest records which mode and which version the peers ran, so a result
495
495
  can be attributed to an artefact rather than to a working copy.
496
496
 
497
+ #### How the peers are reached: `--peer-exec`
498
+
499
+ `--peer-ssh` gives the orchestrator a shell on the peer, which is more than a
500
+ run needs and more than some machines should hand out. `--peer-exec` names the
501
+ transport instead:
502
+
503
+ ```bash
504
+ rosotacom ota-smoke 2_native_chatter --peer a=workstation --peer b=robot \
505
+ --install-mode checkout \
506
+ --peer-checkout a=/home/me/fleet_mgmt --peer-checkout b=/home/robot/fleet_mgmt \
507
+ --peer-exec b='remote-rosotacom robot'
508
+ ```
509
+
510
+ The command is run with the script as its final argument — the contract
511
+ `ssh host <script>` already has — so anything of that shape can carry a peer: a
512
+ wrapper that logs what a run asks for, a container exec, or a forced command
513
+ that accepts some scripts and refuses the rest. rosotacom does not know or care
514
+ which; it only stops assuming that reaching a peer means having a shell on it.
515
+
516
+ Two restrictions, both load-bearing. It applies to `--install-mode checkout`
517
+ only, because the staging modes push a tar stream and a file write by taking
518
+ the ssh client's own argv apart, and a transport that promises no more than
519
+ "run this script" cannot carry them. And a peer takes either `--peer-ssh` or
520
+ `--peer-exec`, never both: leaving it ambiguous hides which one carried the run.
521
+
522
+ `-t` and `-o BatchMode=yes` are dropped rather than forwarded. They are options
523
+ of one client, and a transport that is not ssh never agreed to understand them.
524
+
497
525
  ### Live status / debugging overview
498
526
 
499
527
  Enable a continuously-updated, per-topic pipeline overview by setting
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "tag": "2.4",
3
- "distance": 7,
4
- "node": "g69ca2b26fe2a31c86e07be8e3aa626ef9a839266",
3
+ "distance": 10,
4
+ "node": "gc0c857f77e0a3a0b17134fdc9e0edb7a70f80a1b",
5
5
  "dirty": false,
6
6
  "branch": "HEAD",
7
7
  "node_date": "2026-08-11"