feat: publish Hermes Nextcloud calendar MCP under AGPL-3.0
Some checks failed
Offline checks / test (3.11) (push) Has been cancelled
Offline checks / test (3.13) (push) Has been cancelled

This commit is contained in:
xeno
2026-09-30 20:27:25 +00:00
commit 93b9c9f913
44 changed files with 8193 additions and 0 deletions

View File

@@ -0,0 +1,158 @@
"""Read-only wallet discovery and desktop recommendations. No secret retrieval."""
import os
import shutil
from dataclasses import dataclass
from jeepney import DBus
from secretstorage.collection import get_collection_by_alias
from secretstorage.exceptions import ItemNotFoundException
from secretstorage.util import open_session
from . import credentials
from .types import ErrorCode, SafeError
LABELS = {"gnome": "GNOME Keyring", "kde": "KWallet", "system": "Other Secret Service"}
PROGRAMS = {
"gnome": ("gnome-keyring-daemon",),
"kde": ("ksecretd", "kwalletd6", "kwalletd5"),
}
@dataclass(frozen=True)
class WalletChoice:
provider: str
executable: str | None = None
def __post_init__(self):
if self.provider not in credentials.KEYRING_ALIASES:
raise SafeError(ErrorCode.INVALID_INPUT)
if self.executable is not None and (
self.provider != "system"
or not isinstance(self.executable, str)
or not self.executable.startswith("/")
or "\x00" in self.executable
or len(self.executable) > 4096
):
raise SafeError(ErrorCode.INVALID_INPUT)
@dataclass(frozen=True)
class WalletAvailability:
choice: WalletChoice
status: str
is_default: bool = False
@property
def label(self):
return LABELS[self.choice.provider]
def _probe(connection, owner):
connection.destination = owner
try:
collection = get_collection_by_alias(connection, "default")
if collection.is_locked():
return "locked"
session = open_session(connection)
return "ready" if session.encrypted else "unencrypted communication"
except ItemNotFoundException:
return "no default wallet"
except Exception:
return "unavailable"
def detect_wallets():
"""Inventory named providers and a distinct current generic provider.
Installed but inactive providers are shown without launching them. Running
providers are probed only for a default collection, lock state and encrypted
IPC. Collection labels, item lists and secret values are never displayed.
"""
connection = None
try:
connection = credentials._BoundedConnection(credentials._connect())
(names,) = credentials._bus_reply(connection, DBus().ListNames())
(activatable,) = credentials._bus_reply(
connection, DBus().ListActivatableNames()
)
names, activatable = set(names), set(activatable)
owners = {}
for provider, aliases in credentials.KEYRING_ALIASES.items():
if any(name in names for name in aliases):
try:
owner = credentials._keyring_owner(connection, provider)
owners[provider] = owner
except Exception:
pass # A provider may exit while the inventory is read.
result = []
for provider, programs in PROGRAMS.items():
installed = any(
name in activatable for name in credentials.KEYRING_ALIASES[provider]
) or any(shutil.which(p) for p in programs)
if provider not in owners and not installed:
continue
status = (
_probe(connection, owners[provider])
if provider in owners
else "not running"
)
result.append(
WalletAvailability(
WalletChoice(provider),
status,
provider in owners and owners[provider] == owners.get("system"),
)
)
if "system" in owners and owners["system"] not in {
owners.get("gnome"),
owners.get("kde"),
}:
try:
executable = credentials._owner_executable(connection, owners["system"])
choice = WalletChoice("system", executable)
status = _probe(connection, owners["system"])
except Exception:
choice, status = WalletChoice("system"), "unavailable"
result.append(WalletAvailability(choice, status, True))
return result
except Exception:
raise SafeError(ErrorCode.CREDENTIAL_STORE_UNAVAILABLE) from None
finally:
if connection is not None:
connection.close()
def recommended_wallet(wallets, environment=None):
environment = os.environ if environment is None else environment
desktop = environment.get("XDG_CURRENT_DESKTOP") or environment.get(
"DESKTOP_SESSION", ""
)
tokens = desktop.casefold().split(":")
preferred = None
for token in tokens:
if token in {"kde", "plasma", "plasmawayland", "plasmax11", "lxqt"}:
preferred = "kde"
break
if token in {
"gnome",
"gnome-classic",
"gnome-xorg",
"cinnamon",
"x-cinnamon",
"mate",
"budgie",
"budgie-desktop",
"unity",
"xfce",
"xfce4",
}:
preferred = "gnome"
break
if any(w.choice.provider == preferred for w in wallets):
return preferred
default = next((w for w in wallets if w.is_default), None)
if default:
return default.choice.provider
ready = next((w for w in wallets if w.status == "ready"), None)
return ready.choice.provider if ready else None