feat(desktop): polish credentials settings and messaging env routing (#39217)

* feat(desktop): polish credentials settings and messaging env routing

Align Provider API Keys and Tools & Keys with Advanced ListRow inputs,
add Tools & Keys sidebar subnav, move platform env vars to Messaging via
channel_managed discovery, strip toolset emojis, and condense cron actions.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(desktop): align Messaging credential inputs with settings ListRow style

Remove monospace inputs and use CREDENTIAL_CONTROL_CLASS + ListRow layout
to match Provider API Keys and Tools & Keys.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Austin Pickett
2026-06-04 14:01:15 -04:00
committed by GitHub
parent a3fb48b2ce
commit acce1a2452
20 changed files with 826 additions and 668 deletions

View File

@ -82,6 +82,22 @@ CONFIGURABLE_TOOLSETS = [
("computer_use", "🖱️ Computer Use (macOS)", "background desktop control via cua-driver"),
]
def gui_toolset_label(label: str) -> str:
"""Strip leading emoji/icons from toolset titles for GUI surfaces.
Registry labels use ``<emoji> <title>``; plugin toolsets prefix with ``🔌``.
CLI/TUI keeps the raw ``label`` — only HTTP APIs call this helper.
"""
text = (label or "").strip()
if not text:
return text
parts = text.split(None, 1)
if len(parts) == 2 and parts[0] and not any(ch.isascii() and ch.isalnum() for ch in parts[0]):
return parts[1].strip()
return text
# Toolsets that are OFF by default for new installs.
# They're still in _HERMES_CORE_TOOLS (available at runtime if enabled),
# but the setup checklist won't pre-select them for first-time users.

View File

@ -2833,18 +2833,66 @@ def _channel_managed_env_keys() -> frozenset[str]:
return frozenset()
# Cross-cutting gateway / relay knobs stay on the Keys → Settings tab even though
# they use the ``messaging`` category in OPTIONAL_ENV_VARS. Platform-scoped vars
# (``DISCORD_*``, ``MATRIX_*``, …) are owned by the Messaging UI instead.
_MESSAGING_KEYS_PAGE_KEYS = frozenset({
"GATEWAY_ALLOW_ALL_USERS",
"GATEWAY_PROXY_KEY",
"GATEWAY_PROXY_URL",
})
def _platform_env_prefixes(platform_id: str) -> tuple[str, ...]:
"""Env-var prefixes owned by a messaging platform card."""
aliases: dict[str, tuple[str, ...]] = {
"email": ("EMAIL_",),
"homeassistant": ("HASS_",),
"qqbot": ("QQ_", "QQBOT_"),
"sms": ("TWILIO_",),
"wecom": ("WECOM_BOT_", "WECOM_SECRET"),
"wecom_callback": ("WECOM_CALLBACK_",),
}
if platform_id in aliases:
return aliases[platform_id]
return (platform_id.upper().replace("-", "_") + "_",)
def _discover_platform_env_vars(platform_id: str) -> tuple[str, ...]:
"""All messaging-category env vars for a platform (override + plugin + prefix)."""
prefixes = _platform_env_prefixes(platform_id)
keys: list[str] = []
for name, info in OPTIONAL_ENV_VARS.items():
if info.get("category") != "messaging":
continue
if name in _MESSAGING_KEYS_PAGE_KEYS:
continue
if not any(name.startswith(prefix) for prefix in prefixes):
continue
keys.append(name)
return tuple(sorted(set(keys)))
def _merge_platform_env_vars(
platform_id: str,
override: dict[str, Any],
plugin_entry: Any | None,
) -> tuple[str, ...]:
"""Canonical env-var list for a messaging platform card."""
discovered = _discover_platform_env_vars(platform_id)
if "env_vars" in override:
return tuple(dict.fromkeys((*override["env_vars"], *discovered)))
if plugin_entry is not None and plugin_entry.required_env:
return tuple(dict.fromkeys((*tuple(plugin_entry.required_env), *discovered)))
return discovered
def _build_catalog_entry(
platform_id: str, plugin_entry: Any | None = None
) -> dict[str, Any]:
override = _PLATFORM_OVERRIDES.get(platform_id, {})
if "env_vars" in override:
env_vars: tuple[str, ...] = tuple(override["env_vars"])
elif plugin_entry is not None and plugin_entry.required_env:
env_vars = tuple(plugin_entry.required_env)
else:
prefix = platform_id.upper() + "_"
env_vars = tuple(k for k in OPTIONAL_ENV_VARS if k.startswith(prefix))
env_vars = _merge_platform_env_vars(platform_id, override, plugin_entry)
if "required_env" in override:
required_env = tuple(override["required_env"])
@ -6663,6 +6711,7 @@ async def get_toolsets():
_get_effective_configurable_toolsets,
_get_platform_tools,
_toolset_has_keys,
gui_toolset_label,
)
from toolsets import resolve_toolset
@ -6680,7 +6729,9 @@ async def get_toolsets():
tools = []
is_enabled = name in enabled_toolsets
result.append({
"name": name, "label": label, "description": desc,
"name": name,
"label": gui_toolset_label(label),
"description": desc,
"enabled": is_enabled,
"available": is_enabled,
"configured": _toolset_has_keys(name, config),