rosotacom-dev 2.5.dev52__py3-none-any.whl → 2.5.dev53__py3-none-any.whl
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.
- rosotacom/cli.py +93 -2
- rosotacom/network_preflight.py +134 -0
- {rosotacom_dev-2.5.dev52.dist-info → rosotacom_dev-2.5.dev53.dist-info}/METADATA +19 -1
- {rosotacom_dev-2.5.dev52.dist-info → rosotacom_dev-2.5.dev53.dist-info}/RECORD +8 -7
- {rosotacom_dev-2.5.dev52.dist-info → rosotacom_dev-2.5.dev53.dist-info}/WHEEL +0 -0
- {rosotacom_dev-2.5.dev52.dist-info → rosotacom_dev-2.5.dev53.dist-info}/entry_points.txt +0 -0
- {rosotacom_dev-2.5.dev52.dist-info → rosotacom_dev-2.5.dev53.dist-info}/licenses/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev52.dist-info → rosotacom_dev-2.5.dev53.dist-info}/top_level.txt +0 -0
rosotacom/cli.py
CHANGED
|
@@ -64,6 +64,7 @@ from .image_cache import (
|
|
|
64
64
|
image_fingerprint,
|
|
65
65
|
reference_tag,
|
|
66
66
|
)
|
|
67
|
+
from .network_preflight import NetworkPreflightResult, run_network_preflight
|
|
67
68
|
|
|
68
69
|
anonymize_lib = importlib.import_module("rosotacom.anonymize")
|
|
69
70
|
|
|
@@ -1125,7 +1126,7 @@ def _get_local_ipv4s() -> list[str]:
|
|
|
1125
1126
|
out = subprocess.check_output(["ip", "-o", "-4", "addr", "show"], text=True)
|
|
1126
1127
|
except Exception:
|
|
1127
1128
|
return []
|
|
1128
|
-
return
|
|
1129
|
+
return re.findall(r"\binet\s+(\d+\.\d+\.\d+\.\d+)/", out)
|
|
1129
1130
|
|
|
1130
1131
|
|
|
1131
1132
|
def _default_local_ip() -> str:
|
|
@@ -1234,6 +1235,29 @@ def _auto_identity(bindings: dict[str, PeerBinding]) -> str:
|
|
|
1234
1235
|
raise RuntimeError(f"Auto identity ambiguous: matched peers={matches} for local IPv4s={sorted(local_ips)}.")
|
|
1235
1236
|
|
|
1236
1237
|
|
|
1238
|
+
def _network_preflight(
|
|
1239
|
+
bindings: dict[str, PeerBinding],
|
|
1240
|
+
identity: str,
|
|
1241
|
+
*,
|
|
1242
|
+
require_peer_reachable: bool = False,
|
|
1243
|
+
) -> NetworkPreflightResult:
|
|
1244
|
+
return run_network_preflight(
|
|
1245
|
+
bindings,
|
|
1246
|
+
identity,
|
|
1247
|
+
local_addresses=_get_local_ipv4s(),
|
|
1248
|
+
require_peer_reachable=require_peer_reachable,
|
|
1249
|
+
)
|
|
1250
|
+
|
|
1251
|
+
|
|
1252
|
+
def _print_network_preflight(result: NetworkPreflightResult) -> None:
|
|
1253
|
+
reachability = "peer reachable" if result.peer_reachable else "route verified"
|
|
1254
|
+
print(
|
|
1255
|
+
f"Network preflight OK: {result.identity} ({result.local_address}) -> "
|
|
1256
|
+
f"{result.remote_identity} ({result.remote_address}) via {result.interface} "
|
|
1257
|
+
f"src {result.source_address}; {reachability}."
|
|
1258
|
+
)
|
|
1259
|
+
|
|
1260
|
+
|
|
1237
1261
|
def _peer_com_name(peers: dict[str, Any], peer_key: str) -> str:
|
|
1238
1262
|
value = (peers.get(peer_key) or {}).get("com-name")
|
|
1239
1263
|
if value is None:
|
|
@@ -5123,10 +5147,18 @@ def start_session(args: argparse.Namespace) -> str:
|
|
|
5123
5147
|
identity = args.identity or _auto_identity(bindings)
|
|
5124
5148
|
if not args.identity:
|
|
5125
5149
|
print(f"Auto-selected identity: {identity}")
|
|
5150
|
+
network_isolated = bool(getattr(args, "network_name", None))
|
|
5151
|
+
if not network_isolated:
|
|
5152
|
+
_print_network_preflight(
|
|
5153
|
+
_network_preflight(
|
|
5154
|
+
bindings,
|
|
5155
|
+
identity,
|
|
5156
|
+
require_peer_reachable=getattr(args, "require_peer_reachable", False),
|
|
5157
|
+
)
|
|
5158
|
+
)
|
|
5126
5159
|
remote_name = _remote_peer_name(cfg, identity)
|
|
5127
5160
|
instance = _resolve_session_instance(runtime, session, getattr(args, "instance_id", None))
|
|
5128
5161
|
container_name = _container_name(remote_name, runtime, instance.instance_id)
|
|
5129
|
-
network_isolated = bool(getattr(args, "network_name", None))
|
|
5130
5162
|
if not network_isolated and not getattr(args, "skip_conflict_check", False):
|
|
5131
5163
|
matching = _matching_com_containers(runtime, remote_name)
|
|
5132
5164
|
if _fixed_com_container_name(runtime, remote_name) is not None:
|
|
@@ -5235,6 +5267,30 @@ def start_session(args: argparse.Namespace) -> str:
|
|
|
5235
5267
|
return container_name
|
|
5236
5268
|
|
|
5237
5269
|
|
|
5270
|
+
def preflight_session(args: argparse.Namespace) -> NetworkPreflightResult:
|
|
5271
|
+
runtime = _load_runtime_config(args)
|
|
5272
|
+
session = _resolve_session(args.session_dir, runtime)
|
|
5273
|
+
cfg = _effective_session_config(session.host_dir, runtime)
|
|
5274
|
+
bindings = _resolve_bindings(
|
|
5275
|
+
cfg,
|
|
5276
|
+
runtime,
|
|
5277
|
+
peer=getattr(args, "peer", None),
|
|
5278
|
+
peer_address=getattr(args, "peer_address", None),
|
|
5279
|
+
)
|
|
5280
|
+
if not args.identity and not getattr(args, "auto_identity", True):
|
|
5281
|
+
raise RuntimeError("Missing --identity. Provide --identity <peer> or allow auto identity.")
|
|
5282
|
+
identity = args.identity or _auto_identity(bindings)
|
|
5283
|
+
if not args.identity:
|
|
5284
|
+
print(f"Auto-selected identity: {identity}")
|
|
5285
|
+
result = _network_preflight(
|
|
5286
|
+
bindings,
|
|
5287
|
+
identity,
|
|
5288
|
+
require_peer_reachable=getattr(args, "require_peer_reachable", False),
|
|
5289
|
+
)
|
|
5290
|
+
_print_network_preflight(result)
|
|
5291
|
+
return result
|
|
5292
|
+
|
|
5293
|
+
|
|
5238
5294
|
def stop_session(args: argparse.Namespace) -> None:
|
|
5239
5295
|
runtime = _load_runtime_config(args)
|
|
5240
5296
|
session = _resolve_session(args.session_dir, runtime)
|
|
@@ -8668,6 +8724,11 @@ def _add_start_args(parser: argparse.ArgumentParser) -> None:
|
|
|
8668
8724
|
parser.add_argument("--rewrite-formatting", action="store_true")
|
|
8669
8725
|
_add_peer_arg(parser)
|
|
8670
8726
|
_add_peer_address_arg(parser)
|
|
8727
|
+
parser.add_argument(
|
|
8728
|
+
"--require-peer-reachable",
|
|
8729
|
+
action="store_true",
|
|
8730
|
+
help="Require three bounded ICMP probes to the peer to succeed before starting.",
|
|
8731
|
+
)
|
|
8671
8732
|
parser.add_argument("--instance-id", help="Join or create a named runtime session instance.")
|
|
8672
8733
|
parser.add_argument(
|
|
8673
8734
|
"--skip-conflict-check",
|
|
@@ -8681,6 +8742,22 @@ def _add_start_args(parser: argparse.ArgumentParser) -> None:
|
|
|
8681
8742
|
parser.set_defaults(force=True, auto_identity=True)
|
|
8682
8743
|
|
|
8683
8744
|
|
|
8745
|
+
def _add_preflight_args(parser: argparse.ArgumentParser) -> None:
|
|
8746
|
+
_add_common_config_args(parser)
|
|
8747
|
+
_add_session_arg(parser, "session_dir_positional", nargs="?")
|
|
8748
|
+
_add_session_arg(parser, "-s", "--session-dir", dest="session_dir")
|
|
8749
|
+
_add_identity_arg(parser)
|
|
8750
|
+
parser.add_argument("--no-auto-identity", dest="auto_identity", action="store_false")
|
|
8751
|
+
_add_peer_arg(parser)
|
|
8752
|
+
_add_peer_address_arg(parser)
|
|
8753
|
+
parser.add_argument(
|
|
8754
|
+
"--require-peer-reachable",
|
|
8755
|
+
action="store_true",
|
|
8756
|
+
help="Require three bounded ICMP probes to the peer to succeed.",
|
|
8757
|
+
)
|
|
8758
|
+
parser.set_defaults(auto_identity=True)
|
|
8759
|
+
|
|
8760
|
+
|
|
8684
8761
|
def _normalize_session_arg(args: argparse.Namespace) -> None:
|
|
8685
8762
|
positional = getattr(args, "session_dir_positional", None)
|
|
8686
8763
|
explicit = getattr(args, "session_dir", None)
|
|
@@ -8697,6 +8774,12 @@ def start_command(args: argparse.Namespace) -> int:
|
|
|
8697
8774
|
return 0
|
|
8698
8775
|
|
|
8699
8776
|
|
|
8777
|
+
def preflight_command(args: argparse.Namespace) -> int:
|
|
8778
|
+
_normalize_session_arg(args)
|
|
8779
|
+
preflight_session(args)
|
|
8780
|
+
return 0
|
|
8781
|
+
|
|
8782
|
+
|
|
8700
8783
|
def stop_command(args: argparse.Namespace) -> int:
|
|
8701
8784
|
_normalize_session_arg(args)
|
|
8702
8785
|
stop_session(args)
|
|
@@ -9052,6 +9135,7 @@ def videoquality_command(args: argparse.Namespace) -> int:
|
|
|
9052
9135
|
# turn into `rosotacom start foo` — so a contract test keeps the two in sync.
|
|
9053
9136
|
TOP_LEVEL_COMMANDS = {
|
|
9054
9137
|
"start",
|
|
9138
|
+
"preflight",
|
|
9055
9139
|
"stop",
|
|
9056
9140
|
"doctor",
|
|
9057
9141
|
"ps",
|
|
@@ -9127,6 +9211,13 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
9127
9211
|
_add_start_args(start_parser)
|
|
9128
9212
|
start_parser.set_defaults(func=start_command)
|
|
9129
9213
|
|
|
9214
|
+
preflight_parser = subparsers.add_parser(
|
|
9215
|
+
"preflight",
|
|
9216
|
+
help="Validate the resolved local address, peer route, and optional peer reachability.",
|
|
9217
|
+
)
|
|
9218
|
+
_add_preflight_args(preflight_parser)
|
|
9219
|
+
preflight_parser.set_defaults(func=preflight_command)
|
|
9220
|
+
|
|
9130
9221
|
stop_parser = subparsers.add_parser("stop", help="Stop rosotacom containers for a session.")
|
|
9131
9222
|
_add_common_config_args(stop_parser)
|
|
9132
9223
|
_add_session_arg(stop_parser, "session_dir_positional", nargs="?")
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Host data-plane validation for resolved communication peers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import ipaddress
|
|
6
|
+
import re
|
|
7
|
+
import subprocess
|
|
8
|
+
from collections.abc import Iterable
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
from .deployment import PeerBinding
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class NetworkPreflightResult:
|
|
16
|
+
identity: str
|
|
17
|
+
local_address: str
|
|
18
|
+
remote_identity: str
|
|
19
|
+
remote_address: str
|
|
20
|
+
interface: str
|
|
21
|
+
source_address: str
|
|
22
|
+
peer_reachable: bool | None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _binding_ipv4(binding: PeerBinding, *, role: str) -> str:
|
|
26
|
+
try:
|
|
27
|
+
return str(ipaddress.IPv4Address(binding.address))
|
|
28
|
+
except ipaddress.AddressValueError as exc:
|
|
29
|
+
raise RuntimeError(
|
|
30
|
+
f"Network preflight failed: {role} peer {binding.peer!r} has non-IPv4 address "
|
|
31
|
+
f"{binding.address!r}. Communication peer addresses must be concrete IPv4 addresses."
|
|
32
|
+
) from exc
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def run_network_preflight(
|
|
36
|
+
bindings: dict[str, PeerBinding],
|
|
37
|
+
identity: str,
|
|
38
|
+
*,
|
|
39
|
+
local_addresses: Iterable[str],
|
|
40
|
+
require_peer_reachable: bool = False,
|
|
41
|
+
) -> NetworkPreflightResult:
|
|
42
|
+
"""Validate the selected local binding and kernel path to its peer."""
|
|
43
|
+
if identity not in bindings:
|
|
44
|
+
raise RuntimeError(f"Network preflight failed: identity {identity!r} is not one of peers={sorted(bindings)}.")
|
|
45
|
+
if len(bindings) != 2:
|
|
46
|
+
raise RuntimeError(f"Network preflight failed: expected exactly 2 peers, got peers={sorted(bindings)}.")
|
|
47
|
+
|
|
48
|
+
local = bindings[identity]
|
|
49
|
+
remote_identity = next(peer for peer in bindings if peer != identity)
|
|
50
|
+
remote = bindings[remote_identity]
|
|
51
|
+
local_address = _binding_ipv4(local, role="local")
|
|
52
|
+
remote_address = _binding_ipv4(remote, role="remote")
|
|
53
|
+
|
|
54
|
+
known_local_addresses = sorted(set(local_addresses))
|
|
55
|
+
if not known_local_addresses:
|
|
56
|
+
raise RuntimeError(
|
|
57
|
+
"Network preflight failed: could not determine any local IPv4 addresses. "
|
|
58
|
+
"Verify that the `ip` command is installed and the required data-plane interface is up."
|
|
59
|
+
)
|
|
60
|
+
if local_address not in known_local_addresses:
|
|
61
|
+
raise RuntimeError(
|
|
62
|
+
f"Network preflight failed for identity {identity!r}: configured local address {local_address} "
|
|
63
|
+
f"is not assigned to this host (local IPv4s: {known_local_addresses}). "
|
|
64
|
+
"Verify --identity and the peer bindings; if the address belongs to a VPN or other data-plane "
|
|
65
|
+
"interface, bring that interface up before starting communication."
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
route = subprocess.run(
|
|
70
|
+
["ip", "-4", "route", "get", remote_address],
|
|
71
|
+
text=True,
|
|
72
|
+
capture_output=True,
|
|
73
|
+
check=False,
|
|
74
|
+
)
|
|
75
|
+
except FileNotFoundError as exc:
|
|
76
|
+
raise RuntimeError("Network preflight failed: required command `ip` was not found.") from exc
|
|
77
|
+
route_output = (route.stdout or "").strip()
|
|
78
|
+
if route.returncode != 0 or not route_output:
|
|
79
|
+
detail = (route.stderr or route.stdout or "no route returned").strip()
|
|
80
|
+
raise RuntimeError(
|
|
81
|
+
f"Network preflight failed: no usable IPv4 route from {identity!r} ({local_address}) "
|
|
82
|
+
f"to {remote_identity!r} ({remote_address}): {detail}"
|
|
83
|
+
)
|
|
84
|
+
route_line = route_output.splitlines()[0]
|
|
85
|
+
source_match = re.search(r"\bsrc\s+(\d+\.\d+\.\d+\.\d+)\b", route_line)
|
|
86
|
+
interface_match = re.search(r"\bdev\s+(\S+)", route_line)
|
|
87
|
+
if source_match is None or interface_match is None:
|
|
88
|
+
raise RuntimeError(
|
|
89
|
+
f"Network preflight failed: could not determine source address and interface from route: {route_line}"
|
|
90
|
+
)
|
|
91
|
+
source_address = source_match.group(1)
|
|
92
|
+
interface = interface_match.group(1)
|
|
93
|
+
if source_address != local_address:
|
|
94
|
+
raise RuntimeError(
|
|
95
|
+
f"Network preflight failed: route to {remote_identity!r} ({remote_address}) uses source "
|
|
96
|
+
f"{source_address} on {interface}, but identity {identity!r} is bound to {local_address}. "
|
|
97
|
+
"Fix the route or peer binding before starting communication."
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
peer_reachable: bool | None = None
|
|
101
|
+
if require_peer_reachable:
|
|
102
|
+
try:
|
|
103
|
+
ping = subprocess.run(
|
|
104
|
+
["ping", "-4", "-n", "-c", "3", "-W", "2", "-I", local_address, remote_address],
|
|
105
|
+
text=True,
|
|
106
|
+
capture_output=True,
|
|
107
|
+
check=False,
|
|
108
|
+
timeout=10,
|
|
109
|
+
)
|
|
110
|
+
except FileNotFoundError as exc:
|
|
111
|
+
raise RuntimeError(
|
|
112
|
+
"Network preflight failed: strict peer reachability was requested, but `ping` was not found."
|
|
113
|
+
) from exc
|
|
114
|
+
except subprocess.TimeoutExpired as exc:
|
|
115
|
+
raise RuntimeError(
|
|
116
|
+
f"Network preflight failed: ICMP check from {local_address} to {remote_address} exceeded 10 seconds."
|
|
117
|
+
) from exc
|
|
118
|
+
if ping.returncode != 0:
|
|
119
|
+
raise RuntimeError(
|
|
120
|
+
f"Network preflight failed: peer {remote_identity!r} ({remote_address}) did not answer "
|
|
121
|
+
f"3 ICMP echo requests sent from {local_address} via {interface}. This start requires an "
|
|
122
|
+
"already-reachable peer; verify the data plane, VPN, and firewall."
|
|
123
|
+
)
|
|
124
|
+
peer_reachable = True
|
|
125
|
+
|
|
126
|
+
return NetworkPreflightResult(
|
|
127
|
+
identity=identity,
|
|
128
|
+
local_address=local_address,
|
|
129
|
+
remote_identity=remote_identity,
|
|
130
|
+
remote_address=remote_address,
|
|
131
|
+
interface=interface,
|
|
132
|
+
source_address=source_address,
|
|
133
|
+
peer_reachable=peer_reachable,
|
|
134
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rosotacom-dev
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.dev53
|
|
4
4
|
Summary: Host CLI for ROS OTA communication sessions.
|
|
5
5
|
Author: develNor
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -309,6 +309,24 @@ rosotacom start 1_heartbeat --identity b \
|
|
|
309
309
|
--peer-address a=10.0.0.10 --peer-address b=10.0.0.11
|
|
310
310
|
```
|
|
311
311
|
|
|
312
|
+
Before `start` stops a conflicting container, writes an instance, or builds an
|
|
313
|
+
image, it verifies that the selected identity's address belongs to this host and
|
|
314
|
+
that the route to the other peer selects that address as its source. A missing
|
|
315
|
+
VPN/data-plane interface or a route through the wrong network therefore fails
|
|
316
|
+
without disturbing a working session. Run the same check on its own with
|
|
317
|
+
`preflight`; deployments that require the other host to be online already can
|
|
318
|
+
also make three bounded ICMP probes mandatory:
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
rosotacom preflight 1_heartbeat --identity a \
|
|
322
|
+
--peer-address a=10.0.0.10 --peer-address b=10.0.0.11 \
|
|
323
|
+
--require-peer-reachable
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Peer reachability is opt-in because some valid networks block ICMP and peers may
|
|
327
|
+
be started in either order. `rosotacom start` accepts the same
|
|
328
|
+
`--require-peer-reachable` policy when a deployment does require it.
|
|
329
|
+
|
|
312
330
|
`rosotacom` reads the static session input and creates generated files under `session-instances/<date>/<session>_<timestamp>_<id>/config/`, including per-peer plugin/session specs, topic lists, optional QoS, and optional `domain_bridge.yaml`. Catmux pane output is logged under the same instance in `logs/<peer>/catmux/`.
|
|
313
331
|
|
|
314
332
|
What else a run leaves under `logs/<peer>/`, and which file to open first when a
|
|
@@ -6,7 +6,7 @@ rosotacom/benched_set.py,sha256=lBA1Rj04dbr2e-lIvYTWoL55_LdUpCAC-y7eIvJZ8lc,2721
|
|
|
6
6
|
rosotacom/benchmark.py,sha256=3QoXJtWUGQPwvDWyQT_fOjx6K9G2pmvtnVurA21G2QY,76612
|
|
7
7
|
rosotacom/benchmark_delta.py,sha256=qXzriBkbXOeJdlLKM4ziov1F-JfwlseYHE48S9hjFsQ,10005
|
|
8
8
|
rosotacom/bundle_check.py,sha256=l_3BSuYk6juXQmsNU6jkxRbH8ayH2GkiW9ONKMQ1oJY,18270
|
|
9
|
-
rosotacom/cli.py,sha256=
|
|
9
|
+
rosotacom/cli.py,sha256=DKG6cylS_37hFe5pvIQI1y-Iq-TxmwR2zIFjuUdB-zE,412811
|
|
10
10
|
rosotacom/cli_benchmark.py,sha256=0R30if8b9F4uxTX53KmUuEJ0VXcK_5RcudqR9QARV5Q,355021
|
|
11
11
|
rosotacom/deployment.py,sha256=XVhB8JjxhAsglSEEPtiVSeV6fCG0rgGmDRulh14oIYQ,7318
|
|
12
12
|
rosotacom/ffmpeg_packet.py,sha256=zB1BHgY1kexr5sETVMkm4bkVnRytj9k2t0uOYiHUZ6M,4267
|
|
@@ -14,6 +14,7 @@ rosotacom/forensics.py,sha256=fxpjuRq3pA1wCf2ERai4SlVnNhJBqZbDh6ETRKODNd0,53576
|
|
|
14
14
|
rosotacom/geomap.py,sha256=_85kWI-twzS3rf9KxamnFuhlw8Rp_fRk7n7xI-rUMFc,30292
|
|
15
15
|
rosotacom/image_cache.py,sha256=71wC291j8rRMgF1D5pt2gTQdplmiIwnUDXk0BZoQNNQ,7185
|
|
16
16
|
rosotacom/netem_dist.py,sha256=OWtc8asx-ow1wCxDoQuuJwJsFLl7F_lthLWsp-nlK-M,4642
|
|
17
|
+
rosotacom/network_preflight.py,sha256=mQsjVnHP3CPDfHyLsOdobtymcYhcvJbmJRbXm9EgGDs,5532
|
|
17
18
|
rosotacom/network_profiles.py,sha256=GAyVL794zQ3HnGlr1d6GoG5_qDaF5BsXwN9WRGF7kLE,31530
|
|
18
19
|
rosotacom/network_shaper.py,sha256=kjn9ZtiO1SMm8UHKIjmn92JBu_hE5TJF6vaNF1XKtNU,5463
|
|
19
20
|
rosotacom/plots.py,sha256=YRGRxuGHSHDZmtL0sv1JS7cPVSL9VzjGYuvMxeHAj54,22674
|
|
@@ -197,9 +198,9 @@ rosotacom/resources/ws/session/creation/generate_session_files.py,sha256=NYdRSZT
|
|
|
197
198
|
rosotacom/resources/ws/session/creation/run_session.py,sha256=G2hJhrmLp11gm3JZ6_C-zZcqu_reXlE_wmobB7BiCfU,16849
|
|
198
199
|
rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py,sha256=Ub_5f-ttUUc-rAPrZmg_Op34s8Z4C91hTyEn8fgyt-E,2633
|
|
199
200
|
rosotacom/resources/ws/session/creation/strip_ansi.py,sha256=vNjpemcq4xHr0VG4x0bZqvgwEBEfOgN-b4PzbrM5nfM,851
|
|
200
|
-
rosotacom_dev-2.5.
|
|
201
|
-
rosotacom_dev-2.5.
|
|
202
|
-
rosotacom_dev-2.5.
|
|
203
|
-
rosotacom_dev-2.5.
|
|
204
|
-
rosotacom_dev-2.5.
|
|
205
|
-
rosotacom_dev-2.5.
|
|
201
|
+
rosotacom_dev-2.5.dev53.dist-info/licenses/LICENSE.txt,sha256=FDB3-fao3lqZ94wsJHKAH7U88kAnfvZhZC8Hm5rN__g,1459
|
|
202
|
+
rosotacom_dev-2.5.dev53.dist-info/METADATA,sha256=ydDF-JJdTFmWNx_-sNEMeFbHmg2LwQ849jwxMMYRucM,55410
|
|
203
|
+
rosotacom_dev-2.5.dev53.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
204
|
+
rosotacom_dev-2.5.dev53.dist-info/entry_points.txt,sha256=im11ZWqBbgjQUBz2Ik2RNfMu54wlMwgiVpsBKeI7dCI,49
|
|
205
|
+
rosotacom_dev-2.5.dev53.dist-info/top_level.txt,sha256=BZrtQcIVDWn_pQZR3Mlssqy5SaRXHjOa6B-1D9Fy4Ao,10
|
|
206
|
+
rosotacom_dev-2.5.dev53.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{rosotacom_dev-2.5.dev52.dist-info → rosotacom_dev-2.5.dev53.dist-info}/licenses/LICENSE.txt
RENAMED
|
File without changes
|
|
File without changes
|