solstone-linux 0.3.1__tar.gz → 0.3.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.
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/CHANGELOG.md +8 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/INSTALL.md +8 -8
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/PKG-INFO +2 -2
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/README.md +1 -1
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/pyproject.toml +1 -1
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/__init__.py +1 -1
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/cli.py +16 -19
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/config.py +9 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/dbus_service.py +3 -8
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/doctor.py +12 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/sync.py +194 -53
- solstone_linux-0.3.2/src/solstone_linux/sync_health.py +364 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/tray.py +69 -77
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/upload.py +22 -20
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_cli.py +30 -1
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_dbus_service.py +12 -13
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_doctor.py +26 -1
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_sync.py +210 -23
- solstone_linux-0.3.2/tests/test_sync_health.py +121 -0
- solstone_linux-0.3.2/tests/test_sync_health_surfaces.py +191 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_tray.py +67 -40
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_upload.py +19 -1
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/.gitignore +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/AGENTS.md +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/CLAUDE.md +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/LICENSE +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/Makefile +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/contrib/icons/hicolor/scalable/status/solstone-error.svg +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/contrib/icons/hicolor/scalable/status/solstone-paused.svg +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/contrib/icons/hicolor/scalable/status/solstone-recording.svg +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/contrib/icons/hicolor/scalable/status/solstone-syncing.svg +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/scripts/extract_changelog.sh +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/scripts/release.sh +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/activity.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/audio_detect.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/audio_mute.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/audio_recorder.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/chat_bridge.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/dbusmenu.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/icons/hicolor/scalable/status/solstone-error.svg +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/icons/hicolor/scalable/status/solstone-paused.svg +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/icons/hicolor/scalable/status/solstone-recording.svg +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/icons/hicolor/scalable/status/solstone-syncing.svg +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/install_guard.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/monitor_positions.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/observer.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/recovery.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/screencast.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/session_env.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/sni.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/solstone-linux.service.in +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/src/solstone_linux/streams.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/__init__.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_activity.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_chat_bridge.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_config.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_dbusmenu.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_extract_changelog.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_install_guard.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_monitor_positions.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_observer.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_observer_emits_stream_silent_event.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_screencast.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_screencast_stop_filters_silent_streams.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_session_env.py +0 -0
- {solstone_linux-0.3.1 → solstone_linux-0.3.2}/tests/test_streams.py +0 -0
|
@@ -4,6 +4,14 @@ All notable changes to solstone-linux are documented here.
|
|
|
4
4
|
The format is based on Keep a Changelog (https://keepachangelog.com/),
|
|
5
5
|
and this project adheres to Semantic Versioning.
|
|
6
6
|
|
|
7
|
+
## [0.3.2] - 2026-06-16
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- the tray status now tells the truth about sync. it shows "connected" only when this observer has genuinely reached your journal with nothing left to send, and clearly says when it's offline, needs updating, or needs to re-authorize, instead of looking fine while quietly falling behind. the same honest status carries across the tray, `status`, and `doctor`.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- setup no longer asks for a journal url under any path. if you ran into a lingering "journal url" prompt during setup, that's gone — setup connects to your local journal automatically, and `solstone-linux setup --server-url <url>` still points at a journal you reach directly.
|
|
14
|
+
|
|
7
15
|
## [0.3.1] - 2026-06-15
|
|
8
16
|
|
|
9
17
|
### Changed
|
|
@@ -4,7 +4,7 @@ these instructions are for a coding agent and human working together. solstone-l
|
|
|
4
4
|
|
|
5
5
|
solstone must already be installed and running. if it isn't, start there: https://solstone.app/install
|
|
6
6
|
|
|
7
|
-
> **most users install solstone-linux from PyPI in three commands** on the machine that will host the observer: `pipx install solstone-linux`, `solstone-linux install-service`, then `solstone-linux setup` (which
|
|
7
|
+
> **most users install solstone-linux from PyPI in three commands** on the machine that will host the observer: `pipx install solstone-linux`, `solstone-linux install-service`, then `solstone-linux setup` (which registers against your journal over the local `http://localhost:5015` link — no URL to type). if the observer machine reaches your solstone host directly instead, run `solstone-linux setup --server-url <journal-url>`. the instructions below are for developers building from source or troubleshooting the install.
|
|
8
8
|
|
|
9
9
|
## before you begin
|
|
10
10
|
|
|
@@ -14,7 +14,7 @@ check if solstone-linux is already installed and running:
|
|
|
14
14
|
|
|
15
15
|
```
|
|
16
16
|
systemctl --user status solstone-linux
|
|
17
|
-
|
|
17
|
+
journal observer list
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
if it's already active and connected, you're done.
|
|
@@ -24,12 +24,12 @@ if it's already active and connected, you're done.
|
|
|
24
24
|
- **system dependencies.** the observer needs PyGObject, GStreamer, and PipeWire bindings from system packages. installing these requires sudo.
|
|
25
25
|
- **stream name.** this identifies this observer's stream. the machine's hostname is the typical choice.
|
|
26
26
|
|
|
27
|
-
###
|
|
27
|
+
### journal reached directly (not over the local link)
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
By default the observer registers over the local `http://localhost:5015` link, so the journal and observer are colocated. If you reach your journal directly over HTTPS instead:
|
|
30
30
|
|
|
31
31
|
- clone anywhere; the `$(sol root)/observers` path in step 2 only applies when sol is installed locally.
|
|
32
|
-
- `solstone-linux setup
|
|
32
|
+
- run `solstone-linux setup --server-url <journal-url>` to point at that journal and auto-register the observer over HTTP, persisting the returned key. No manual key handoff is needed if the journal's observer-registration endpoint is reachable.
|
|
33
33
|
- otherwise, the install sequence below is the same.
|
|
34
34
|
|
|
35
35
|
## install sequence
|
|
@@ -66,7 +66,7 @@ this is the developer/from-source path; most installs should use the `pipx insta
|
|
|
66
66
|
|
|
67
67
|
`uv` / `pipx`: Fedora packages both (`sudo dnf install uv pipx`); Debian/Ubuntu package `pipx` but not `uv`. the PyPI install flow only needs `pipx` — `uv` is optional and used by the from-source dev workflow in the Makefile.
|
|
68
68
|
|
|
69
|
-
2. cloning into `$(sol root)/observers` is only a developer convenience for keeping observer checkouts colocated with a local solstone clone. for
|
|
69
|
+
2. cloning into `$(sol root)/observers` is only a developer convenience for keeping observer checkouts colocated with a local solstone clone. for a journal you reach directly, clone anywhere — the observer runs independently of your journal at runtime:
|
|
70
70
|
```
|
|
71
71
|
cd "$(sol root)/observers"
|
|
72
72
|
git clone https://github.com/solpbc/solstone-linux.git
|
|
@@ -75,11 +75,11 @@ this is the developer/from-source path; most installs should use the `pipx insta
|
|
|
75
75
|
```
|
|
76
76
|
`make install-service` is a smart install-or-upgrade: detects fresh-install vs upgrade via a marker file, runs CI in upgrade mode, guards against cross-repo contamination.
|
|
77
77
|
|
|
78
|
-
3. run
|
|
78
|
+
3. run setup:
|
|
79
79
|
```
|
|
80
80
|
solstone-linux setup
|
|
81
81
|
```
|
|
82
|
-
this
|
|
82
|
+
this registers the observer against your journal over the local `http://localhost:5015` link — no URL to type. pass `--server-url <journal-url>` for a journal you reach directly.
|
|
83
83
|
|
|
84
84
|
4. verify the service is running:
|
|
85
85
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: solstone-linux
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Standalone Linux desktop observer for solstone
|
|
5
5
|
License-Expression: AGPL-3.0-only
|
|
6
6
|
License-File: LICENSE
|
|
@@ -53,7 +53,7 @@ solstone-linux install-service
|
|
|
53
53
|
solstone-linux setup
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
`setup`
|
|
56
|
+
`setup` registers the observer against your journal over the local `http://localhost:5015` link, so there's no URL to type. If this machine reaches your solstone host directly instead, run `solstone-linux setup --server-url <journal-url>`. (Legacy fallback: mint a key on the journal host with `journal observer create <name>` and paste it during setup.)
|
|
57
57
|
|
|
58
58
|
### Developers building from source
|
|
59
59
|
|
|
@@ -38,7 +38,7 @@ solstone-linux install-service
|
|
|
38
38
|
solstone-linux setup
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
`setup`
|
|
41
|
+
`setup` registers the observer against your journal over the local `http://localhost:5015` link, so there's no URL to type. If this machine reaches your solstone host directly instead, run `solstone-linux setup --server-url <journal-url>`. (Legacy fallback: mint a key on the journal host with `journal observer create <name>` and paste it during setup.)
|
|
42
42
|
|
|
43
43
|
### Developers building from source
|
|
44
44
|
|
|
@@ -15,18 +15,19 @@ from __future__ import annotations
|
|
|
15
15
|
import argparse
|
|
16
16
|
import asyncio
|
|
17
17
|
import importlib.resources
|
|
18
|
-
import json
|
|
19
18
|
import logging
|
|
20
19
|
import os
|
|
21
20
|
import shutil
|
|
22
21
|
import socket
|
|
23
22
|
import subprocess
|
|
24
23
|
import sys
|
|
24
|
+
import time
|
|
25
25
|
from pathlib import Path
|
|
26
26
|
|
|
27
27
|
from . import doctor, streams
|
|
28
28
|
from .config import DEFAULT_SERVER_URL, load_config, save_config
|
|
29
29
|
from .streams import stream_name
|
|
30
|
+
from .sync_health import derive_health, load_facts
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
def _setup_logging(verbose: bool = False) -> None:
|
|
@@ -93,11 +94,12 @@ def cmd_setup(args: argparse.Namespace) -> int:
|
|
|
93
94
|
|
|
94
95
|
config = load_config()
|
|
95
96
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
# Resolve the journal URL: an explicit --server-url wins, then any saved
|
|
98
|
+
# URL, otherwise the local link default. Under pure-PL the journal is
|
|
99
|
+
# reached over the localhost link, so no URL needs to be typed.
|
|
100
|
+
server_url = (
|
|
101
|
+
getattr(args, "server_url", None) or config.server_url or DEFAULT_SERVER_URL
|
|
102
|
+
)
|
|
101
103
|
config.server_url = server_url
|
|
102
104
|
|
|
103
105
|
stream_override = getattr(args, "stream_name", None)
|
|
@@ -157,10 +159,11 @@ def _cmd_setup_interactive() -> int:
|
|
|
157
159
|
|
|
158
160
|
config = load_config()
|
|
159
161
|
|
|
160
|
-
#
|
|
161
|
-
|
|
162
|
-
url
|
|
163
|
-
|
|
162
|
+
# No prompt: default to the local link. Under pure-PL the journal is reached
|
|
163
|
+
# over the localhost link, so no URL needs to be typed; a saved URL (or
|
|
164
|
+
# `solstone-linux setup --server-url <url>`) points at a journal reached
|
|
165
|
+
# directly.
|
|
166
|
+
config.server_url = config.server_url or DEFAULT_SERVER_URL
|
|
164
167
|
|
|
165
168
|
# Derive stream name
|
|
166
169
|
if not config.stream:
|
|
@@ -375,15 +378,9 @@ def cmd_status(args: argparse.Namespace) -> int:
|
|
|
375
378
|
else:
|
|
376
379
|
print(f"Retain: {retention} day(s)")
|
|
377
380
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
try:
|
|
382
|
-
with open(synced_path) as f:
|
|
383
|
-
synced = json.load(f)
|
|
384
|
-
print(f"Synced: {len(synced)} day(s) fully synced")
|
|
385
|
-
except (json.JSONDecodeError, OSError):
|
|
386
|
-
pass
|
|
381
|
+
facts = load_facts(config.state_dir)
|
|
382
|
+
health = derive_health(facts, time.time(), config.sync_stale_threshold)
|
|
383
|
+
print(health.cli)
|
|
387
384
|
|
|
388
385
|
# Systemd status
|
|
389
386
|
try:
|
|
@@ -24,6 +24,7 @@ DEFAULT_SERVER_URL = "http://localhost:5015"
|
|
|
24
24
|
DEFAULT_SEGMENT_INTERVAL = 300
|
|
25
25
|
DEFAULT_SYNC_RETRY_DELAYS = [5, 30, 120, 300]
|
|
26
26
|
DEFAULT_SYNC_MAX_RETRIES = 10
|
|
27
|
+
DEFAULT_SYNC_STALE_THRESHOLD = 600
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
@dataclass
|
|
@@ -38,6 +39,7 @@ class Config:
|
|
|
38
39
|
default_factory=lambda: list(DEFAULT_SYNC_RETRY_DELAYS)
|
|
39
40
|
)
|
|
40
41
|
sync_max_retries: int = DEFAULT_SYNC_MAX_RETRIES
|
|
42
|
+
sync_stale_threshold: int = DEFAULT_SYNC_STALE_THRESHOLD
|
|
41
43
|
cache_retention_days: int = 7
|
|
42
44
|
chat_bridge_enabled: bool = True
|
|
43
45
|
capture_framerate: int = 1
|
|
@@ -97,6 +99,12 @@ def load_config(base_dir: Path | None = None) -> Config:
|
|
|
97
99
|
config.sync_retry_delays = data["sync_retry_delays"]
|
|
98
100
|
if "sync_max_retries" in data:
|
|
99
101
|
config.sync_max_retries = data["sync_max_retries"]
|
|
102
|
+
try:
|
|
103
|
+
config.sync_stale_threshold = int(
|
|
104
|
+
data.get("sync_stale_threshold", DEFAULT_SYNC_STALE_THRESHOLD)
|
|
105
|
+
)
|
|
106
|
+
except (TypeError, ValueError):
|
|
107
|
+
config.sync_stale_threshold = DEFAULT_SYNC_STALE_THRESHOLD
|
|
100
108
|
try:
|
|
101
109
|
config.cache_retention_days = int(data.get("cache_retention_days", 7))
|
|
102
110
|
except (TypeError, ValueError):
|
|
@@ -120,6 +128,7 @@ def save_config(config: Config) -> None:
|
|
|
120
128
|
"segment_interval": config.segment_interval,
|
|
121
129
|
"sync_retry_delays": config.sync_retry_delays,
|
|
122
130
|
"sync_max_retries": config.sync_max_retries,
|
|
131
|
+
"sync_stale_threshold": config.sync_stale_threshold,
|
|
123
132
|
"cache_retention_days": config.cache_retention_days,
|
|
124
133
|
"chat_bridge_enabled": config.chat_bridge_enabled,
|
|
125
134
|
"capture_framerate": config.capture_framerate,
|
|
@@ -38,13 +38,13 @@ class ObserverService(ServiceInterface):
|
|
|
38
38
|
@dbus_property(access=PropertyAccess.READ)
|
|
39
39
|
def SyncStatus(self) -> "s":
|
|
40
40
|
if self._observer._sync:
|
|
41
|
-
return self._observer._sync.
|
|
42
|
-
return "
|
|
41
|
+
return self._observer._sync.health.state.value
|
|
42
|
+
return "unknown"
|
|
43
43
|
|
|
44
44
|
@dbus_property(access=PropertyAccess.READ)
|
|
45
45
|
def SyncProgress(self) -> "s":
|
|
46
46
|
if self._observer._sync:
|
|
47
|
-
return self._observer._sync.
|
|
47
|
+
return self._observer._sync.progress
|
|
48
48
|
return ""
|
|
49
49
|
|
|
50
50
|
@dbus_property(access=PropertyAccess.READ)
|
|
@@ -122,17 +122,12 @@ class ObserverService(ServiceInterface):
|
|
|
122
122
|
except OSError:
|
|
123
123
|
pass
|
|
124
124
|
|
|
125
|
-
synced_days = 0
|
|
126
|
-
if self._observer._sync:
|
|
127
|
-
synced_days = len(self._observer._sync._synced_days)
|
|
128
|
-
|
|
129
125
|
total_size_mb = int(total_size / (1024 * 1024))
|
|
130
126
|
uptime_seconds = int(time.monotonic() - self._observer._start_mono)
|
|
131
127
|
|
|
132
128
|
return {
|
|
133
129
|
"captures_today": Variant("i", captures_today),
|
|
134
130
|
"total_size_mb": Variant("i", total_size_mb),
|
|
135
|
-
"synced_days": Variant("i", synced_days),
|
|
136
131
|
"uptime_seconds": Variant("i", uptime_seconds),
|
|
137
132
|
}
|
|
138
133
|
|
|
@@ -13,8 +13,12 @@ import os
|
|
|
13
13
|
import shutil
|
|
14
14
|
import subprocess
|
|
15
15
|
import sys
|
|
16
|
+
import time
|
|
16
17
|
from typing import Callable, NamedTuple
|
|
17
18
|
|
|
19
|
+
from .config import load_config
|
|
20
|
+
from .sync_health import derive_health, load_facts
|
|
21
|
+
|
|
18
22
|
CheckResult = NamedTuple(
|
|
19
23
|
"CheckResult",
|
|
20
24
|
[("name", str), ("severity", str), ("detail", str)],
|
|
@@ -286,6 +290,13 @@ def check_appindicator_ext() -> CheckResult:
|
|
|
286
290
|
)
|
|
287
291
|
|
|
288
292
|
|
|
293
|
+
def check_sync_health() -> CheckResult:
|
|
294
|
+
config = load_config()
|
|
295
|
+
facts = load_facts(config.state_dir)
|
|
296
|
+
health = derive_health(facts, time.time(), config.sync_stale_threshold)
|
|
297
|
+
return CheckResult("sync health", health.doctor_severity, health.doctor_detail)
|
|
298
|
+
|
|
299
|
+
|
|
289
300
|
def run_doctor() -> int:
|
|
290
301
|
checks: list[tuple[str, Callable[[], CheckResult]]] = [
|
|
291
302
|
("python version", check_python_version),
|
|
@@ -297,6 +308,7 @@ def run_doctor() -> int:
|
|
|
297
308
|
("xdg-desktop-portal", lambda: asyncio.run(check_portal())),
|
|
298
309
|
("x11 capture", check_x11_capture),
|
|
299
310
|
("systemd --user", check_user_systemd),
|
|
311
|
+
("sync health", check_sync_health),
|
|
300
312
|
("pipx", check_pipx),
|
|
301
313
|
("appindicator ext (soft)", check_appindicator_ext),
|
|
302
314
|
]
|