main
py 122 lines 3.57 KB
Raw
1 from __future__ import annotations
2
3 import json
4 import os
5 from pathlib import Path
6 from typing import Any
7
8 from plugins._orchestrator.helpers.adapters.base import TerminalAgentAdapter
9
10
11 _AUTH_ENV_VARS = {
12 "ALIBABA_CODING_PLAN_API_KEY",
13 "ANTHROPIC_API_KEY",
14 "ANTHROPIC_TOKEN",
15 "ARCEEAI_API_KEY",
16 "AZURE_FOUNDRY_API_KEY",
17 "CLAUDE_CODE_OAUTH_TOKEN",
18 "COPILOT_GITHUB_TOKEN",
19 "DASHSCOPE_API_KEY",
20 "DEEPSEEK_API_KEY",
21 "GEMINI_API_KEY",
22 "GH_TOKEN",
23 "GITHUB_TOKEN",
24 "GLM_API_KEY",
25 "GMI_API_KEY",
26 "GOOGLE_API_KEY",
27 "HF_TOKEN",
28 "KILOCODE_API_KEY",
29 "KIMI_API_KEY",
30 "KIMI_CODING_API_KEY",
31 "MINIMAX_API_KEY",
32 "NVIDIA_API_KEY",
33 "NOVITA_API_KEY",
34 "OLLAMA_API_KEY",
35 "OPENAI_API_KEY",
36 "OPENCODE_GO_API_KEY",
37 "OPENCODE_ZEN_API_KEY",
38 "OPENROUTER_API_KEY",
39 "STEPFUN_API_KEY",
40 "TOKENHUB_API_KEY",
41 "XAI_API_KEY",
42 "ZAI_API_KEY",
43 }
44
45 _SECRET_KEYS = {
46 "access_token",
47 "agent_key",
48 "api_key",
49 "id_token",
50 "refresh_token",
51 "runtime_api_key",
52 "token",
53 }
54
55
56 class HermesAgentAdapter(TerminalAgentAdapter):
57 id = "hermes"
58 title = "Hermes Agent"
59 binary = "hermes"
60 install_hint = "curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash"
61 description = "Nous Research Hermes Agent CLI in quiet headless chat mode."
62
63 def _home(self) -> Path:
64 configured = os.environ.get("HERMES_HOME", "").strip()
65 return Path(configured).expanduser() if configured else Path.home() / ".hermes"
66
67 def auth_status(self, config: dict[str, Any] | None = None) -> dict[str, Any]:
68 env_var = next((name for name in sorted(_AUTH_ENV_VARS) if os.environ.get(name)), "")
69 if env_var:
70 return {"connected": True, "mode": "env", "auth_path": env_var}
71
72 home = self._home()
73 env_path = home / ".env"
74 try:
75 if _env_file_has_key(env_path):
76 return {"connected": True, "mode": "external", "auth_path": str(env_path)}
77 except OSError as exc:
78 return {"connected": False, "mode": "", "auth_path": "", "error": str(exc)}
79
80 auth_path = home / "auth.json"
81 try:
82 if _auth_store_has_credentials(auth_path):
83 return {"connected": True, "mode": "external", "auth_path": str(auth_path)}
84 except OSError as exc:
85 return {"connected": False, "mode": "", "auth_path": "", "error": str(exc)}
86
87 return {"connected": False, "mode": "", "auth_path": ""}
88
89
90 def _env_file_has_key(path: Path) -> bool:
91 if not path.is_file():
92 return False
93 for line in path.read_text(encoding="utf-8", errors="ignore").splitlines():
94 raw = line.strip()
95 if not raw or raw.startswith("#") or "=" not in raw:
96 continue
97 key, value = raw.split("=", 1)
98 if key.strip() in _AUTH_ENV_VARS and value.strip().strip("'\""):
99 return True
100 return False
101
102
103 def _auth_store_has_credentials(path: Path) -> bool:
104 if not path.is_file() or path.stat().st_size <= 0:
105 return False
106 try:
107 data = json.loads(path.read_text(encoding="utf-8"))
108 except ValueError:
109 return False
110 return _contains_secret(data)
111
112
113 def _contains_secret(value: Any) -> bool:
114 if isinstance(value, dict):
115 for key, item in value.items():
116 if str(key) in _SECRET_KEYS and isinstance(item, str) and len(item.strip()) > 3:
117 return True
118 if _contains_secret(item):
119 return True
120 if isinstance(value, list):
121 return any(_contains_secret(item) for item in value)
122 return False