bytesim 0.1.0__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.
Files changed (48) hide show
  1. bytesim/__init__.py +14 -0
  2. bytesim/__main__.py +3 -0
  3. bytesim/cli.py +45 -0
  4. bytesim/constants.py +48 -0
  5. bytesim/demos.py +74 -0
  6. bytesim/model/__init__.py +5 -0
  7. bytesim/model/addressing.py +160 -0
  8. bytesim/model/base.py +57 -0
  9. bytesim/model/cloning.py +249 -0
  10. bytesim/model/equivalence.py +86 -0
  11. bytesim/model/groups.py +133 -0
  12. bytesim/model/ha.py +93 -0
  13. bytesim/model/layer2.py +106 -0
  14. bytesim/model/layout.py +203 -0
  15. bytesim/model/naming.py +114 -0
  16. bytesim/model/network.py +138 -0
  17. bytesim/model/routing.py +117 -0
  18. bytesim/model/stp.py +315 -0
  19. bytesim/model/types.py +107 -0
  20. bytesim/py.typed +0 -0
  21. bytesim/scripting.py +108 -0
  22. bytesim/synthesis.py +644 -0
  23. bytesim/topology.py +517 -0
  24. bytesim/tui/__init__.py +4 -0
  25. bytesim/tui/app.py +161 -0
  26. bytesim/tui/cables.py +58 -0
  27. bytesim/tui/canvas.py +489 -0
  28. bytesim/tui/command_input.py +29 -0
  29. bytesim/tui/commands/__init__.py +1 -0
  30. bytesim/tui/commands/addressing.py +235 -0
  31. bytesim/tui/commands/base.py +43 -0
  32. bytesim/tui/commands/groups.py +215 -0
  33. bytesim/tui/commands/history.py +134 -0
  34. bytesim/tui/commands/interpreter.py +85 -0
  35. bytesim/tui/commands/naming.py +68 -0
  36. bytesim/tui/commands/registry.py +28 -0
  37. bytesim/tui/commands/scripting.py +54 -0
  38. bytesim/tui/commands/switching.py +192 -0
  39. bytesim/tui/commands/topology.py +255 -0
  40. bytesim/tui/commands/traffic.py +312 -0
  41. bytesim/tui/help.py +50 -0
  42. bytesim/tui/inspector.py +115 -0
  43. bytesim/tui/theme.py +12 -0
  44. bytesim-0.1.0.dist-info/METADATA +178 -0
  45. bytesim-0.1.0.dist-info/RECORD +48 -0
  46. bytesim-0.1.0.dist-info/WHEEL +4 -0
  47. bytesim-0.1.0.dist-info/entry_points.txt +2 -0
  48. bytesim-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,115 @@
1
+ """The inspector panel: a text summary of the selection (Rich markup)."""
2
+ from __future__ import annotations
3
+
4
+ from ..constants import KINDS, VIP_HOST
5
+ from ..model import Iface, Network, fmt
6
+ from .theme import HA_COLOR, ROLE_COLOR, group_color
7
+
8
+
9
+ def mode_text(ifc: Iface) -> str:
10
+ """How an interface got its address: dhcp, adhoc, static, auto, or dhcp→adhoc."""
11
+ if ifc.mode == "dhcp" and ifc.via == "adhoc":
12
+ return "dhcp→adhoc"
13
+ return ifc.via or ifc.mode
14
+
15
+
16
+ def render_inspector(net: Network, sel: str | None, selected_group: str | None,
17
+ graph: bool, auto: bool, naming: str = "off") -> str:
18
+ out = [f"mode: [b]{'graph' if graph else 'addr'}[/] · auto-arrange: "
19
+ f"{'[green]on[/]' if auto else '[yellow]off[/]'}",
20
+ f"auto-renumber: {'[dim]off[/]' if naming == 'off' else f'[cyan]{naming}[/]'}",
21
+ f"{len(net.devices)} devices · {len(net.links)} cables", ""]
22
+ if net.storming:
23
+ out[-1:] = [f"[b red]⚡ broadcast storm:[/] [red]{' '.join(sorted(net.storming, key=net.bridge_id))}[/]", ""]
24
+ for k, (name, _, color) in KINDS.items():
25
+ ids = [d.id for d in net.sorted_devices() if d.kind == k]
26
+ out.append(f"[{color}]■ {name}{'es' if name.endswith('h') else 's'}[/] " + (" ".join(ids) if ids else "[dim]—[/]"))
27
+ def nest(g: str) -> str:
28
+ subs = net.subgroups(g)
29
+ return f"[{group_color(g)}]{g}[/]" + (" ‹" + " ".join(nest(x) for x in subs) + "›" if subs else "")
30
+
31
+ tops = [g for g in net.groups if net.parent_of(g) is None]
32
+ out.append("[b]┆ groups[/] " + (" ".join(nest(g) for g in tops) if tops else "[dim]—[/]"))
33
+ out.append("")
34
+ gsel = selected_group
35
+ if gsel and gsel in net.groups:
36
+ members = net.devices_in(gsel)
37
+ inside = sum(1 for l in net.links if l.a in members and l.b in members)
38
+ outside = sum(1 for l in net.links if (l.a in members) != (l.b in members))
39
+ out.append(f"[b reverse {group_color(gsel)}] {gsel} [/] [b]group[/] {len(members)} devices")
40
+ if (chain := net.chain(gsel)):
41
+ out.append(f"[dim]inside {' › '.join(chain)}[/]")
42
+ for sub in net.subgroups(gsel):
43
+ out.append(f" [{group_color(sub)}]▣ {sub:<10}[/] {len(net.devices_in(sub))} devices")
44
+ out.append(f"[dim]{inside} internal · {outside} external cables[/]")
45
+ if gsel in net.clusters and not graph:
46
+ st = net.ha_state(gsel)
47
+ out.append(f"[b]HA cluster[/] · policy {st['policy']}")
48
+ out.append(f"[{HA_COLOR[st['level']]}]{st['text']}[/]")
49
+ out.append(f"control links {st['ctrl_up']}/{st['ctrl']} up · "
50
+ f"{len(st['partitions'])} partition{'s' if len(st['partitions']) != 1 else ''}")
51
+ for netid, owners in sorted(st["owners"].items()):
52
+ out.append(f" {fmt((netid, VIP_HOST))} → {' + '.join(o[0] for o in owners)}")
53
+ for did in [m for m in net.groups[gsel] if m in net.devices]:
54
+ d = net.devices[did]
55
+ used = sum(1 for l in net.links if l.touches(did))
56
+ out.append(f" [{d.color}]■ {did:<5}[/] {d.name:<8} {used}/{d.nports}"
57
+ + (" [red]✕ down[/]" if not d.up else ""))
58
+ elif sel and sel in net.devices:
59
+ d = net.devices[sel]
60
+ used = sum(1 for l in net.links if l.touches(d.id))
61
+ kind = "NICs" if d.kind == "c" else "ports"
62
+ chain = net.chain(d.id)
63
+ g = chain[-1] if chain else None
64
+ out.append(f"[b reverse {d.color}] {d.id} [/] [b]{d.name}[/] {used}/{d.nports} {kind}"
65
+ + (" [b red]✕ DOWN[/]" if not d.up else ""))
66
+ out.append(f"[dim]position {d.x},{d.y}[/]"
67
+ + (f" [{group_color(g)}]{' › '.join(chain)}[/]" if g else ""))
68
+ if d.kind in "rg" and not graph:
69
+ out.append("DHCP server " + ("[red]off[/]" if d.id in net.dhcp_off else "[green]on[/]"))
70
+ if d.kind == "g":
71
+ out.append("[magenta]☁ uplink to the internet[/]")
72
+ for p in range(1, d.nports + 1):
73
+ l = net.link_on(d.id, p)
74
+ if l:
75
+ o, op = l.other(d.id)
76
+ od = net.devices[o]
77
+ tag = f" [{ROLE_COLOR[l.role]}]{l.role}[/]" if l.role != "data" else ""
78
+ mark = "[b red]✕[/]" if not l.up else "[b]●[/]"
79
+ if d.kind == "s" and (role := net.port_role(d.id, p)):
80
+ tag += {"blocking": " [b yellow]⊘ blocking[/]", "root": " [cyan]root[/]",
81
+ "designated": " [dim]desig[/]", "edge": " [dim]edge[/]"}[role]
82
+ if (d.id, p) in net.stp_blocked:
83
+ mark = "[b yellow]⊘[/]"
84
+ out.append(f" {mark} {d.port_name(p):<5} → [{od.color}]{o}[/]:{od.port_name(op)}{tag}")
85
+ else:
86
+ out.append(f" [grey42]○ {d.port_name(p):<5} free[/]")
87
+ ifc = net.ifaces.get(f"{d.id}:{p}")
88
+ line = f" [dim]{d.mac(p):<6}[/]"
89
+ if d.kind != "s" and ifc and not graph and (ifc.addr or ifc.mode in ("static", "none")):
90
+ mode = mode_text(ifc)
91
+ color = {"dhcp": "gold1", "static": "cyan", "auto": "grey62", "none": "red"}.get(mode, "yellow")
92
+ line += f" {fmt(ifc.addr)} [{color}]{mode}[/]"
93
+ if ifc.gw is not None:
94
+ line += f" [dim]gw {ifc.gw}[/]"
95
+ out.append(line)
96
+ if d.kind == "s":
97
+ stp = (f"STP [green]on[/] · root bridge [cyan]{net.stp_roots.get(d.id, d.id)}[/]"
98
+ if net.stp_enabled(d.id) else "STP [yellow]off[/]")
99
+ prio = net.stp_priority.get(d.id)
100
+ out.append(stp + (f" · priority {prio}" if prio is not None else ""))
101
+ if d.id in net.storming:
102
+ out.append("[b red]⚡ STORMING: every port saturated[/]")
103
+ for mac, ports in sorted(net.mac_flaps.get(d.id, {}).items()):
104
+ out.append(f"[orange1]MAC {mac} flapping {'⇄'.join(f'p{q}' for q in sorted(ports))}[/]")
105
+ table = net.mac_tables.get(d.id, {})
106
+ out.append("")
107
+ out.append(f"[b]MAC table[/] [dim]({len(table)} learned)[/]")
108
+ for mac, port in sorted(table.items(), key=lambda t: (t[1], t[0])):
109
+ out.append(f" {d.port_name(port):<4} {mac}")
110
+ if not table:
111
+ out.append(" [dim]empty: send some traffic[/]")
112
+ else:
113
+ out.append("[dim]nothing selected[/]")
114
+ out.append("[dim]tab / click / type an id or group[/]")
115
+ return "\n".join(out)
bytesim/tui/theme.py ADDED
@@ -0,0 +1,12 @@
1
+ """Colours for cable roles, cluster health and groups."""
2
+ from __future__ import annotations
3
+
4
+ GROUP_COLORS = ["orange1", "orchid", "spring_green2", "gold1", "deep_sky_blue1", "salmon1"]
5
+
6
+ ROLE_COLOR = {"data": "grey50", "control": "orchid", "control-backup": "orchid", "fabric": "dark_cyan"}
7
+
8
+ HA_COLOR = {"ok": "green", "degraded": "orange1", "split": "bold red", "down": "red"}
9
+
10
+
11
+ def group_color(name: str) -> str:
12
+ return GROUP_COLORS[sum(map(ord, name)) % len(GROUP_COLORS)]
@@ -0,0 +1,178 @@
1
+ Metadata-Version: 2.5
2
+ Name: bytesim
3
+ Version: 0.1.0
4
+ Summary: A text-mode network simulator: cable up gateways, routers, switches and computers, then watch DHCP, routing, multicast and HA failover in your terminal.
5
+ Project-URL: Homepage, https://github.com/BytesAndCoffee/bytesim
6
+ Project-URL: Documentation, https://github.com/BytesAndCoffee/bytesim/tree/main/docs
7
+ Project-URL: Repository, https://github.com/BytesAndCoffee/bytesim
8
+ Project-URL: Issues, https://github.com/BytesAndCoffee/bytesim/issues
9
+ Project-URL: Changelog, https://github.com/BytesAndCoffee/bytesim/blob/main/CHANGELOG.md
10
+ Author: Michael Yazdani
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: dhcp,education,network,networking,routing,simulator,textual,tui
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Environment :: Console
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Intended Audience :: Education
18
+ Classifier: Intended Audience :: System Administrators
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3 :: Only
22
+ Classifier: Programming Language :: Python :: 3.10
23
+ Classifier: Programming Language :: Python :: 3.11
24
+ Classifier: Programming Language :: Python :: 3.12
25
+ Classifier: Programming Language :: Python :: 3.13
26
+ Classifier: Programming Language :: Python :: 3.14
27
+ Classifier: Topic :: Education
28
+ Classifier: Topic :: System :: Networking
29
+ Classifier: Typing :: Typed
30
+ Requires-Python: >=3.10
31
+ Requires-Dist: pyyaml>=6.0
32
+ Requires-Dist: textual>=6.0
33
+ Provides-Extra: dev
34
+ Requires-Dist: build; extra == 'dev'
35
+ Requires-Dist: pyflakes; extra == 'dev'
36
+ Requires-Dist: pytest>=7; extra == 'dev'
37
+ Requires-Dist: twine; extra == 'dev'
38
+ Description-Content-Type: text/markdown
39
+
40
+ # ByteSim
41
+
42
+ [![CI](https://github.com/BytesAndCoffee/bytesim/actions/workflows/ci.yml/badge.svg)](https://github.com/BytesAndCoffee/bytesim/actions/workflows/ci.yml)
43
+ [![PyPI](https://img.shields.io/pypi/v/bytesim)](https://pypi.org/project/bytesim/)
44
+ [![Python](https://img.shields.io/badge/python-3.10%E2%80%933.14-blue)](https://pypi.org/project/bytesim/)
45
+
46
+ **A network simulator that runs in your terminal.** Add gateways, routers,
47
+ switches and computers, cable them together by typing device ids, and watch
48
+ DHCP, routing, multicast and router failover happen packet by packet.
49
+
50
+ ```
51
+ ╭┄ rfab1 · HA ┄┄┄┄┄┄┄┄┄┄┄┄╮
52
+ ┆ ┌────────┐ ┌────────┐ ┆
53
+ ┆ │ r1 │ │ r2 │ ┆
54
+ ┆ │ ●●○●●● │ │ ●●○●●● │ ┆
55
+ ┆ └─┬┬─┬┬┬─┘ └─┬┬─┬┬┬─┘ ┆
56
+ ╰┄┄┄││┄┃║║┄┄┄┄┄┄┄┄││┄┃║║┄┄┄╯
57
+ ┌──────────────┘└─┴┴┼──────────┼┼─┴┤║
58
+ │ ┌─────┴──────────┴┴──┼┴────────────┐
59
+ ╭┄┄┄┄│ fab1 ┄┄┄┄┄┄┄│┄┄┄┄┄┄┄╮ ╭┄┄┄┄│ fab2 ┄┄┄┄┄┄┄│┄┄┄┄┄┄┄╮
60
+ ┆ ┌──┴─────┐ ┌──┴─────┐ ┆ ┆ ┌──┴─────┐ ┌──┴─────┐ ┆
61
+ ┆ │ s1 │ │ s2 │ ┆ ┆ │ s3 │ │ s4 │ ┆
62
+ ┆ │ ●●●●○○ │ │ ●●●○○○ │ ┆ ┆ │ ●●●●○○ │ │ ●●●○○○ │ ┆
63
+ ┆ └─┬─┬┬───┘ └─┬─┬────┘ ┆ ┆ └─┬─┬┬───┘ └─┬─┬────┘ ┆
64
+ ╰┄┄┄│┄││┄┄┄┄┄┄┄┄┄┄│┄│┄┄┄┄┄┄╯ ╰┄┄┄│┄││┄┄┄┄┄┄┄┄┄┄│┄│┄┄┄┄┄┄╯
65
+ └─●•••─────┬─┘ │ │ ││ │ └─────┐
66
+ ┌─┴──┐ ┌┴───┐ ┌────┐ ┌─┴──┐ ┌────┐ ┌────┐
67
+ │ c1 │ │ c2 │ │ c3 │ │ c4 │ │ c5 │ │ c6 │
68
+ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘
69
+ (1,10) (1,11) (1,12) (2,10) (2,11) (2,12)
70
+ ```
71
+
72
+ ByteSim keeps what matters about networking (graphs, broadcast domains,
73
+ forwarding, failure) and leaves out the detail that gets in the way:
74
+
75
+ - **Devices with real ports.** Routers and switches can have any number of
76
+ ports, and computers can have several network cards. Every port shows on
77
+ the device as `○` (free) or `●` (in use).
78
+ - **Cabling by typing.** `r1 s2` connects two devices using their first free
79
+ ports. `r1:3 s2:8` picks the ports.
80
+ - **Automatic layout**, with arrow keys or the mouse to move things yourself.
81
+ - **Simple addressing.** An address is a pair: a network id and an 8-bit host
82
+ number, like `(2,14)`. MAC addresses name the physical port: `R1A` is port 1
83
+ of router r1.
84
+ - **DHCP and ad-hoc addressing**, animated as broadcast-then-reply.
85
+ - **Routing** through gateways and between routers, with ARP shown for every hop.
86
+ - **Unicast, ping and multicast.** With multicast you can see the packet copied
87
+ wherever the paths split.
88
+ - **Cable roles** (data, control, fabric) and failures you can trigger by hand.
89
+ - **Switching loops**: spanning tree with root bridges and blocked ports, and
90
+ real broadcast storms (frames multiplying, MAC tables flapping) when STP is off.
91
+ - **HA router clusters** with a shared virtual gateway, failover, split-brain,
92
+ and quorum or witness policies.
93
+ - **Nested groups** drawn as labelled frames, which you can **clone** along with
94
+ their internal cables.
95
+ - **BFS/DFS renumbering**, once or automatically after every change, so ids
96
+ follow the network's shape.
97
+ - **Graph mode**, which ignores addressing so you can explore pure connectivity.
98
+ - **Topology files.** Describe networks in YAML with reusable templates and link
99
+ macros (`mesh: [routers, switches]`), then load or export them.
100
+ - **Construction scripts.** `export script` writes the console commands that
101
+ rebuild your network, reusing repeated parts with `clone` and `copy`. Each
102
+ script is checked by replaying it before you get it, and `run` replays it.
103
+
104
+ ## Install
105
+
106
+ ```sh
107
+ pip install bytesim
108
+ ```
109
+
110
+ Requires Python 3.10+ and a terminal with Unicode and colour support.
111
+
112
+ ## Quick start
113
+
114
+ ```sh
115
+ bytesim --demo basic # a small routed network
116
+ bytesim --demo ha # a clustered router pair between two fabrics
117
+ bytesim --demo loop # a switch loop with STP off: set off a storm
118
+ bytesim mynet.json # open a saved network
119
+ bytesim campus.yaml # build one from a topology file
120
+ ```
121
+
122
+ Then, at the command line at the bottom of the screen:
123
+
124
+ ```text
125
+ add r 4 add a router with 4 ports → r1
126
+ add s 8 add an 8-port switch → s1
127
+ add c 1 3 add three computers with 1 NIC → c1 c2 c3
128
+ r1 s1 cable r1 to s1
129
+ s1 c1 cable s1 to c1 (c1 gets an address by DHCP)
130
+ ping c1 c2 watch the echo request and reply
131
+ ```
132
+
133
+ Type `help` for every command. Press `Esc` to switch from the command line to
134
+ the canvas, where the arrow keys move the selected device and `Tab` cycles
135
+ through devices.
136
+
137
+ ## Documentation
138
+
139
+ - [User guide](docs/guide.md): concepts, every command, keys, worked examples
140
+ - [Architecture](docs/architecture.md): how the code is organised, and how to add a command
141
+ - [Topology format](docs/topology-format.md): templates and link macros in YAML
142
+ - [Construction scripts](docs/scripts.md): `export script` and `run`, with a tutorial
143
+ - [Save file format](docs/file-format.md)
144
+ - [Changelog](CHANGELOG.md)
145
+
146
+ ## Using the model from Python
147
+
148
+ The simulation is a plain Python model with no UI dependency:
149
+
150
+ ```python
151
+ from bytesim import Network
152
+
153
+ net = Network()
154
+ net.add("r"); net.add("s"); net.add("c"); net.add("c")
155
+ net.connect("r1", None, "s1", None)
156
+ net.connect("s1", None, "c1", None)
157
+ net.connect("s1", None, "c2", None)
158
+ net.reconcile() # router autoconfig + DHCP
159
+
160
+ print(net.iface("c1", 1).addr) # (1, 10)
161
+ for src, dst, frames in net.route("c1", ("c2", 1)):
162
+ print(src, "→", dst, [d for d, _ in frames])
163
+ ```
164
+
165
+ ## Development
166
+
167
+ ```sh
168
+ git clone https://github.com/BytesAndCoffee/bytesim
169
+ cd bytesim
170
+ python -m venv .venv && . .venv/bin/activate
171
+ pip install -e ".[dev]"
172
+ pytest
173
+ python -m build && twine check dist/*
174
+ ```
175
+
176
+ ## License
177
+
178
+ MIT © Michael Yazdani. See [LICENSE](LICENSE).
@@ -0,0 +1,48 @@
1
+ bytesim/__init__.py,sha256=Ewcp6sGbLRMp8bLmlBrcXTSYrWs1YV4UPhXa7XL6Eu4,399
2
+ bytesim/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30
3
+ bytesim/cli.py,sha256=QFSKs828d-ymEjIKIIduYOq9-f6xIhtXcaNV1hxW1iY,1836
4
+ bytesim/constants.py,sha256=8YIsJ-NK3kPJlL3ivFlS0YDNUEYOtNuGv-Z4CKjdoUA,1513
5
+ bytesim/demos.py,sha256=mAIVbhGytT_99Tx9-7sydvVPk5_309PsBIAkgzm7nN0,3252
6
+ bytesim/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ bytesim/scripting.py,sha256=3qMfukdImjbHqYJOAmJKm0sm5MQO9N9oLSiCs6CImlU,3776
8
+ bytesim/synthesis.py,sha256=SyHIREMxyoIHxD28NmHDumFPSYmnVA8qwq3vzhrs7OM,27960
9
+ bytesim/topology.py,sha256=wuxqPGF4xLC34gD1eEt3dFEEitskg1uHqI6NRtC_DfQ,24676
10
+ bytesim/model/__init__.py,sha256=GfNW68cYmWQvteIJvTnBJEJrpk-CsMWTp-2wajNgCmU,254
11
+ bytesim/model/addressing.py,sha256=ugs7nbObzz4tDgXJK-ylDNdNXBGRzvrXpe1h2ZGw96M,7471
12
+ bytesim/model/base.py,sha256=v9KZyE_TY-bw3VxOsGBa3B7DHO9h_Qb7YcmWuoU_C_0,2714
13
+ bytesim/model/cloning.py,sha256=y4OzZKBTa5fRiHeqjWKdWzqcWiPRpzK7E6VQ3J2P8Mg,12480
14
+ bytesim/model/equivalence.py,sha256=njKmkvPnpl5kJqXwZ4o4vWWYNMinsLTf9rw0JBvxE1c,3668
15
+ bytesim/model/groups.py,sha256=Huk3BeVoRtNydnRJBJ27kfFzPGH84-W2NjUh2qxf058,5928
16
+ bytesim/model/ha.py,sha256=56FGlLgFroB7AK3z95atcW6fi71xM0DskGVIZ4D8AkA,4643
17
+ bytesim/model/layer2.py,sha256=4f9PuTg4jaIlXB4hPc6PgrnMSRm3t-eDFFvhXmLIXAc,4404
18
+ bytesim/model/layout.py,sha256=DMMRU9DVZCdaDN_ycO-4OfP71nxefOX6wStt0Tt0B2w,9064
19
+ bytesim/model/naming.py,sha256=tIc9nqLitoMdVdBvmAALI4437WCZ2Iv_47G1onV_IMk,4928
20
+ bytesim/model/network.py,sha256=TYU552egQtsLbasw9WUkI8Kn6x_ia25lK0ZKVbHC3FY,5818
21
+ bytesim/model/routing.py,sha256=eh5vTFl7SWlokia8vBdM1q_KjZG3_RGc0Ki84uQ8ScE,5382
22
+ bytesim/model/stp.py,sha256=p9RLiCBvJSn5oeD4sl_2blff7m4i3GX9ucUjIaSVCzw,14393
23
+ bytesim/model/types.py,sha256=NoYyF5ZfWa-URe30wzqe4VlfTdxcXuGOv6-s2bZS4o0,2781
24
+ bytesim/tui/__init__.py,sha256=OIBGeSC0sJ0ybcCzFky9U8slgG03-6Lo92UZI7H6ddM,88
25
+ bytesim/tui/app.py,sha256=czaWUrz8jy5slmQqsgfpKJQNQuosuoF6bqJvs2P8RbA,6589
26
+ bytesim/tui/cables.py,sha256=zf7LnSu8Xb2BR0JuPfr4SS-YEbWOOuhFznZTLq9ntyE,2488
27
+ bytesim/tui/canvas.py,sha256=JxtJrlJdLBZv2FZmco6gS-A-2oOSomIYEo8WIBw2Wt8,20715
28
+ bytesim/tui/command_input.py,sha256=yLJkvVHZuLIpX0cD7MYTUgZ0PKiFX34Lewt2L3dMePM,993
29
+ bytesim/tui/help.py,sha256=k_FQ0I5K4Rf6llMOp33qL55_z1UAVVoqI1W-sHCoj1s,4652
30
+ bytesim/tui/inspector.py,sha256=CEg1e58-pFjBwW2Qc8ZZ-TOs-ueHMETREKuydU_e3qg,6572
31
+ bytesim/tui/theme.py,sha256=Q0N-G6kJqk57oPqTaYlddxGNhNpYumW-WxKjHmSiYlo,480
32
+ bytesim/tui/commands/__init__.py,sha256=fcmi2KBXO6wQy0dfLkM5Ya5PRqkUbHLvn16h63xbUdI,84
33
+ bytesim/tui/commands/addressing.py,sha256=-vs3Rn16EWi34_r9sVxSZyNOBnM7zchnI0A7mctGUKE,10622
34
+ bytesim/tui/commands/base.py,sha256=eXPZEUQ0SWKdQBXdPJq0dql6-3rqbzDSBD4ccYhFUNc,1277
35
+ bytesim/tui/commands/groups.py,sha256=d7nv39ouQscAsw6jgfy96oTD_l7F8Mb_5WuItOoSFk8,10825
36
+ bytesim/tui/commands/history.py,sha256=msSHRVySMBIMH8_Jr06pjbuN6kmtN6xwLgF_NaowbXY,4942
37
+ bytesim/tui/commands/interpreter.py,sha256=xHvnzJ9gwRDg7obsS3cJ2owTP55zdEl9pMC9KE5Qvss,3251
38
+ bytesim/tui/commands/naming.py,sha256=wO-9CM8YRDcbs957e4A0oLhD_7Ype4h9SufhRU2ph7o,3122
39
+ bytesim/tui/commands/registry.py,sha256=Gas3rglMrz2rD5e3myNzL4oGkE2bkUWDvgWYoKBJ3aQ,951
40
+ bytesim/tui/commands/scripting.py,sha256=LQ6Zz5WyOCKlTVQ-3z8RjCceufeN7to4koP8uCIyI0I,2633
41
+ bytesim/tui/commands/switching.py,sha256=E9WE6qSCmsiFr1qxnmMaNOW-PBdfC5y7vT8o1NrP8sc,8958
42
+ bytesim/tui/commands/topology.py,sha256=nJ2v6a5Deu-oKbPtPd9GG4_xdFQB3zMYJXONSOGV-Yk,11828
43
+ bytesim/tui/commands/traffic.py,sha256=2Vaxosg1xZ21xnJDqTp5KQ2zd9GJofPnbLMlTu9gIeM,13912
44
+ bytesim-0.1.0.dist-info/METADATA,sha256=HHVbTM__4sjSZt34WHYGVAw-jXB4ZCNZAuACsYAx6uY,8802
45
+ bytesim-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
46
+ bytesim-0.1.0.dist-info/entry_points.txt,sha256=0TVPSg_gO-nuL7VmnswUn16V6hEjIl3baj9_cKS0D-M,45
47
+ bytesim-0.1.0.dist-info/licenses/LICENSE,sha256=tCjN4t4dQq1IAFqmZrk1GXnbkBlNPIjUNn06h5gpAQY,1072
48
+ bytesim-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ bytesim = bytesim.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Michael Yazdani
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.