Skip to content

Commit 3be62c7

Browse files
authored
Merge pull request #248 from modelstudioai/fix/hermes-home-env
fix(config): resolve Hermes home via HERMES_HOME and platform default (prepare 2.2.1)
2 parents fd8967f + be407fd commit 3be62c7

22 files changed

Lines changed: 219 additions & 20 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
66

77
[中文版](CHANGELOG.zh.md) · [README](README.md) · [Contributing](CONTRIBUTING.md)
88

9+
## [2.2.1] - 2026-10-12
10+
11+
### Fixed
12+
13+
- **Hermes agent config path** — `bl config agent --agent hermes` now writes to the location Hermes actually reads (`$HERMES_HOME/config.yaml`, or the platform-native default: `%LOCALAPPDATA%\hermes` on Windows, `~/.hermes` elsewhere) instead of always `~/.hermes/config.yaml`. Previously on Windows or with a custom `HERMES_HOME`, config landed where Hermes never read it and the change silently took no effect; agent detection could also falsely report Hermes as unconfigured.
14+
915
## [2.2.0] - 2026-10-12
1016

1117
### Changed

‎CHANGELOG.zh.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66

77
[English](CHANGELOG.md) · [README](README.zh.md) · [参与贡献](CONTRIBUTING.zh.md)
88

9+
## [2.2.1] - 2026-10-12
10+
11+
### 修复
12+
13+
- **Hermes Agent 配置路径** —— `bl config agent --agent hermes` 现在会写入 Hermes 实际读取的位置(`$HERMES_HOME/config.yaml`,或平台原生默认路径:Windows 为 `%LOCALAPPDATA%\hermes`,其它平台为 `~/.hermes`),不再固定写 `~/.hermes/config.yaml`。此前在 Windows 或自定义 `HERMES_HOME` 环境下,配置被写到 Hermes 不读取的文件、变更静默失效,Agent 探测也可能误报 Hermes 为「未配置」。
14+
915
## [2.2.0] - 2026-10-12
1016

1117
### 变更

‎packages/cli/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bailian-cli",
3-
"version": "2.2.0",
3+
"version": "2.2.1",
44
"description": "CLI for Aliyun Model Studio (DashScope) AI Platform.",
55
"keywords": [
66
"agent",

‎packages/commands/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bailian-cli-commands",
3-
"version": "2.2.0",
3+
"version": "2.2.1",
44
"description": "Command library for bailian-cli products (knowledge, memory, media, …). See https://www.npmjs.com/package/bailian-cli for usage.",
55
"homepage": "https://bailian.console.aliyun.com/cli",
66
"bugs": {

‎packages/commands/src/commands/config/agent/writers/hermes.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
import { homedir } from "os";
21
import { join } from "path";
32
import { existsSync, readFileSync } from "fs";
43
import yaml from "yaml";
4+
import { getHermesHome } from "bailian-cli-core";
55
import { backup, writeTextAtomic, isAnthropicEndpoint, type AgentDef } from "./utils.ts";
66

77
export default {
88
label: "Hermes Agent",
99
write({ baseUrl, apiKey, model }) {
10-
const configPath = join(homedir(), ".hermes", "config.yaml");
10+
// Honor HERMES_HOME / the platform-native default so the config lands where
11+
// Hermes actually reads it (see core getHermesHome); never hard-code ~/.hermes.
12+
const configPath = join(getHermesHome(), "config.yaml");
1113

1214
backup(configPath);
1315

‎packages/commands/src/commands/config/inventory.ts‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
import { inflateRawSync } from "node:zlib";
2020
import yaml from "yaml";
2121
import { parse as parseToml } from "smol-toml";
22+
import { getHermesHome } from "bailian-cli-core";
2223
import { qwenworkMcpPath, workbuddyMcpPaths } from "../mcp/agent-config.ts";
2324

2425
/**
@@ -143,7 +144,7 @@ function skillRoots(home: string): Array<{ source: string; dir: string }> {
143144
{ source: "opencode", dir: join(home, ".config", "opencode", "skills") },
144145
{ source: "openclaw", dir: join(home, ".openclaw", "skills") },
145146
{ source: "openclaw", dir: join(home, ".openclaw", "workspace", "skills") },
146-
{ source: "hermes", dir: join(home, ".hermes", "skills") },
147+
{ source: "hermes", dir: join(getHermesHome(home), "skills") },
147148
{ source: "gemini", dir: join(home, ".gemini", "skills") },
148149
{
149150
source: "antigravity",
@@ -857,9 +858,9 @@ const AGENT_PROBES: AgentProbe[] = [
857858
{
858859
id: "hermes",
859860
label: "Hermes Agent",
860-
paths: (h) => [join(h, ".hermes", "config.yaml")],
861+
paths: (h) => [join(getHermesHome(h), "config.yaml")],
861862
detect: (h) => {
862-
const text = readText(join(h, ".hermes", "config.yaml"));
863+
const text = readText(join(getHermesHome(h), "config.yaml"));
863864
if (!text) return { configured: false };
864865
let config: Record<string, unknown> | undefined;
865866
try {
@@ -1000,7 +1001,7 @@ function agentDetailFields(id: string, home: string): AgentField[] {
10001001
pushField(out, "API", bailian?.api);
10011002
pushField(out, "API Key", bailian?.apiKey, true);
10021003
} else if (id === "hermes") {
1003-
const text = readText(join(home, ".hermes", "config.yaml"));
1004+
const text = readText(join(getHermesHome(home), "config.yaml"));
10041005
let config: Record<string, unknown> | undefined;
10051006
if (text) {
10061007
try {

‎packages/commands/tests/config-agent-writers.test.ts‎

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import { mkdtempSync, rmSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from "fs";
1+
import {
2+
mkdtempSync,
3+
rmSync,
4+
readFileSync,
5+
writeFileSync,
6+
mkdirSync,
7+
readdirSync,
8+
existsSync,
9+
} from "fs";
210
import { tmpdir, homedir } from "os";
311
import { join } from "path";
412
import { afterEach, beforeEach, describe, expect, test } from "vite-plus/test";
@@ -500,6 +508,34 @@ describe("config agent writers", () => {
500508
});
501509
});
502510

511+
test("hermes 尊重 HERMES_HOME 环境变量,写入自定义 home 而非 ~/.hermes", () => {
512+
const customHome = join(home, "custom-hermes-home");
513+
mkdirSync(customHome, { recursive: true });
514+
const prevHermesHome = process.env.HERMES_HOME;
515+
process.env.HERMES_HOME = customHome;
516+
try {
517+
const summary = hermes.write({
518+
baseUrl: OAI_URL,
519+
apiKey: "sk-h",
520+
model: "qwen3-coder-plus",
521+
});
522+
const expectedPath = join(customHome, "config.yaml");
523+
// 写入位置与返回的 paths 都必须是 $HERMES_HOME/config.yaml
524+
expect(summary.paths).toEqual([expectedPath]);
525+
const config = yaml.parse(readFileSync(expectedPath, "utf8"));
526+
expect(config.model).toMatchObject({
527+
default: "qwen3-coder-plus",
528+
provider: "custom",
529+
base_url: OAI_URL,
530+
});
531+
// 不再写入硬编码的 ~/.hermes/config.yaml(否则 Hermes 读不到,静默失效)
532+
expect(existsSync(join(home, ".hermes", "config.yaml"))).toBe(false);
533+
} finally {
534+
if (prevHermesHome === undefined) delete process.env.HERMES_HOME;
535+
else process.env.HERMES_HOME = prevHermesHome;
536+
}
537+
});
538+
503539
test("codex 写入 config.toml 与 auth.json(官方 env_key 结构,合并保留)", () => {
504540
// 预置 config.toml 无关顶层键与另一个 provider,验证非破坏性合并
505541
mkdirSync(join(home, ".codex"), { recursive: true });

‎packages/commands/tests/inventory.test.ts‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,35 @@ test("listAgents 报告安装与已连接 bailian-cli 的状态", () => {
240240
expect(agents).toHaveLength(6);
241241
});
242242
});
243+
244+
test("listAgents 通过 HERMES_HOME 探测 relocated 的 hermes 配置", () => {
245+
const prevHermesHome = process.env.HERMES_HOME;
246+
withHome((home) => {
247+
// Hermes home relocated away from ~/.hermes via HERMES_HOME.
248+
const hermesHome = join(home, "relocated-hermes");
249+
process.env.HERMES_HOME = hermesHome;
250+
write(
251+
hermesHome,
252+
"config.yaml",
253+
[
254+
"model:",
255+
" default: qwen3-max",
256+
" provider: custom",
257+
" base_url: https://dashscope.aliyuncs.com/compatible-mode/v1",
258+
" api_key: sk-test",
259+
].join("\n"),
260+
);
261+
try {
262+
const byId = Object.fromEntries(listAgents(home).map((a) => [a.id, a]));
263+
// Probe must follow HERMES_HOME, not the hard-coded ~/.hermes (issue #131).
264+
expect(byId.hermes).toMatchObject({
265+
installed: true,
266+
configured: true,
267+
model: "qwen3-max",
268+
});
269+
} finally {
270+
if (prevHermesHome === undefined) delete process.env.HERMES_HOME;
271+
else process.env.HERMES_HOME = prevHermesHome;
272+
}
273+
});
274+
});

‎packages/core/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bailian-cli-core",
3-
"version": "2.2.0",
3+
"version": "2.2.1",
44
"description": "Core SDK for bailian-cli. See https://www.npmjs.com/package/bailian-cli for usage.",
55
"homepage": "https://bailian.console.aliyun.com/cli",
66
"bugs": {

‎packages/core/src/skills/agents.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
import { homedir } from "node:os";
1111
import { dirname, isAbsolute, join, resolve, sep } from "node:path";
1212
import { getSkillsDir } from "./lock.ts";
13+
import { getHermesHome } from "../utils/agent-home.ts";
1314

1415
/**
1516
* Agent fan-out: after a skill lands in the canonical dir (~/.bailian/skills/<name>),
@@ -164,7 +165,9 @@ export function getAgentTargets(): AgentTarget[] {
164165
detectDirs: [join(xdgConfig, "goose")],
165166
},
166167
fromBase("grok", "Grok Build", envBase(process.env.GROK_HOME, ".grok")),
167-
fromBase("hermes", "Hermes Agent", envBase(process.env.HERMES_HOME, ".hermes")),
168+
// Hermes resolves its home via HERMES_HOME → platform-native default; share the
169+
// canonical resolver so skill fan-out, the config writer and the inventory probe agree.
170+
fromBase("hermes", "Hermes Agent", getHermesHome(home)),
168171
simple("iflow-cli", "iFlow CLI", ".iflow"),
169172
simple("inference-sh", "inference.sh", ".inferencesh"),
170173
{

0 commit comments

Comments
 (0)