-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathpyproject.toml
More file actions
179 lines (164 loc) · 7.73 KB
/
Copy pathpyproject.toml
File metadata and controls
179 lines (164 loc) · 7.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
[project]
name = "meshtastic-mcp"
dynamic = ["version"]
description = "MCP server + agent skills for AI tooling to discover, drive, observe, and test Meshtastic devices and apps: device discovery, serial/TCP transport, admin, recorder/observability, PlatformIO build/flash (optional), and hardware-free Android-emulator e2e."
readme = "README.md"
requires-python = ">=3.11"
license = "GPL-3.0-only"
license-files = ["LICENSE"]
authors = [{ name = "Meshtastic" }]
keywords = ["meshtastic", "mcp", "lora", "mesh", "testing", "agent", "ai"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development :: Testing",
"Topic :: System :: Hardware",
]
# Portable core: works against any connected device (serial or TCP), no firmware checkout.
dependencies = [
"fastmcp>=3.4.5,<4",
"mcp>=1.2",
"pyserial>=3.5",
"meshtastic>=2.7.8",
"platformdirs>=4.0",
]
[project.optional-dependencies]
# Channel AES-CTR frame crafting for `inject_frame` / `meshinject` (encrypted mode).
# Lazy-imported (inject.py); decoded/ciphertext/fuzz modes work without it.
inject = ["cryptography>=42.0"]
# Resident uiautomator2 server for the Android UI-driving fast path (emulator/u2.py):
# ~100x faster hierarchy dumps, Unicode-safe input, atomic find-then-tap. Import-guarded;
# without it every driving helper falls back to plain adb.
android-fast = ["uiautomator2>=3.0"]
# Hardware test harness (pytest + live TUI).
test = [
"pytest>=8",
"pytest-html>=4",
"pytest-reportlog>=0.4",
"pytest-timeout>=2.3",
"coverage[toml]>=7",
"pytest-cov>=5",
"pyyaml>=6",
"textual>=0.50",
]
# On-device OLED UI testing + `capture_screen` (camera + OCR; ~100 MB models).
ui = [
"opencv-python-headless>=4.9",
"numpy>=1.26",
# easyocr (deep-learning OCR) pulls torch, which has no macOS x86_64 wheel
# since torch 2.3 — so skip it on Intel macs and fall back to the pytesseract
# OCR backend there (`brew install tesseract`). Other platforms get easyocr.
"easyocr>=1.7; sys_platform != 'darwin' or platform_machine != 'x86_64'",
"pytesseract>=0.3",
"Pillow>=10.0",
"rich-pixels>=3.0",
]
ui-min = ["opencv-python-headless>=4.9", "numpy>=1.26"]
# Richer synthetic node names for the replay sim (Faker person names/usernames/
# callsigns). Optional: the sim falls back to its built-in adjective+noun pools
# when Faker is absent, so this only *improves* names — nothing depends on it.
sim = ["faker>=20"]
# macOS menu-bar controller for the FleetSuite service (start/stop/status).
# macOS-only; the service and nightly run fine without it. See scripts/install-menubar.sh.
menubar = ["rumps>=0.4; sys_platform == 'darwin'"]
# FleetSuite: the FastAPI web backend + Vue SPA (the bench control plane). The
# desktop window uses pywebview; `--browser` mode serves headless without it.
web = [
"fastapi>=0.110",
"uvicorn[standard]>=0.27",
"aiosqlite>=0.19",
"pywebview>=5.0",
"requests>=2.31",
]
# The `sdr` capability is the RF-compliance oracle (rf_scan/rf_confirm_tx): captures
# raw IQ from an RTL-SDR and checks it against lora_compliance's prediction of what
# the device's configured region/preset should produce on air.
#
# `pyrtlsdrlib` ships a prebuilt librtlsdr (macOS/Windows/Linux wheels) that matches
# pyrtlsdr's expected ABI, so `[sdr]` works out of the box. It is preferred by
# pyrtlsdr's loader and avoids the common trap where a *system* librtlsdr is the
# osmocom fork (e.g. Homebrew's) that lacks `rtlsdr_set_dithering` and makes
# `import rtlsdr` fail with an AttributeError. If you'd rather use a system librtlsdr
# (`apt install librtlsdr-dev rtl-sdr`), it must be the librtlsdr/librtlsdr (keenerd)
# fork; pyrtlsdrlib is harmless alongside it (loader falls back to the system lib).
sdr = ["pyrtlsdr>=0.3", "pyrtlsdrlib>=0.0.4", "numpy>=1.26"]
# The `tak` capability enables real TAKPacketV2 zstd-dictionary wire compression
# for the replay sim (profile tak.wire="v2"), via the meshtastic-tak SDK. Not on
# PyPI, so this is a git dependency; it pulls `zstandard`.
tak = [
"meshtastic-tak @ git+https://github.com/meshtastic/TAKPacket-SDK.git#subdirectory=python",
]
# The `firmware` capability needs a Meshtastic firmware checkout + PlatformIO `pio`
# on PATH (not pip-installable) — set MESHTASTIC_FIRMWARE_ROOT.
# The `emulator` capability needs the `android` CLI + `adb` on PATH (not pip-installable).
dev = ["ruff>=0.6", "mypy>=1.10", "pytest>=8"]
[project.scripts]
meshtastic-mcp = "meshtastic_mcp.__main__:main"
meshtastic-mcp-atak = "meshtastic_mcp.atak_cli:main"
meshtastic-mcp-test-tui = "meshtastic_mcp.cli.test_tui:main"
meshtastic-mcp-web = "meshtastic_mcp.web.__main__:main"
meshtastic-mcp-menubar = "meshtastic_mcp.menubar:main"
[project.urls]
Homepage = "https://github.com/meshtastic/meshtastic-mcp"
Repository = "https://github.com/meshtastic/meshtastic-mcp"
Issues = "https://github.com/meshtastic/meshtastic-mcp/issues"
[build-system]
requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"
# The [tak] extra pulls meshtastic-tak from git (not on PyPI); allow the direct ref.
[tool.hatch.metadata]
allow-direct-references = true
[tool.hatch.version]
source = "vcs"
[tool.hatch.build.hooks.vcs]
version-file = "src/meshtastic_mcp/_version.py"
[tool.hatch.build]
# The built FleetSuite SPA is gitignored (regenerated by `cd web-ui && npm run build`)
# but must ship — release builds the SPA first, then the wheel. This must be on
# [tool.hatch.build] (NOT just the wheel target): `uv build` builds the wheel FROM
# the sdist, so if the sdist drops the gitignored static files the wheel silently
# loses them too (caught by the release smoke check, v0.1.0 first attempt).
artifacts = ["src/meshtastic_mcp/web/static/**"]
[tool.hatch.build.targets.wheel]
# Skills (SKILL.md + references + helper) live inside the package dir, so hatchling ships
# them in the wheel automatically — keeping `meshtastic-mcp skills install` version-matched.
packages = ["src/meshtastic_mcp"]
[tool.pytest.ini_options]
testpaths = ["tests"]
markers = [
"firmware: needs a Meshtastic firmware checkout (variants/pio/userprefs); auto-skipped without MESHTASTIC_FIRMWARE_ROOT",
"meshtasticd: drives a native meshtasticd virtual radio; auto-skipped without MESHTASTIC_MESHTASTICD_BIN",
"atak: brings up a real ATAK emulator; auto-skipped without MESHTASTIC_MCP_ATAK_APK",
"timing: asserts on wall-clock rate/latency; may flake on a contended runner — deselect with -m 'not timing'",
]
[tool.ruff]
line-length = 100
target-version = "py311"
src = ["src", "tests"]
[tool.ruff.lint]
select = ["E", "F", "W", "I", "UP", "B", "C4", "SIM", "RUF"]
ignore = [
"B008",
"SIM105",
"SIM108", # ternary suggestions hurt readability for CRC/format math
"RUF001", "RUF002", "RUF003", # ambiguous unicode (×, –, −) is intentional in technical prose
"RUF012", # Textual widgets use class-level mutable defaults by design
]
[tool.ruff.lint.per-file-ignores]
"tests/**" = ["B011", "SIM117", "E501", "E402"] # device-output fixtures: long lines + conditional imports after skip guards
"src/meshtastic_mcp/cli/**" = ["E501"] # CLI/TUI embed rich-markup templates + help text
"src/meshtastic_mcp/camera.py" = ["E501"] # one long ffmpeg-error f-string
"src/meshtastic_mcp/web/**" = ["E501", "B008", "B904", "RUF006"] # FleetSuite SPA backend: long strings, FastAPI Depends()/HTTPException re-raises, fire-and-forget discovery tasks
[tool.mypy]
python_version = "3.11"
files = ["src/meshtastic_mcp"]
ignore_missing_imports = true # meshtastic / cv2 / pubsub ship no stubs; fastmcp does
check_untyped_defs = false
warn_unused_ignores = false
exclude = [
"_version.py",
]