Skip to content

Commit a2b77f4

Browse files
committed
fix(hermes-local): check config.env for API keys in test environment
The test environment was only checking process.env for API keys, missing the secrets resolved by the server into ctx.config.env. Now checks config.env first (adapter-configured secrets) then falls back to process.env, matching how other adapters (Claude) handle it. Also adds ZAI_API_KEY to the checked providers and strips inline YAML comments from parsed config values in detect-model.
1 parent 3aa1e9f commit a2b77f4

2 files changed

Lines changed: 25 additions & 9 deletions

File tree

‎packages/adapters/hermes-local/src/server/detect-model.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export function parseModelFromConfig(content: string): DetectedModel | null {
6464
const match = trimmed.match(/^\s*(\w+)\s*:\s*(.+)$/);
6565
if (match) {
6666
const key = match[1];
67-
const val = match[2].trim().replace(/^['"]|['"]$/g, "");
67+
const val = match[2].trim().replace(/#.*$/, "").trim().replace(/^['"]|['"]$/g, "");
6868
if (key === "default") model = val;
6969
if (key === "provider") provider = val;
7070
}

‎packages/adapters/hermes-local/src/server/test.ts‎

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,16 +128,31 @@ function checkModel(
128128
};
129129
}
130130

131-
function checkApiKeys(): AdapterEnvironmentCheck | null {
132-
const hasAnthropic = !!process.env.ANTHROPIC_API_KEY;
133-
const hasOpenRouter = !!process.env.OPENROUTER_API_KEY;
134-
const hasOpenAI = !!process.env.OPENAI_API_KEY;
131+
function checkApiKeys(
132+
config: Record<string, unknown>,
133+
): AdapterEnvironmentCheck | null {
134+
// The server resolves secret refs into config.env before calling testEnvironment,
135+
// so we check config.env first (adapter-configured secrets), then fall back to
136+
// process.env (server/host environment). This mirrors how the Claude adapter does it.
137+
const envConfig = (config.env ?? {}) as Record<string, unknown>;
138+
const resolvedEnv: Record<string, string> = {};
139+
for (const [key, value] of Object.entries(envConfig)) {
140+
if (typeof value === "string" && value.length > 0) resolvedEnv[key] = value;
141+
}
142+
143+
const has = (key: string): boolean =>
144+
!!(resolvedEnv[key] ?? process.env[key]);
145+
146+
const hasAnthropic = has("ANTHROPIC_API_KEY");
147+
const hasOpenRouter = has("OPENROUTER_API_KEY");
148+
const hasOpenAI = has("OPENAI_API_KEY");
149+
const hasZai = has("ZAI_API_KEY");
135150

136-
if (!hasAnthropic && !hasOpenRouter && !hasOpenAI) {
151+
if (!hasAnthropic && !hasOpenRouter && !hasOpenAI && !hasZai) {
137152
return {
138153
level: "warn",
139154
message: "No LLM API keys found in environment",
140-
hint: "Set ANTHROPIC_API_KEY, OPENROUTER_API_KEY, or OPENAI_API_KEY. Hermes may also have keys configured in ~/.hermes/.env",
155+
hint: "Set ANTHROPIC_API_KEY, OPENROUTER_API_KEY, OPENAI_API_KEY, or ZAI_API_KEY in the agent's env secrets. Hermes may also have keys configured in ~/.hermes/.env",
141156
code: "hermes_no_api_keys",
142157
};
143158
}
@@ -146,6 +161,7 @@ function checkApiKeys(): AdapterEnvironmentCheck | null {
146161
if (hasAnthropic) providers.push("Anthropic");
147162
if (hasOpenRouter) providers.push("OpenRouter");
148163
if (hasOpenAI) providers.push("OpenAI");
164+
if (hasZai) providers.push("Z.AI");
149165

150166
return {
151167
level: "info",
@@ -191,8 +207,8 @@ export async function testEnvironment(
191207
const modelCheck = checkModel(config);
192208
if (modelCheck) checks.push(modelCheck);
193209

194-
// 5. API keys
195-
const apiKeyCheck = checkApiKeys();
210+
// 5. API keys (check config.env — server resolves secrets before calling us)
211+
const apiKeyCheck = checkApiKeys(config);
196212
if (apiKeyCheck) checks.push(apiKeyCheck);
197213

198214
// Determine overall status

0 commit comments

Comments
 (0)