rosotacom-dev 2.5.dev8__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.dev8 → rosotacom_dev-2.5.dev10}/PKG-INFO +29 -1
  2. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/README.md +28 -0
  3. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/cli.py +210 -28
  4. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom_dev.egg-info/PKG-INFO +29 -1
  5. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom_dev.egg-info/scm_version.json +2 -2
  6. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_cli.py +227 -2
  7. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.devcontainer/devcontainer.json.template +0 -0
  8. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.devcontainer/re_generate_devcontainer_json.py +0 -0
  9. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/CODEOWNERS +0 -0
  10. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/ISSUE_TEMPLATE/documentation-audit.md +0 -0
  11. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/ISSUE_TEMPLATE/implementation-cleanup.md +0 -0
  12. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/ISSUE_TEMPLATE/quality-workflow.md +0 -0
  13. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/ISSUE_TEMPLATE/redesign.md +0 -0
  14. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/ISSUE_TEMPLATE/release-process.md +0 -0
  15. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/ISSUE_TEMPLATE/repository-diagnosis.md +0 -0
  16. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/ISSUE_TEMPLATE/test-ci-audit.md +0 -0
  17. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/ISSUE_TEMPLATE/work-item.md +0 -0
  18. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  19. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/dependabot.yml +0 -0
  20. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/workflows/benchmark-calibrate.yml +0 -0
  21. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/workflows/benchmark-gate.yml +0 -0
  22. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/workflows/dev-release.yml +0 -0
  23. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/workflows/image-scan.yml +0 -0
  24. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/workflows/nightly-e2e.yml +0 -0
  25. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/workflows/pr-lightweight.yml +0 -0
  26. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/workflows/pr-merge-gate.yml +0 -0
  27. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.github/workflows/release.yml +0 -0
  28. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/.pre-commit-config.yaml +0 -0
  29. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/AGENTS.md +0 -0
  30. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/CONCURRENCY.md +0 -0
  31. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/CONTRIBUTING.md +0 -0
  32. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/DEVELOPMENT_PRINCIPLES.md +0 -0
  33. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/LICENSE.txt +0 -0
  34. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/MANIFEST.in +0 -0
  35. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/budgets.jsonl +0 -0
  36. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/calibration/README.md +0 -0
  37. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/calibration/capacity-size-tight-cyclone.json +0 -0
  38. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/calibration/probe-loss-gop-tight-cyclone.json +0 -0
  39. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/calibration/probe-loss-nominal-cyclone.json +0 -0
  40. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/calibration/probe-loss-tight-cyclone.json +0 -0
  41. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/calibration/probe-loss-tight-fastdds.json +0 -0
  42. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/calibration/probe-loss-tight-zenoh.json +0 -0
  43. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/codecov.yml +0 -0
  44. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/deployment-configuration.md +0 -0
  45. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/benchmark-ab.md +0 -0
  46. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/benchmark-bag-as-load.md +0 -0
  47. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/ci.md +0 -0
  48. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/ffmpeg-keyframes.md +0 -0
  49. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/findings/README.md +0 -0
  50. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/findings/cyclone-spdp-discovery-bursts.md +0 -0
  51. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/findings/delay-alone-no-loss.md +0 -0
  52. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/findings/head-of-line-irregularity.md +0 -0
  53. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/findings/jitter-loss-bandwidth-latency.md +0 -0
  54. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/findings/lighter-message-loss.md +0 -0
  55. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/findings/zero-loss-boundaries-18kb-20hz.md +0 -0
  56. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/forensics-report.md +0 -0
  57. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/geo-link-map.md +0 -0
  58. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/github-repository-settings.md +0 -0
  59. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/link-trace.md +0 -0
  60. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/owner-runbook.md +0 -0
  61. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/performance-bands.md +0 -0
  62. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/playbooks/README.md +0 -0
  63. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/quality-model.md +0 -0
  64. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/release-notes/TEMPLATE.md +0 -0
  65. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/release-notes/v2.1.md +0 -0
  66. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/release-notes/v2.2.md +0 -0
  67. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/release-notes/v2.3.md +0 -0
  68. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/release-notes/v2.4.md +0 -0
  69. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/release.md +0 -0
  70. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/rfcs/0001-expectation-driven-test-suite.md +0 -0
  71. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/rfcs/0002-expectation-concepts.md +0 -0
  72. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/rfcs/0003-metric-backbone.md +0 -0
  73. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/rfcs/0004-network-profiles.md +0 -0
  74. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/rfcs/0005-benchmark-genres-and-ci.md +0 -0
  75. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/rfcs/0006-dynamic-qos.md +0 -0
  76. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/rfcs/0007-regression-gate.md +0 -0
  77. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/rfcs/README.md +0 -0
  78. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/testing.md +0 -0
  79. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/docs/work-items.md +0 -0
  80. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/install.sh +0 -0
  81. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/justfile +0 -0
  82. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/media/control_center.png +0 -0
  83. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/media/echo_test.png +0 -0
  84. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/media/forensics-report-example.png +0 -0
  85. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/media/host.png +0 -0
  86. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/media/vehicle.png +0 -0
  87. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/pyproject.toml +0 -0
  88. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/requirements.txt +0 -0
  89. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/rosotacom.yaml +0 -0
  90. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/run_session_in_container.py +0 -0
  91. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/session-configuration.md +0 -0
  92. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/setup.cfg +0 -0
  93. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/__init__.py +0 -0
  94. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/__main__.py +0 -0
  95. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/anonymize.py +0 -0
  96. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/bag_ground_truth.py +0 -0
  97. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/benched_set.py +0 -0
  98. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/benchmark.py +0 -0
  99. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/bundle_check.py +0 -0
  100. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/cli_benchmark.py +0 -0
  101. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/deployment.py +0 -0
  102. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/ffmpeg_packet.py +0 -0
  103. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/forensics.py +0 -0
  104. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/geomap.py +0 -0
  105. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/network_profiles.py +0 -0
  106. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/network_shaper.py +0 -0
  107. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/plots.py +0 -0
  108. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/py.typed +0 -0
  109. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/benched-set.yaml +0 -0
  110. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/.gitignore +0 -0
  111. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/README.md +0 -0
  112. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/__init__.py +0 -0
  113. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +0 -0
  114. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/deployment.example.yaml +0 -0
  115. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/profiles.yaml +0 -0
  116. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/ros2docker.json +0 -0
  117. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/rosotacom.yaml +0 -0
  118. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +0 -0
  119. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +0 -0
  120. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +0 -0
  121. {rosotacom_dev-2.5.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +0 -0
  165. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +0 -0
  166. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +0 -0
  167. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +0 -0
  168. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +0 -0
  169. {rosotacom_dev-2.5.dev8 → 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.dev8 → 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.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +0 -0
  172. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +0 -0
  173. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +0 -0
  174. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +0 -0
  175. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +0 -0
  176. {rosotacom_dev-2.5.dev8 → 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.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +0 -0
  178. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +0 -0
  179. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +0 -0
  180. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +0 -0
  181. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +0 -0
  182. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +0 -0
  183. {rosotacom_dev-2.5.dev8 → 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.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +0 -0
  185. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +0 -0
  186. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +0 -0
  187. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ros2docker.json.example +0 -0
  188. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/.vscode/c_cpp_properties.json +0 -0
  189. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/.vscode/settings.json +0 -0
  190. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +0 -0
  191. {rosotacom_dev-2.5.dev8 → 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.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +0 -0
  193. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +0 -0
  194. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +0 -0
  195. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +0 -0
  196. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +0 -0
  197. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +0 -0
  198. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +0 -0
  199. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +0 -0
  200. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/get_ota_xml.py +0 -0
  201. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/utils.py +0 -0
  202. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ota_configs/zenoh.json5.template +0 -0
  203. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/.gitkeep +0 -0
  204. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +0 -0
  205. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
  206. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +0 -0
  207. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_cpp/package.xml +0 -0
  208. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +0 -0
  209. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +0 -0
  210. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +0 -0
  211. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +0 -0
  212. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +0 -0
  213. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +0 -0
  214. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +0 -0
  215. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +0 -0
  216. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +0 -0
  217. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_msgs/package.xml +0 -0
  218. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +0 -0
  219. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
  220. {rosotacom_dev-2.5.dev8 → 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.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +0 -0
  222. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +0 -0
  223. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +0 -0
  224. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +0 -0
  225. {rosotacom_dev-2.5.dev8 → 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.dev8 → 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.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +0 -0
  228. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +0 -0
  229. {rosotacom_dev-2.5.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → 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.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +0 -0
  236. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +0 -0
  237. {rosotacom_dev-2.5.dev8 → 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.dev8 → 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.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +0 -0
  240. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +0 -0
  241. {rosotacom_dev-2.5.dev8 → 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.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +0 -0
  243. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +0 -0
  244. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +0 -0
  245. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +0 -0
  246. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +0 -0
  247. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +0 -0
  248. {rosotacom_dev-2.5.dev8 → 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.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +0 -0
  250. {rosotacom_dev-2.5.dev8 → 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.dev8 → 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.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +0 -0
  253. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +0 -0
  254. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +0 -0
  255. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +0 -0
  256. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +0 -0
  257. {rosotacom_dev-2.5.dev8 → 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.dev8 → 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.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/package.xml +0 -0
  260. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
  261. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/setup.cfg +0 -0
  262. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/setup.py +0 -0
  263. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +0 -0
  264. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +0 -0
  265. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +0 -0
  266. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +0 -0
  267. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/content/network/up_connectivity.py +0 -0
  268. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +0 -0
  269. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/creation/anonymize_bag.py +0 -0
  270. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/creation/catmux_log_setup.sh +0 -0
  271. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/creation/create_session_yaml.py +0 -0
  272. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/creation/generate_session_files.py +0 -0
  273. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/creation/run_session.py +0 -0
  274. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +0 -0
  275. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/resources/ws/session/creation/strip_ansi.py +0 -0
  276. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/status_eval.py +0 -0
  277. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/stream_stats.py +0 -0
  278. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/trace_profiles.py +0 -0
  279. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/transit.py +0 -0
  280. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom/video_quality.py +0 -0
  281. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom_dev.egg-info/SOURCES.txt +0 -0
  282. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom_dev.egg-info/dependency_links.txt +0 -0
  283. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom_dev.egg-info/entry_points.txt +0 -0
  284. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom_dev.egg-info/requires.txt +0 -0
  285. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom_dev.egg-info/scm_file_list.json +0 -0
  286. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/src/rosotacom_dev.egg-info/top_level.txt +0 -0
  287. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/stop_session_in_container.py +0 -0
  288. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/terminology.md +0 -0
  289. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/contract/test_benched_set_registry.py +0 -0
  290. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/contract/test_findings.py +0 -0
  291. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/contract/test_markdown_links.py +0 -0
  292. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/contract/test_ota_source_bundle.py +0 -0
  293. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/contract/test_package_metadata.py +0 -0
  294. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/contract/test_packaged_resources.py +0 -0
  295. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/contract/test_profile_covers_generated_commands.py +0 -0
  296. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/contract/test_pytest_policy.py +0 -0
  297. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/contract/test_readme_examples.py +0 -0
  298. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/contract/test_security_maintenance_config.py +0 -0
  299. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/contract/test_workflow_contracts.py +0 -0
  300. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/e2e/conftest.py +0 -0
  301. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/e2e/test_anonymize_e2e.py +0 -0
  302. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/e2e/test_benchmark_ab.py +0 -0
  303. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/e2e/test_benchmark_capacity.py +0 -0
  304. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/e2e/test_benchmark_replay.py +0 -0
  305. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/e2e/test_parallel_smoke.py +0 -0
  306. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/e2e/test_smoke.py +0 -0
  307. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/e2e/test_timeline_stepping.py +0 -0
  308. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/e2e/test_video_quality_e2e.py +0 -0
  309. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/sessions/generate_rmw_matrix.py +0 -0
  310. {rosotacom_dev-2.5.dev8 → 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.dev8 → rosotacom_dev-2.5.dev10}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota/session-definition.yaml +0 -0
  312. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota-tuned/session-definition.yaml +0 -0
  313. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/sessions/rmw_matrix/1_heartbeat_fastdds/session-definition.yaml +0 -0
  314. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/sessions/rmw_matrix/1_heartbeat_zen-endpoints/session-definition.yaml +0 -0
  315. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_anonymize.py +0 -0
  316. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_bag_ground_truth.py +0 -0
  317. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_benched_set.py +0 -0
  318. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_benchmark.py +0 -0
  319. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_bundle_check.py +0 -0
  320. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_cli_benchmark.py +0 -0
  321. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_concurrency.py +0 -0
  322. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_deployment.py +0 -0
  323. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_ffmpeg_packet.py +0 -0
  324. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_forensics.py +0 -0
  325. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_geomap.py +0 -0
  326. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_link_bytes.py +0 -0
  327. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_link_trace.py +0 -0
  328. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_network_profiles.py +0 -0
  329. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_network_shaper.py +0 -0
  330. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_ota_xml.py +0 -0
  331. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_plots.py +0 -0
  332. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_stage_latency.py +0 -0
  333. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_status_eval.py +0 -0
  334. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_status_overview.py +0 -0
  335. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_stream_stats.py +0 -0
  336. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_trace_profiles.py +0 -0
  337. {rosotacom_dev-2.5.dev8 → rosotacom_dev-2.5.dev10}/tests/unit/test_transit.py +0 -0
  338. {rosotacom_dev-2.5.dev8 → 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.dev8
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
  )
@@ -2378,6 +2455,7 @@ def _resolve_ota_smoke_context(
2378
2455
  )
2379
2456
  install_mode = str(getattr(args, "install_mode", None) or "source")
2380
2457
  checkouts = _parse_peer_checkout_overrides(getattr(args, "peer_checkout", None))
2458
+ exec_commands = _parse_peer_exec_overrides(getattr(args, "peer_exec", None))
2381
2459
  project: str | None = None
2382
2460
  if install_mode == "checkout":
2383
2461
  if not runtime.rosotacom_config:
@@ -2385,10 +2463,12 @@ def _resolve_ota_smoke_context(
2385
2463
  local_checkout, project = _ota_checkout_project_path(runtime.rosotacom_config)
2386
2464
  for name, path in sorted(checkouts.items()):
2387
2465
  binding = bindings.get(name)
2388
- 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:
2389
2468
  raise RuntimeError(
2390
2469
  f"--peer-checkout {name}={path} names the peer that runs on this machine, but the active "
2391
- 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)."
2392
2472
  )
2393
2473
  plan = _ota_plan_from_bindings(
2394
2474
  bindings,
@@ -2397,6 +2477,7 @@ def _resolve_ota_smoke_context(
2397
2477
  install_pin=getattr(args, "install_pin", None),
2398
2478
  checkouts=checkouts,
2399
2479
  project=project,
2480
+ exec_commands=exec_commands,
2400
2481
  )
2401
2482
  plan_peers = set(plan.peers)
2402
2483
  target_peers = set(_peer_keys_from_cfg(target.cfg))
@@ -2428,7 +2509,7 @@ def _ota_network_sudo_passwords(
2428
2509
  return {peer.name: "" for peer in plan.peers.values()}
2429
2510
  passwords: dict[str, str] = {}
2430
2511
  for peer in plan.peers.values():
2431
- target = peer.ssh or "local shell"
2512
+ target = _ota_peer_target(peer)
2432
2513
  passwords[peer.name] = getpass.getpass(f"sudo password for {peer.name} ({target}) [network shaping only]: ")
2433
2514
  return passwords
2434
2515
 
@@ -2528,8 +2609,8 @@ def _ota_preflight(
2528
2609
  sudo_mode = _validate_ota_sudo_mode(sudo_mode)
2529
2610
  sudo_passwords = sudo_passwords or {}
2530
2611
  for peer in plan.peers.values():
2531
- if peer.ssh:
2532
- _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)
2533
2614
  required = ["python3", "docker", "tar"]
2534
2615
  if require_tmux:
2535
2616
  required.append("tmux")
@@ -2634,6 +2715,12 @@ def _ota_stage_tree(peer: OtaSmokePeer, source: Path, destination: str, *, dry_r
2634
2715
  "-",
2635
2716
  ".",
2636
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.")
2637
2724
  if not peer.ssh:
2638
2725
  print(f"+ {label}: copy {source} -> {destination}")
2639
2726
  if not dry_run:
@@ -2655,7 +2742,13 @@ def _ota_stage_tree(peer: OtaSmokePeer, source: Path, destination: str, *, dry_r
2655
2742
 
2656
2743
 
2657
2744
  def _ota_stage_text(peer: OtaSmokePeer, text: str, destination: str, *, dry_run: bool, label: str) -> None:
2658
- 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)}")
2659
2752
  if dry_run:
2660
2753
  return
2661
2754
  if peer.ssh:
@@ -3198,7 +3291,7 @@ def _ota_verify_isolation(
3198
3291
  published = _ota_run(source, publish, label=f"{source_name}: publish isolation probe", dry_run=dry_run, check=False)
3199
3292
  if published.returncode != 0:
3200
3293
  _ota_print_failure_output(f"{source_name}: publish isolation probe", published)
3201
- 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)")
3202
3295
  return errors
3203
3296
  _print_completed_output(published)
3204
3297
  check = _ota_rosotacom_command(plan, _ota_probe_check_parts(target, receiver_name, peer_args), receiver)
@@ -6332,8 +6425,17 @@ def _stop_smoke_topic_publishers(containers: dict[str, str], specs: list[SmokeTo
6332
6425
  )
6333
6426
 
6334
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
+
6335
6437
  def _publish_isolation_probe(
6336
- 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
6337
6439
  ) -> None:
6338
6440
  """Publish a local-only topic in the container's local application domain,
6339
6441
  detached, for `duration` seconds (self-stops via `timeout`)."""
@@ -6362,6 +6464,45 @@ def _topic_present(container_name: str, ros_setup: str, topic: str) -> bool:
6362
6464
  return topic in names
6363
6465
 
6364
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
+
6365
6506
  def _verify_isolation(
6366
6507
  pub_container: str,
6367
6508
  pub_setup: str,
@@ -6376,15 +6517,18 @@ def _verify_isolation(
6376
6517
  log_line(f"Starting isolation probe {topic} in {pub_container}")
6377
6518
  _publish_isolation_probe(pub_container, pub_setup, topic)
6378
6519
  try:
6379
- live = False
6380
- for _ in range(8):
6381
- log_line(f"Waiting for isolation probe {topic} to advertise in {pub_container}")
6382
- if _topic_present(pub_container, pub_setup, topic):
6383
- live = True
6384
- break
6385
- time.sleep(1)
6386
- if not live:
6387
- 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
+ ]
6388
6532
  log_line(f"Checking that isolation probe {topic} is absent in {check_container}")
6389
6533
  if _topic_present(check_container, check_setup, topic):
6390
6534
  return [f"isolation breach: {topic} from {pub_container} leaked to {check_container}"]
@@ -6516,12 +6660,32 @@ def probe_publish_command(args: argparse.Namespace) -> int:
6516
6660
  # Block until the topic is actually advertised so a caller's subsequent
6517
6661
  # absence check on the other peer is meaningful (not a false pass on a
6518
6662
  # publisher that never came up).
6519
- for _ in range(10):
6520
- if _topic_present(container, ros_setup, args.topic):
6521
- print(f"Publishing {args.topic} in {container} (identity {args.identity}); advertised.")
6522
- return 0
6523
- time.sleep(1)
6524
- 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
+ )
6525
6689
  return 1
6526
6690
 
6527
6691
 
@@ -8495,6 +8659,18 @@ def main(argv: list[str] | None = None) -> int:
8495
8659
  "Required for every peer in that mode; the same repository is at a different path on each machine."
8496
8660
  ),
8497
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
+ )
8498
8674
  ota_smoke_parser.add_argument("--target-type", choices=["auto", "session", "scenario"], default="auto")
8499
8675
  ota_smoke_parser.add_argument("--interactive", action="store_true", help="Open a local control tmux UI.")
8500
8676
  ota_smoke_parser.add_argument("--stop", action="store_true", help="Stop an OTA smoke run.")
@@ -8957,7 +9133,13 @@ def main(argv: list[str] | None = None) -> int:
8957
9133
  probe_publish_parser.add_argument("--instance-id")
8958
9134
  probe_publish_parser.add_argument("--topic", default=ISOLATION_PROBE_TOPIC)
8959
9135
  probe_publish_parser.add_argument("--rate", type=float, default=5.0)
8960
- 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
+ )
8961
9143
  probe_publish_parser.add_argument(
8962
9144
  "--stop", action="store_true", help="Stop a running probe publisher instead of starting one."
8963
9145
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rosotacom-dev
3
- Version: 2.5.dev8
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": 8,
4
- "node": "gaee95efe30af5304247a301f7bd0a393f42600d2",
3
+ "distance": 10,
4
+ "node": "gc0c857f77e0a3a0b17134fdc9e0edb7a70f80a1b",
5
5
  "dirty": false,
6
6
  "branch": "HEAD",
7
7
  "node_date": "2026-08-11"