muxplex-deck 0.5.1__tar.gz → 0.5.2__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.
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/PKG-INFO +1 -1
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/pyproject.toml +1 -1
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/cli.py +26 -28
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/config.py +3 -3
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/service.py +67 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/README.md +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/deck_probe/__init__.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/deck_probe/__main__.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/deck_probe/capabilities.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/deck_probe/events.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/deck_probe/main.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/deck_probe/rendering.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/__init__.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/__main__.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/attention.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/device.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/device_real.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/emulator.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/focus.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/hidhelp.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/init_wizard.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/interaction.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/layout.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/main.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/rendering.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/statusfile.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/usbnode.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/views.py +0 -0
- {muxplex_deck-0.5.1 → muxplex_deck-0.5.2}/src/muxplex_deck/wsl.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: muxplex-deck
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
4
4
|
Summary: Hardware probe/diagnostic app for the Elgato Stream Deck+ (seed of the muxplex sidecar)
|
|
5
5
|
Author: Amplifier
|
|
6
6
|
Author-email: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
|
|
@@ -16,6 +16,7 @@ import argparse
|
|
|
16
16
|
import contextlib
|
|
17
17
|
import json
|
|
18
18
|
import platform
|
|
19
|
+
import shutil
|
|
19
20
|
import subprocess
|
|
20
21
|
import sys
|
|
21
22
|
import time
|
|
@@ -593,38 +594,35 @@ def check_server_reachable(server_url: str, ca_file: Path | None) -> tuple[str,
|
|
|
593
594
|
|
|
594
595
|
|
|
595
596
|
def check_service_status() -> tuple[str, str]:
|
|
596
|
-
"""
|
|
597
|
+
"""Service-state check: not installed / installed but not running / running.
|
|
598
|
+
|
|
599
|
+
Three distinct states, not two -- `service_is_installed()` (unit/plist
|
|
600
|
+
file exists) and `service_is_active()` (currently running) are checked
|
|
601
|
+
independently, so an installed-but-crash-looping service is reported
|
|
602
|
+
honestly instead of as "not installed" (which used to tell the user to
|
|
603
|
+
re-run `service install` for a service that was already installed and
|
|
604
|
+
failing -- see AGENTS.md for the incident this fixes).
|
|
605
|
+
"""
|
|
606
|
+
from .service import service_is_active, service_is_installed
|
|
607
|
+
|
|
597
608
|
if sys.platform == "darwin":
|
|
598
|
-
|
|
609
|
+
manager, tool = "launchd", "launchctl"
|
|
610
|
+
else:
|
|
611
|
+
manager, tool = "systemd", "systemctl"
|
|
599
612
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
capture_output=True,
|
|
605
|
-
text=True,
|
|
606
|
-
timeout=5,
|
|
607
|
-
check=False,
|
|
608
|
-
)
|
|
609
|
-
except FileNotFoundError:
|
|
610
|
-
return "warn", "Service: launchctl not found"
|
|
611
|
-
if result.returncode == 0:
|
|
612
|
-
return "ok", "Service: installed (launchd)"
|
|
613
|
+
if shutil.which(tool) is None:
|
|
614
|
+
return "warn", f"Service: {tool} not found -- run muxplex-deck directly"
|
|
615
|
+
|
|
616
|
+
if not service_is_installed():
|
|
613
617
|
return "warn", "Service: not installed -- run: muxplex-deck service install"
|
|
614
618
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
)
|
|
623
|
-
except FileNotFoundError:
|
|
624
|
-
return "warn", "Service: systemctl not found -- run muxplex-deck directly"
|
|
625
|
-
if result.returncode == 0:
|
|
626
|
-
return "ok", f"Service: {result.stdout.strip()} (systemd)"
|
|
627
|
-
return "warn", "Service: not installed -- run: muxplex-deck service install"
|
|
619
|
+
if service_is_active():
|
|
620
|
+
return "ok", f"Service: installed and running ({manager})"
|
|
621
|
+
|
|
622
|
+
return "warn", (
|
|
623
|
+
f"Service: installed ({manager}) but not running -- check: "
|
|
624
|
+
"muxplex-deck service logs"
|
|
625
|
+
)
|
|
628
626
|
|
|
629
627
|
|
|
630
628
|
_CHECK_MARKS: dict[str, str] = {
|
|
@@ -145,9 +145,9 @@ def load_config(config_path: str | None = None) -> Config:
|
|
|
145
145
|
if not path.exists():
|
|
146
146
|
raise ConfigError(
|
|
147
147
|
f"Config file not found: {path}\n"
|
|
148
|
-
"
|
|
149
|
-
'
|
|
150
|
-
"
|
|
148
|
+
"Run: muxplex-deck init\n"
|
|
149
|
+
"(or create it by hand with at least a 'server_url' field, e.g.:\n"
|
|
150
|
+
' {"server_url": "https://<your-server>:8088"})'
|
|
151
151
|
)
|
|
152
152
|
|
|
153
153
|
try:
|
|
@@ -44,6 +44,8 @@ import sys
|
|
|
44
44
|
import time
|
|
45
45
|
from pathlib import Path
|
|
46
46
|
|
|
47
|
+
from . import config as config_mod
|
|
48
|
+
|
|
47
49
|
# ---------------------------------------------------------------------------
|
|
48
50
|
# Constants
|
|
49
51
|
# ---------------------------------------------------------------------------
|
|
@@ -307,6 +309,22 @@ def service_is_active() -> bool:
|
|
|
307
309
|
return result.returncode == 0
|
|
308
310
|
|
|
309
311
|
|
|
312
|
+
def service_is_installed() -> bool:
|
|
313
|
+
"""Is the muxplex-deck unit/plist file present on disk?
|
|
314
|
+
|
|
315
|
+
Independent of `service_is_active()` -- a service that is installed but
|
|
316
|
+
crash-looping (or simply stopped) is still installed. `doctor`'s
|
|
317
|
+
`check_service_status` used to conflate "not active" with "not
|
|
318
|
+
installed" and recommend `service install` for a service that was
|
|
319
|
+
already installed and failing; this is the file-existence half of the
|
|
320
|
+
fix (see AGENTS.md for the incident: 1113 restarts, then told to
|
|
321
|
+
"install" a service that was already there).
|
|
322
|
+
"""
|
|
323
|
+
if _is_darwin():
|
|
324
|
+
return _LAUNCHD_PLIST_PATH.exists()
|
|
325
|
+
return _SYSTEMD_UNIT_PATH.exists()
|
|
326
|
+
|
|
327
|
+
|
|
310
328
|
def _enable_linger() -> None:
|
|
311
329
|
"""Best-effort `loginctl enable-linger` so the service survives logout.
|
|
312
330
|
|
|
@@ -346,6 +364,37 @@ def _print_next_steps() -> None:
|
|
|
346
364
|
print()
|
|
347
365
|
|
|
348
366
|
|
|
367
|
+
def _config_ready() -> tuple[bool, str | None]:
|
|
368
|
+
"""Return (True, None) if config is loadable, else (False, ConfigError message).
|
|
369
|
+
|
|
370
|
+
Calls the exact same `config.load_config()` the installed unit's own
|
|
371
|
+
`ExecStart` (`... run`, no `--config` flag) will call at startup, so
|
|
372
|
+
"ready to install" and "ready to run" can never diverge. A config that
|
|
373
|
+
fails this check would make the service crash immediately once
|
|
374
|
+
started, and `Restart=always` (`KeepAlive` on launchd) would then
|
|
375
|
+
restart it every few seconds forever -- see the `service install`
|
|
376
|
+
crash-loop incident in AGENTS.md (1113 restarts on a fresh machine
|
|
377
|
+
with no config yet).
|
|
378
|
+
"""
|
|
379
|
+
try:
|
|
380
|
+
config_mod.load_config(None)
|
|
381
|
+
except config_mod.ConfigError as exc:
|
|
382
|
+
return False, str(exc)
|
|
383
|
+
return True, None
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def _print_config_not_ready(config_error: str | None) -> None:
|
|
387
|
+
"""Explain why install is stopping short of enabling/starting the service."""
|
|
388
|
+
_print_guidance_block(
|
|
389
|
+
"Not enabling or starting yet -- configuration is incomplete:\n"
|
|
390
|
+
+ (config_error or "unknown configuration error")
|
|
391
|
+
)
|
|
392
|
+
print()
|
|
393
|
+
print(" Run muxplex-deck init to create it, then run this command again:")
|
|
394
|
+
print(" muxplex-deck service install")
|
|
395
|
+
print()
|
|
396
|
+
|
|
397
|
+
|
|
349
398
|
def _systemd_install() -> None:
|
|
350
399
|
print("\nmuxplex-deck service install (systemd --user)\n")
|
|
351
400
|
|
|
@@ -362,6 +411,15 @@ def _systemd_install() -> None:
|
|
|
362
411
|
subprocess.run(["systemctl", "--user", "daemon-reload"], check=True)
|
|
363
412
|
_step_ok("Reloaded the systemd user daemon")
|
|
364
413
|
|
|
414
|
+
# Never enable/start a unit whose config we already know will make it
|
|
415
|
+
# crash on launch -- see `_config_ready()`. The unit file above is
|
|
416
|
+
# harmless to have on disk either way; only the "make it run" steps are
|
|
417
|
+
# gated.
|
|
418
|
+
config_ready, config_error = _config_ready()
|
|
419
|
+
if not config_ready:
|
|
420
|
+
_print_config_not_ready(config_error)
|
|
421
|
+
return
|
|
422
|
+
|
|
365
423
|
subprocess.run(
|
|
366
424
|
["systemctl", "--user", "enable", "--now", "muxplex-deck"], check=True
|
|
367
425
|
)
|
|
@@ -567,6 +625,15 @@ def _launchd_install() -> None:
|
|
|
567
625
|
_LAUNCHD_PLIST_PATH.write_text(plist_content)
|
|
568
626
|
_step_ok(f"Wrote plist: {_LAUNCHD_PLIST_PATH}")
|
|
569
627
|
|
|
628
|
+
# Never bootstrap (load + start) a job whose config we already know will
|
|
629
|
+
# make it crash on launch -- see `_config_ready()`. `KeepAlive` would
|
|
630
|
+
# then have launchd relaunch it forever. The plist above is harmless to
|
|
631
|
+
# have on disk either way; only the "make it run" step is gated.
|
|
632
|
+
config_ready, config_error = _config_ready()
|
|
633
|
+
if not config_ready:
|
|
634
|
+
_print_config_not_ready(config_error)
|
|
635
|
+
return
|
|
636
|
+
|
|
570
637
|
result = _launchd_bootstrap()
|
|
571
638
|
if result.returncode == 0:
|
|
572
639
|
_step_ok("Loaded + started the service (launchctl bootstrap)")
|
|
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
|