feat(desktop): /api/env derives provider key membership from unified catalog
The Keys tab now surfaces every keys-tab provider in provider_catalog() (the `hermes model` universe), synthesizing a card even when the env var has no hand entry in OPTIONAL_ENV_VARS. Closes the drift where openai-api, kilocode, novita, tencent-tokenhub, and copilot were CLI-configurable but invisible in the desktop Providers → API keys tab. Each provider row now carries backend-derived provider/provider_label grouping hints so the desktop can group by the same provider identity the CLI picker uses. Hand OPTIONAL_ENV_VARS prose still wins where present (enrichment, not a gate). Shared non-provider credentials (e.g. tool-category GITHUB_TOKEN) are explicitly not hijacked into a provider card — Copilot uses its provider-owned COPILOT_GITHUB_TOKEN.
This commit is contained in:
parent
054b8c82fd
commit
3be1326f8d
2 changed files with 139 additions and 8 deletions
|
|
@ -1299,6 +1299,48 @@ class TestWebServerEndpoints:
|
|||
for key, info in data.items():
|
||||
assert info["channel_managed"] is (key in channel_keys)
|
||||
|
||||
def test_get_env_vars_surfaces_catalog_providers(self):
|
||||
"""Every keys-tab provider in the unified catalog must appear in /api/env
|
||||
as a provider card, even when it has no hand entry in OPTIONAL_ENV_VARS.
|
||||
|
||||
Regression for the GUI⇄CLI drift: openai-api, kilocode, novita,
|
||||
tencent-tokenhub, copilot were configurable via `hermes model` but
|
||||
invisible in the desktop Providers → API keys tab.
|
||||
"""
|
||||
from hermes_cli.provider_catalog import provider_catalog
|
||||
|
||||
data = self.client.get("/api/env").json()
|
||||
for d in provider_catalog():
|
||||
if d.tab != "keys" or not d.api_key_env_vars:
|
||||
continue
|
||||
# The PRIMARY credential var must surface as this provider's card.
|
||||
# (Shared aliases like GITHUB_TOKEN are intentionally left on their
|
||||
# existing tool category and not hijacked — see the copilot test.)
|
||||
primary = d.api_key_env_vars[0]
|
||||
assert primary in data, f"{primary} ({d.slug}) missing from /api/env"
|
||||
info = data[primary]
|
||||
assert info["category"] == "provider"
|
||||
assert info["provider"] == d.slug
|
||||
assert info["provider_label"] == d.label
|
||||
|
||||
def test_get_env_vars_provider_rows_carry_grouping_hints(self):
|
||||
"""Provider env rows expose the backend `provider`/`provider_label` the
|
||||
desktop Keys tab groups by (so it no longer relies on prefix guesses)."""
|
||||
data = self.client.get("/api/env").json()
|
||||
# OPENAI_API_KEY is a hand-listed protected var AND a catalog provider;
|
||||
# it must come back tagged to the openai-api provider.
|
||||
assert data["OPENAI_API_KEY"]["provider"] == "openai-api"
|
||||
assert data["OPENAI_API_KEY"]["category"] == "provider"
|
||||
|
||||
def test_get_env_vars_copilot_uses_provider_token_not_shared_github_token(self):
|
||||
"""Copilot surfaces as its own provider card via COPILOT_GITHUB_TOKEN;
|
||||
the shared GITHUB_TOKEN keeps its existing (tool) category."""
|
||||
data = self.client.get("/api/env").json()
|
||||
assert data["COPILOT_GITHUB_TOKEN"]["provider"] == "copilot"
|
||||
assert data["COPILOT_GITHUB_TOKEN"]["category"] == "provider"
|
||||
# Shared GITHUB_TOKEN must NOT be hijacked into the copilot provider card.
|
||||
assert data.get("GITHUB_TOKEN", {}).get("provider", "") != "copilot"
|
||||
|
||||
def test_platform_scoped_messaging_env_vars_are_channel_managed(self):
|
||||
from hermes_cli.web_server import (
|
||||
_MESSAGING_KEYS_PAGE_KEYS,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue