plexus-python 0.4.1__tar.gz → 0.4.3__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.
- {plexus_python-0.4.1 → plexus_python-0.4.3}/CHANGELOG.md +19 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/PKG-INFO +1 -1
- {plexus_python-0.4.1 → plexus_python-0.4.3}/plexus/__init__.py +1 -1
- {plexus_python-0.4.1 → plexus_python-0.4.3}/plexus/cli.py +232 -21
- {plexus_python-0.4.1 → plexus_python-0.4.3}/pyproject.toml +1 -1
- {plexus_python-0.4.1 → plexus_python-0.4.3}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/.github/workflows/ci.yml +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/.github/workflows/publish.yml +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/.gitignore +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/AGENTS.md +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/API.md +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/CODE_OF_CONDUCT.md +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/CONTRIBUTING.md +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/LICENSE +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/README.md +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/SECURITY.md +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/examples/README.md +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/examples/basic.py +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/examples/can.py +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/examples/i2c_bme280.py +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/examples/mavlink.py +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/examples/mqtt.py +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/plexus/buffer.py +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/plexus/client.py +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/plexus/config.py +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/plexus/ws.py +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/scripts/plexus.service +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/scripts/scan_buses.py +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/scripts/setup.sh +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/tests/test_basic.py +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/tests/test_buffer.py +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/tests/test_config.py +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/tests/test_retry.py +0 -0
- {plexus_python-0.4.1 → plexus_python-0.4.3}/tests/test_ws.py +0 -0
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.3] - 2026-04-27 - Re-release of 0.4.2 with correct __version__
|
|
4
|
+
|
|
5
|
+
The 0.4.2 wheel shipped with `plexus.__version__ == "0.4.1"` because the
|
|
6
|
+
tag was cut before the `__init__.py` bump landed. 0.4.3 is the same code
|
|
7
|
+
with `__version__ = "0.4.3"`. 0.4.2 has been yanked.
|
|
8
|
+
|
|
9
|
+
## [0.4.2] - 2026-04-27 - CLI auth: branded success page + auto-redirect
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- `plexus/cli.py` — the localhost callback's success and error pages now
|
|
14
|
+
match the Plexus app's dark aesthetic (black background, zinc-800
|
|
15
|
+
bordered card, white headlines, monospace URL, status-color badge).
|
|
16
|
+
- After a successful `plexus init`, the browser tab now auto-redirects
|
|
17
|
+
to the configured app endpoint (`PLEXUS_ENDPOINT`, default
|
|
18
|
+
`https://app.plexus.company`) after a 10-second countdown, so first-
|
|
19
|
+
time users land on their dashboard without having to navigate there
|
|
20
|
+
manually. Falls back to `<meta http-equiv="refresh">` when JS is off.
|
|
21
|
+
|
|
3
22
|
## [0.4.1] - 2026-04-27 - CI fixes for 0.4.0
|
|
4
23
|
|
|
5
24
|
### Fixed
|
|
@@ -35,41 +35,245 @@ from . import config
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
DEFAULT_TIMEOUT_SECONDS = 300
|
|
38
|
-
|
|
38
|
+
SUCCESS_REDIRECT_SECONDS = 10
|
|
39
|
+
SUCCESS_HTML_TEMPLATE = """<!doctype html>
|
|
39
40
|
<html lang="en">
|
|
40
41
|
<head>
|
|
41
42
|
<meta charset="utf-8" />
|
|
42
43
|
<title>Plexus CLI</title>
|
|
43
44
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
45
|
+
<meta http-equiv="refresh" content="{seconds};url={target}" />
|
|
44
46
|
<style>
|
|
45
|
-
:root { color-scheme:
|
|
47
|
+
:root {{ color-scheme: dark; }}
|
|
48
|
+
* {{ box-sizing: border-box; }}
|
|
49
|
+
html, body {{ height: 100%; }}
|
|
50
|
+
body {{
|
|
51
|
+
margin: 0;
|
|
52
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Inter',
|
|
53
|
+
'Segoe UI', system-ui, sans-serif;
|
|
54
|
+
background: #000;
|
|
55
|
+
color: #fafafa;
|
|
56
|
+
display: flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
justify-content: center;
|
|
59
|
+
padding: 24px;
|
|
60
|
+
-webkit-font-smoothing: antialiased;
|
|
61
|
+
}}
|
|
62
|
+
.shell {{
|
|
63
|
+
width: 100%;
|
|
64
|
+
max-width: 380px;
|
|
65
|
+
display: flex;
|
|
66
|
+
flex-direction: column;
|
|
67
|
+
align-items: center;
|
|
68
|
+
gap: 24px;
|
|
69
|
+
}}
|
|
70
|
+
.brand {{
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
gap: 10px;
|
|
74
|
+
color: #e4e4e7;
|
|
75
|
+
font-size: 14px;
|
|
76
|
+
font-weight: 500;
|
|
77
|
+
letter-spacing: -0.01em;
|
|
78
|
+
}}
|
|
79
|
+
.brand .mark {{
|
|
80
|
+
width: 22px;
|
|
81
|
+
height: 22px;
|
|
82
|
+
border-radius: 6px;
|
|
83
|
+
background: linear-gradient(135deg, #fafafa 0%, #71717a 100%);
|
|
84
|
+
display: inline-block;
|
|
85
|
+
}}
|
|
86
|
+
.card {{
|
|
87
|
+
width: 100%;
|
|
88
|
+
background: #09090b;
|
|
89
|
+
border: 1px solid #27272a;
|
|
90
|
+
border-radius: 12px;
|
|
91
|
+
padding: 28px 28px 24px;
|
|
92
|
+
text-align: center;
|
|
93
|
+
}}
|
|
94
|
+
.check {{
|
|
95
|
+
width: 36px;
|
|
96
|
+
height: 36px;
|
|
97
|
+
border-radius: 999px;
|
|
98
|
+
background: rgba(34, 197, 94, 0.12);
|
|
99
|
+
color: #4ade80;
|
|
100
|
+
display: inline-flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
justify-content: center;
|
|
103
|
+
margin: 0 auto 16px;
|
|
104
|
+
}}
|
|
105
|
+
.check svg {{ width: 18px; height: 18px; }}
|
|
106
|
+
h1 {{
|
|
107
|
+
margin: 0 0 6px;
|
|
108
|
+
font-size: 16px;
|
|
109
|
+
font-weight: 600;
|
|
110
|
+
color: #fafafa;
|
|
111
|
+
letter-spacing: -0.01em;
|
|
112
|
+
}}
|
|
113
|
+
.lede {{
|
|
114
|
+
margin: 0 0 20px;
|
|
115
|
+
color: #a1a1aa;
|
|
116
|
+
font-size: 13px;
|
|
117
|
+
line-height: 1.5;
|
|
118
|
+
}}
|
|
119
|
+
.meta {{
|
|
120
|
+
margin-top: 20px;
|
|
121
|
+
padding-top: 16px;
|
|
122
|
+
border-top: 1px solid #18181b;
|
|
123
|
+
color: #71717a;
|
|
124
|
+
font-size: 12px;
|
|
125
|
+
}}
|
|
126
|
+
.meta a {{
|
|
127
|
+
color: #a1a1aa;
|
|
128
|
+
text-decoration: none;
|
|
129
|
+
font-family: ui-monospace, SFMono-Regular, 'SF Mono',
|
|
130
|
+
Menlo, Consolas, monospace;
|
|
131
|
+
}}
|
|
132
|
+
.meta a:hover {{ color: #fafafa; }}
|
|
133
|
+
#countdown {{
|
|
134
|
+
font-variant-numeric: tabular-nums;
|
|
135
|
+
color: #e4e4e7;
|
|
136
|
+
}}
|
|
137
|
+
</style>
|
|
138
|
+
</head>
|
|
139
|
+
<body>
|
|
140
|
+
<div class="shell">
|
|
141
|
+
<div class="brand">
|
|
142
|
+
<span class="mark" aria-hidden="true"></span>
|
|
143
|
+
<span>Plexus</span>
|
|
144
|
+
</div>
|
|
145
|
+
<div class="card">
|
|
146
|
+
<div class="check" aria-hidden="true">
|
|
147
|
+
<svg viewBox="0 0 20 20" fill="none"
|
|
148
|
+
stroke="currentColor" stroke-width="2.5"
|
|
149
|
+
stroke-linecap="round" stroke-linejoin="round">
|
|
150
|
+
<polyline points="5 10.5 8.5 14 15 7" />
|
|
151
|
+
</svg>
|
|
152
|
+
</div>
|
|
153
|
+
<h1>You’re all set</h1>
|
|
154
|
+
<p class="lede">
|
|
155
|
+
Return to your terminal — the CLI has your key.
|
|
156
|
+
</p>
|
|
157
|
+
<div class="meta">
|
|
158
|
+
Opening <a href="{target}">{target_label}</a> in
|
|
159
|
+
<span id="countdown">{seconds}</span>s…
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
<script>
|
|
164
|
+
(function () {{
|
|
165
|
+
var n = {seconds};
|
|
166
|
+
var el = document.getElementById('countdown');
|
|
167
|
+
var t = setInterval(function () {{
|
|
168
|
+
n -= 1;
|
|
169
|
+
if (el) el.textContent = n;
|
|
170
|
+
if (n <= 0) {{
|
|
171
|
+
clearInterval(t);
|
|
172
|
+
window.location.replace({target_js!s});
|
|
173
|
+
}}
|
|
174
|
+
}}, 1000);
|
|
175
|
+
}})();
|
|
176
|
+
</script>
|
|
177
|
+
</body>
|
|
178
|
+
</html>"""
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _success_html(target: str) -> bytes:
|
|
182
|
+
label = target.replace("https://", "").replace("http://", "").rstrip("/")
|
|
183
|
+
return SUCCESS_HTML_TEMPLATE.format(
|
|
184
|
+
seconds=SUCCESS_REDIRECT_SECONDS,
|
|
185
|
+
target=target,
|
|
186
|
+
target_label=label,
|
|
187
|
+
target_js=repr(target),
|
|
188
|
+
).encode("utf-8")
|
|
189
|
+
|
|
190
|
+
ERROR_HTML = """<!doctype html>
|
|
191
|
+
<html lang="en">
|
|
192
|
+
<head>
|
|
193
|
+
<meta charset="utf-8" />
|
|
194
|
+
<title>Plexus CLI</title>
|
|
195
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
196
|
+
<style>
|
|
197
|
+
:root { color-scheme: dark; }
|
|
198
|
+
* { box-sizing: border-box; }
|
|
199
|
+
html, body { height: 100%; }
|
|
46
200
|
body {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
201
|
+
margin: 0;
|
|
202
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Inter',
|
|
203
|
+
'Segoe UI', system-ui, sans-serif;
|
|
204
|
+
background: #000;
|
|
205
|
+
color: #fafafa;
|
|
206
|
+
display: flex;
|
|
207
|
+
align-items: center;
|
|
208
|
+
justify-content: center;
|
|
209
|
+
padding: 24px;
|
|
210
|
+
-webkit-font-smoothing: antialiased;
|
|
211
|
+
}
|
|
212
|
+
.shell {
|
|
213
|
+
width: 100%;
|
|
214
|
+
max-width: 380px;
|
|
215
|
+
display: flex;
|
|
216
|
+
flex-direction: column;
|
|
217
|
+
align-items: center;
|
|
218
|
+
gap: 24px;
|
|
219
|
+
}
|
|
220
|
+
.brand {
|
|
221
|
+
display: flex; align-items: center; gap: 10px;
|
|
222
|
+
color: #e4e4e7; font-size: 14px; font-weight: 500;
|
|
223
|
+
letter-spacing: -0.01em;
|
|
224
|
+
}
|
|
225
|
+
.brand .mark {
|
|
226
|
+
width: 22px; height: 22px; border-radius: 6px;
|
|
227
|
+
background: linear-gradient(135deg, #fafafa 0%, #71717a 100%);
|
|
228
|
+
display: inline-block;
|
|
50
229
|
}
|
|
51
230
|
.card {
|
|
52
|
-
|
|
53
|
-
|
|
231
|
+
width: 100%;
|
|
232
|
+
background: #09090b;
|
|
233
|
+
border: 1px solid #27272a;
|
|
234
|
+
border-radius: 12px;
|
|
235
|
+
padding: 28px;
|
|
236
|
+
text-align: center;
|
|
237
|
+
}
|
|
238
|
+
.icon {
|
|
239
|
+
width: 36px; height: 36px; border-radius: 999px;
|
|
240
|
+
background: rgba(239, 68, 68, 0.12);
|
|
241
|
+
color: #f87171;
|
|
242
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
243
|
+
margin: 0 auto 16px;
|
|
244
|
+
}
|
|
245
|
+
.icon svg { width: 18px; height: 18px; }
|
|
246
|
+
h1 {
|
|
247
|
+
margin: 0 0 6px; font-size: 16px; font-weight: 600;
|
|
248
|
+
color: #fafafa; letter-spacing: -0.01em;
|
|
249
|
+
}
|
|
250
|
+
p {
|
|
251
|
+
margin: 0; color: #a1a1aa; font-size: 13px; line-height: 1.5;
|
|
54
252
|
}
|
|
55
|
-
h1 { margin: 0 0 8px; font-size: 18px; }
|
|
56
|
-
p { margin: 0; color: #888; font-size: 14px; }
|
|
57
253
|
</style>
|
|
58
254
|
</head>
|
|
59
255
|
<body>
|
|
60
|
-
<div class="
|
|
61
|
-
<
|
|
62
|
-
|
|
256
|
+
<div class="shell">
|
|
257
|
+
<div class="brand">
|
|
258
|
+
<span class="mark" aria-hidden="true"></span>
|
|
259
|
+
<span>Plexus</span>
|
|
260
|
+
</div>
|
|
261
|
+
<div class="card">
|
|
262
|
+
<div class="icon" aria-hidden="true">
|
|
263
|
+
<svg viewBox="0 0 20 20" fill="none"
|
|
264
|
+
stroke="currentColor" stroke-width="2.5"
|
|
265
|
+
stroke-linecap="round" stroke-linejoin="round">
|
|
266
|
+
<line x1="6" y1="6" x2="14" y2="14" />
|
|
267
|
+
<line x1="14" y1="6" x2="6" y2="14" />
|
|
268
|
+
</svg>
|
|
269
|
+
</div>
|
|
270
|
+
<h1>Authorization failed</h1>
|
|
271
|
+
<p>Return to your terminal for details.</p>
|
|
272
|
+
</div>
|
|
63
273
|
</div>
|
|
64
274
|
</body>
|
|
65
275
|
</html>""".encode("utf-8")
|
|
66
276
|
|
|
67
|
-
ERROR_HTML = """<!doctype html>
|
|
68
|
-
<html><head><meta charset="utf-8" /><title>Plexus CLI</title></head>
|
|
69
|
-
<body><pre style="font-family:ui-monospace,monospace;padding:24px">
|
|
70
|
-
Plexus CLI authorization failed. Return to your terminal for details.
|
|
71
|
-
</pre></body></html>""".encode("utf-8")
|
|
72
|
-
|
|
73
277
|
|
|
74
278
|
class _CallbackResult:
|
|
75
279
|
key: Optional[str] = None
|
|
@@ -83,7 +287,14 @@ def _pick_free_port() -> int:
|
|
|
83
287
|
return s.getsockname()[1]
|
|
84
288
|
|
|
85
289
|
|
|
86
|
-
def _make_handler(
|
|
290
|
+
def _make_handler(
|
|
291
|
+
result: _CallbackResult,
|
|
292
|
+
expected_state: str,
|
|
293
|
+
done: threading.Event,
|
|
294
|
+
redirect_target: str,
|
|
295
|
+
):
|
|
296
|
+
success_html = _success_html(redirect_target)
|
|
297
|
+
|
|
87
298
|
class Handler(http.server.BaseHTTPRequestHandler):
|
|
88
299
|
# Silence the default request log — we don't want CLI noise.
|
|
89
300
|
def log_message(self, *_args, **_kwargs): # type: ignore[override]
|
|
@@ -123,7 +334,7 @@ def _make_handler(result: _CallbackResult, expected_state: str, done: threading.
|
|
|
123
334
|
self.send_response(200)
|
|
124
335
|
self.send_header("Content-Type", "text/html; charset=utf-8")
|
|
125
336
|
self.end_headers()
|
|
126
|
-
self.wfile.write(
|
|
337
|
+
self.wfile.write(success_html)
|
|
127
338
|
done.set()
|
|
128
339
|
|
|
129
340
|
return Handler
|
|
@@ -165,7 +376,7 @@ def cmd_init(args: argparse.Namespace) -> int:
|
|
|
165
376
|
|
|
166
377
|
result = _CallbackResult()
|
|
167
378
|
done = threading.Event()
|
|
168
|
-
handler = _make_handler(result, state, done)
|
|
379
|
+
handler = _make_handler(result, state, done, redirect_target=endpoint)
|
|
169
380
|
|
|
170
381
|
server = socketserver.TCPServer(("127.0.0.1", port), handler)
|
|
171
382
|
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|