fix(paths): structurally resolve home dir to prevent Windows path bugs (#12125)

* fix(paths): structurally resolve home dir to prevent Windows path bugs

Extract resolveRawHomeDir as a private function and gate the public
resolveEffectiveHomeDir through a single path.resolve() exit point.
This makes it structurally impossible for unresolved paths (missing
drive letter on Windows) to escape the function, regardless of how
many return paths exist in the raw lookup logic.

Simplify resolveRequiredHomeDir to only resolve the process.cwd()
fallback, since resolveEffectiveHomeDir now returns resolved values.

Fix shortenMeta in tool-meta.ts: the colon-based split for file:line
patterns (e.g. file.txt:12) conflicts with Windows drive letters
(C:\...) because indexOf(":") matches the drive colon first.
shortenHomeInString already handles file:line patterns correctly via
split/join, so the colon split was both unnecessary and harmful.

Update test assertions across all affected files to use path.resolve()
in expected values and input strings so they match the now-correct
resolved output on both Unix and Windows.

Fixes #12119

* fix(changelog): add paths Windows fix entry (#12125)

---------

Co-authored-by: Sebastian <19554889+sebslight@users.noreply.github.com>
This commit is contained in:
Marcus Castro
2026-02-08 22:06:29 -03:00
committed by GitHub
parent 0244d521a1
commit 456bd58740
12 changed files with 94 additions and 63 deletions

View File

@@ -19,6 +19,7 @@ Docs: https://docs.openclaw.ai
- Config: clamp `maxTokens` to `contextWindow` to prevent invalid model configs. (#5516) Thanks @lailoo. - Config: clamp `maxTokens` to `contextWindow` to prevent invalid model configs. (#5516) Thanks @lailoo.
- Docs: fix language switcher ordering and Japanese locale flag in Mintlify nav. (#12023) Thanks @joshp123. - Docs: fix language switcher ordering and Japanese locale flag in Mintlify nav. (#12023) Thanks @joshp123.
- Paths: make internal path resolution respect `HOME`/`USERPROFILE` before `os.homedir()` across config, agents, sessions, pairing, cron, and CLI profiles. (#12091) Thanks @sebslight. - Paths: make internal path resolution respect `HOME`/`USERPROFILE` before `os.homedir()` across config, agents, sessions, pairing, cron, and CLI profiles. (#12091) Thanks @sebslight.
- Paths: structurally resolve `OPENCLAW_HOME`-derived home paths and fix Windows drive-letter handling in tool meta shortening. (#12125) Thanks @mcaxtr.
- Thinking: allow xhigh for `github-copilot/gpt-5.2-codex` and `github-copilot/gpt-5.2`. (#11646) Thanks @seans-openclawbot. - Thinking: allow xhigh for `github-copilot/gpt-5.2-codex` and `github-copilot/gpt-5.2`. (#11646) Thanks @seans-openclawbot.
- Discord: support forum/media `thread create` starter messages, wire `message thread create --message`, and harden thread-create routing. (#10062) Thanks @jarvis89757. - Discord: support forum/media `thread create` starter messages, wire `message thread create --message`, and harden thread-create routing. (#10062) Thanks @jarvis89757.
- Gateway: stabilize chat routing by canonicalizing node session keys for node-originated chat methods. (#11755) Thanks @mbelinky. - Gateway: stabilize chat routing by canonicalizing node session keys for node-originated chat methods. (#11755) Thanks @mbelinky.

View File

@@ -1,3 +1,4 @@
import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest"; import { afterEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js"; import type { OpenClawConfig } from "../config/config.js";
import { import {
@@ -212,14 +213,17 @@ describe("resolveAgentConfig", () => {
vi.stubEnv("OPENCLAW_HOME", "/srv/openclaw-home"); vi.stubEnv("OPENCLAW_HOME", "/srv/openclaw-home");
const workspace = resolveAgentWorkspaceDir({} as OpenClawConfig, "main"); const workspace = resolveAgentWorkspaceDir({} as OpenClawConfig, "main");
expect(workspace).toBe("/srv/openclaw-home/.openclaw/workspace"); expect(workspace).toBe(path.join(path.resolve("/srv/openclaw-home"), ".openclaw", "workspace"));
}); });
// Unix-style paths behave differently on Windows; skip there // Unix-style paths behave differently on Windows; skip there
it.skipIf(process.platform === "win32")("uses OPENCLAW_HOME for default agentDir", () => { it.skipIf(process.platform === "win32")("uses OPENCLAW_HOME for default agentDir", () => {
vi.stubEnv("OPENCLAW_HOME", "/srv/openclaw-home"); vi.stubEnv("OPENCLAW_HOME", "/srv/openclaw-home");
vi.stubEnv("OPENCLAW_STATE_DIR", "");
const agentDir = resolveAgentDir({} as OpenClawConfig, "main"); const agentDir = resolveAgentDir({} as OpenClawConfig, "main");
expect(agentDir).toBe("/srv/openclaw-home/.openclaw/agents/main/agent"); expect(agentDir).toBe(
path.join(path.resolve("/srv/openclaw-home"), ".openclaw", "agents", "main", "agent"),
);
}); });
}); });

View File

@@ -1,3 +1,4 @@
import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest"; import { afterEach, describe, expect, it, vi } from "vitest";
afterEach(() => { afterEach(() => {
@@ -13,6 +14,8 @@ describe("DEFAULT_AGENT_WORKSPACE_DIR", () => {
vi.resetModules(); vi.resetModules();
const mod = await import("./workspace.js"); const mod = await import("./workspace.js");
expect(mod.DEFAULT_AGENT_WORKSPACE_DIR).toBe("/srv/openclaw-home/.openclaw/workspace"); expect(mod.DEFAULT_AGENT_WORKSPACE_DIR).toBe(
path.join(path.resolve("/srv/openclaw-home"), ".openclaw", "workspace"),
);
}); });
}); });

View File

@@ -15,7 +15,7 @@ describe("resolveDefaultAgentWorkspaceDir", () => {
HOME: "/home/other", HOME: "/home/other",
} as NodeJS.ProcessEnv); } as NodeJS.ProcessEnv);
expect(dir).toBe(path.join("/srv/openclaw-home", ".openclaw", "workspace")); expect(dir).toBe(path.join(path.resolve("/srv/openclaw-home"), ".openclaw", "workspace"));
}); });
}); });

View File

@@ -1,31 +1,35 @@
import path from "node:path";
import { beforeEach, describe, expect, it, vi } from "vitest"; import { beforeEach, describe, expect, it, vi } from "vitest";
import { formatToolAggregate, formatToolPrefix, shortenMeta, shortenPath } from "./tool-meta.js"; import { formatToolAggregate, formatToolPrefix, shortenMeta, shortenPath } from "./tool-meta.js";
// Use path.resolve so inputs match the resolved HOME on every platform.
const home = path.resolve("/Users/test");
describe("tool meta formatting", () => { describe("tool meta formatting", () => {
beforeEach(() => { beforeEach(() => {
vi.unstubAllEnvs(); vi.unstubAllEnvs();
}); });
it("shortens paths under HOME", () => { it("shortens paths under HOME", () => {
vi.stubEnv("HOME", "/Users/test"); vi.stubEnv("HOME", home);
expect(shortenPath("/Users/test")).toBe("~"); expect(shortenPath(home)).toBe("~");
expect(shortenPath("/Users/test/a/b.txt")).toBe("~/a/b.txt"); expect(shortenPath(`${home}/a/b.txt`)).toBe("~/a/b.txt");
expect(shortenPath("/opt/x")).toBe("/opt/x"); expect(shortenPath("/opt/x")).toBe("/opt/x");
}); });
it("shortens meta strings with optional colon suffix", () => { it("shortens meta strings with optional colon suffix", () => {
vi.stubEnv("HOME", "/Users/test"); vi.stubEnv("HOME", home);
expect(shortenMeta("/Users/test/a.txt")).toBe("~/a.txt"); expect(shortenMeta(`${home}/a.txt`)).toBe("~/a.txt");
expect(shortenMeta("/Users/test/a.txt:12")).toBe("~/a.txt:12"); expect(shortenMeta(`${home}/a.txt:12`)).toBe("~/a.txt:12");
expect(shortenMeta("cd /Users/test/dir && ls")).toBe("cd ~/dir && ls"); expect(shortenMeta(`cd ${home}/dir && ls`)).toBe("cd ~/dir && ls");
expect(shortenMeta("")).toBe(""); expect(shortenMeta("")).toBe("");
}); });
it("formats aggregates with grouping and brace-collapse", () => { it("formats aggregates with grouping and brace-collapse", () => {
vi.stubEnv("HOME", "/Users/test"); vi.stubEnv("HOME", home);
const out = formatToolAggregate(" fs ", [ const out = formatToolAggregate(" fs ", [
"/Users/test/dir/a.txt", `${home}/dir/a.txt`,
"/Users/test/dir/b.txt", `${home}/dir/b.txt`,
"note", "note",
"a→b", "a→b",
]); ]);
@@ -36,22 +40,22 @@ describe("tool meta formatting", () => {
}); });
it("wraps aggregate meta in backticks when markdown is enabled", () => { it("wraps aggregate meta in backticks when markdown is enabled", () => {
vi.stubEnv("HOME", "/Users/test"); vi.stubEnv("HOME", home);
const out = formatToolAggregate("fs", ["/Users/test/dir/a.txt"], { markdown: true }); const out = formatToolAggregate("fs", [`${home}/dir/a.txt`], { markdown: true });
expect(out).toContain("`~/dir/a.txt`"); expect(out).toContain("`~/dir/a.txt`");
}); });
it("keeps exec flags outside markdown and moves them to the front", () => { it("keeps exec flags outside markdown and moves them to the front", () => {
vi.stubEnv("HOME", "/Users/test"); vi.stubEnv("HOME", home);
const out = formatToolAggregate("exec", ["cd /Users/test/dir && gemini 2>&1 · elevated"], { const out = formatToolAggregate("exec", [`cd ${home}/dir && gemini 2>&1 · elevated`], {
markdown: true, markdown: true,
}); });
expect(out).toBe("🛠️ Exec: elevated · `cd ~/dir && gemini 2>&1`"); expect(out).toBe("🛠️ Exec: elevated · `cd ~/dir && gemini 2>&1`");
}); });
it("formats prefixes with default labels", () => { it("formats prefixes with default labels", () => {
vi.stubEnv("HOME", "/Users/test"); vi.stubEnv("HOME", home);
expect(formatToolPrefix(undefined, undefined)).toBe("🧩 Tool"); expect(formatToolPrefix(undefined, undefined)).toBe("🧩 Tool");
expect(formatToolPrefix("x", "/Users/test/a.txt")).toBe("🧩 X: ~/a.txt"); expect(formatToolPrefix("x", `${home}/a.txt`)).toBe("🧩 X: ~/a.txt");
}); });
}); });

View File

@@ -13,13 +13,7 @@ export function shortenMeta(meta: string): string {
if (!meta) { if (!meta) {
return meta; return meta;
} }
const colonIdx = meta.indexOf(":"); return shortenHomeInString(meta);
if (colonIdx === -1) {
return shortenHomeInString(meta);
}
const base = meta.slice(0, colonIdx);
const rest = meta.slice(colonIdx);
return `${shortenHomeInString(base)}${rest}`;
} }
export function formatToolAggregate( export function formatToolAggregate(

View File

@@ -61,7 +61,7 @@ describe("applyCliProfileEnv", () => {
env, env,
homedir: () => "/home/peter", homedir: () => "/home/peter",
}); });
const expectedStateDir = path.join("/home/peter", ".openclaw-dev"); const expectedStateDir = path.join(path.resolve("/home/peter"), ".openclaw-dev");
expect(env.OPENCLAW_PROFILE).toBe("dev"); expect(env.OPENCLAW_PROFILE).toBe("dev");
expect(env.OPENCLAW_STATE_DIR).toBe(expectedStateDir); expect(env.OPENCLAW_STATE_DIR).toBe(expectedStateDir);
expect(env.OPENCLAW_CONFIG_PATH).toBe(path.join(expectedStateDir, "openclaw.json")); expect(env.OPENCLAW_CONFIG_PATH).toBe(path.join(expectedStateDir, "openclaw.json"));
@@ -94,9 +94,10 @@ describe("applyCliProfileEnv", () => {
homedir: () => "/home/fallback", homedir: () => "/home/fallback",
}); });
expect(env.OPENCLAW_STATE_DIR).toBe(path.join("/srv/openclaw-home", ".openclaw-work")); const resolvedHome = path.resolve("/srv/openclaw-home");
expect(env.OPENCLAW_STATE_DIR).toBe(path.join(resolvedHome, ".openclaw-work"));
expect(env.OPENCLAW_CONFIG_PATH).toBe( expect(env.OPENCLAW_CONFIG_PATH).toBe(
path.join("/srv/openclaw-home", ".openclaw-work", "openclaw.json"), path.join(resolvedHome, ".openclaw-work", "openclaw.json"),
); );
}); });
}); });

View File

@@ -49,10 +49,11 @@ describe("state + config path candidates", () => {
OPENCLAW_HOME: "/srv/openclaw-home", OPENCLAW_HOME: "/srv/openclaw-home",
} as NodeJS.ProcessEnv; } as NodeJS.ProcessEnv;
expect(resolveStateDir(env)).toBe(path.join("/srv/openclaw-home", ".openclaw")); const resolvedHome = path.resolve("/srv/openclaw-home");
expect(resolveStateDir(env)).toBe(path.join(resolvedHome, ".openclaw"));
const candidates = resolveDefaultConfigCandidates(env); const candidates = resolveDefaultConfigCandidates(env);
expect(candidates[0]).toBe(path.join("/srv/openclaw-home", ".openclaw", "openclaw.json")); expect(candidates[0]).toBe(path.join(resolvedHome, ".openclaw", "openclaw.json"));
}); });
it("prefers OPENCLAW_HOME over HOME for default state/config locations", () => { it("prefers OPENCLAW_HOME over HOME for default state/config locations", () => {
@@ -61,32 +62,34 @@ describe("state + config path candidates", () => {
HOME: "/home/other", HOME: "/home/other",
} as NodeJS.ProcessEnv; } as NodeJS.ProcessEnv;
expect(resolveStateDir(env)).toBe(path.join("/srv/openclaw-home", ".openclaw")); const resolvedHome = path.resolve("/srv/openclaw-home");
expect(resolveStateDir(env)).toBe(path.join(resolvedHome, ".openclaw"));
const candidates = resolveDefaultConfigCandidates(env); const candidates = resolveDefaultConfigCandidates(env);
expect(candidates[0]).toBe(path.join("/srv/openclaw-home", ".openclaw", "openclaw.json")); expect(candidates[0]).toBe(path.join(resolvedHome, ".openclaw", "openclaw.json"));
}); });
it("orders default config candidates in a stable order", () => { it("orders default config candidates in a stable order", () => {
const home = "/home/test"; const home = "/home/test";
const resolvedHome = path.resolve(home);
const candidates = resolveDefaultConfigCandidates({} as NodeJS.ProcessEnv, () => home); const candidates = resolveDefaultConfigCandidates({} as NodeJS.ProcessEnv, () => home);
const expected = [ const expected = [
path.join(home, ".openclaw", "openclaw.json"), path.join(resolvedHome, ".openclaw", "openclaw.json"),
path.join(home, ".openclaw", "clawdbot.json"), path.join(resolvedHome, ".openclaw", "clawdbot.json"),
path.join(home, ".openclaw", "moltbot.json"), path.join(resolvedHome, ".openclaw", "moltbot.json"),
path.join(home, ".openclaw", "moldbot.json"), path.join(resolvedHome, ".openclaw", "moldbot.json"),
path.join(home, ".clawdbot", "openclaw.json"), path.join(resolvedHome, ".clawdbot", "openclaw.json"),
path.join(home, ".clawdbot", "clawdbot.json"), path.join(resolvedHome, ".clawdbot", "clawdbot.json"),
path.join(home, ".clawdbot", "moltbot.json"), path.join(resolvedHome, ".clawdbot", "moltbot.json"),
path.join(home, ".clawdbot", "moldbot.json"), path.join(resolvedHome, ".clawdbot", "moldbot.json"),
path.join(home, ".moltbot", "openclaw.json"), path.join(resolvedHome, ".moltbot", "openclaw.json"),
path.join(home, ".moltbot", "clawdbot.json"), path.join(resolvedHome, ".moltbot", "clawdbot.json"),
path.join(home, ".moltbot", "moltbot.json"), path.join(resolvedHome, ".moltbot", "moltbot.json"),
path.join(home, ".moltbot", "moldbot.json"), path.join(resolvedHome, ".moltbot", "moldbot.json"),
path.join(home, ".moldbot", "openclaw.json"), path.join(resolvedHome, ".moldbot", "openclaw.json"),
path.join(home, ".moldbot", "clawdbot.json"), path.join(resolvedHome, ".moldbot", "clawdbot.json"),
path.join(home, ".moldbot", "moltbot.json"), path.join(resolvedHome, ".moldbot", "moltbot.json"),
path.join(home, ".moldbot", "moldbot.json"), path.join(resolvedHome, ".moldbot", "moldbot.json"),
]; ];
expect(candidates).toEqual(expected); expect(candidates).toEqual(expected);
}); });

View File

@@ -502,6 +502,8 @@ describe("resolveSessionTranscriptCandidates", () => {
const candidates = resolveSessionTranscriptCandidates("sess-1", undefined); const candidates = resolveSessionTranscriptCandidates("sess-1", undefined);
const fallback = candidates[candidates.length - 1]; const fallback = candidates[candidates.length - 1];
expect(fallback).toBe(path.join("/srv/openclaw-home", ".openclaw", "sessions", "sess-1.jsonl")); expect(fallback).toBe(
path.join(path.resolve("/srv/openclaw-home"), ".openclaw", "sessions", "sess-1.jsonl"),
);
}); });
}); });

View File

@@ -1,3 +1,4 @@
import path from "node:path";
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { expandHomePrefix, resolveEffectiveHomeDir, resolveRequiredHomeDir } from "./home-dir.js"; import { expandHomePrefix, resolveEffectiveHomeDir, resolveRequiredHomeDir } from "./home-dir.js";
@@ -9,17 +10,21 @@ describe("resolveEffectiveHomeDir", () => {
USERPROFILE: "C:/Users/other", USERPROFILE: "C:/Users/other",
} as NodeJS.ProcessEnv; } as NodeJS.ProcessEnv;
expect(resolveEffectiveHomeDir(env, () => "/fallback")).toBe("/srv/openclaw-home"); expect(resolveEffectiveHomeDir(env, () => "/fallback")).toBe(
path.resolve("/srv/openclaw-home"),
);
}); });
it("falls back to HOME then USERPROFILE then homedir", () => { it("falls back to HOME then USERPROFILE then homedir", () => {
expect(resolveEffectiveHomeDir({ HOME: "/home/alice" } as NodeJS.ProcessEnv)).toBe( expect(resolveEffectiveHomeDir({ HOME: "/home/alice" } as NodeJS.ProcessEnv)).toBe(
"/home/alice", path.resolve("/home/alice"),
); );
expect(resolveEffectiveHomeDir({ USERPROFILE: "C:/Users/alice" } as NodeJS.ProcessEnv)).toBe( expect(resolveEffectiveHomeDir({ USERPROFILE: "C:/Users/alice" } as NodeJS.ProcessEnv)).toBe(
"C:/Users/alice", path.resolve("C:/Users/alice"),
);
expect(resolveEffectiveHomeDir({} as NodeJS.ProcessEnv, () => "/fallback")).toBe(
path.resolve("/fallback"),
); );
expect(resolveEffectiveHomeDir({} as NodeJS.ProcessEnv, () => "/fallback")).toBe("/fallback");
}); });
it("expands OPENCLAW_HOME when set to ~", () => { it("expands OPENCLAW_HOME when set to ~", () => {
@@ -28,7 +33,7 @@ describe("resolveEffectiveHomeDir", () => {
HOME: "/home/alice", HOME: "/home/alice",
} as NodeJS.ProcessEnv; } as NodeJS.ProcessEnv;
expect(resolveEffectiveHomeDir(env)).toBe("/home/alice/svc"); expect(resolveEffectiveHomeDir(env)).toBe(path.resolve("/home/alice/svc"));
}); });
}); });
@@ -41,6 +46,14 @@ describe("resolveRequiredHomeDir", () => {
).toBe(process.cwd()); ).toBe(process.cwd());
}); });
it("returns a fully resolved path for OPENCLAW_HOME", () => {
const result = resolveRequiredHomeDir(
{ OPENCLAW_HOME: "/custom/home" } as NodeJS.ProcessEnv,
() => "/fallback",
);
expect(result).toBe(path.resolve("/custom/home"));
});
it("returns cwd when OPENCLAW_HOME is tilde-only and no fallback home exists", () => { it("returns cwd when OPENCLAW_HOME is tilde-only and no fallback home exists", () => {
expect( expect(
resolveRequiredHomeDir({ OPENCLAW_HOME: "~" } as NodeJS.ProcessEnv, () => { resolveRequiredHomeDir({ OPENCLAW_HOME: "~" } as NodeJS.ProcessEnv, () => {
@@ -55,7 +68,7 @@ describe("expandHomePrefix", () => {
const value = expandHomePrefix("~/x", { const value = expandHomePrefix("~/x", {
env: { OPENCLAW_HOME: "/srv/openclaw-home" } as NodeJS.ProcessEnv, env: { OPENCLAW_HOME: "/srv/openclaw-home" } as NodeJS.ProcessEnv,
}); });
expect(value).toBe("/srv/openclaw-home/x"); expect(value).toBe(`${path.resolve("/srv/openclaw-home")}/x`);
}); });
it("keeps non-tilde values unchanged", () => { it("keeps non-tilde values unchanged", () => {

View File

@@ -1,4 +1,5 @@
import os from "node:os"; import os from "node:os";
import path from "node:path";
function normalize(value: string | undefined): string | undefined { function normalize(value: string | undefined): string | undefined {
const trimmed = value?.trim(); const trimmed = value?.trim();
@@ -9,6 +10,11 @@ export function resolveEffectiveHomeDir(
env: NodeJS.ProcessEnv = process.env, env: NodeJS.ProcessEnv = process.env,
homedir: () => string = os.homedir, homedir: () => string = os.homedir,
): string | undefined { ): string | undefined {
const raw = resolveRawHomeDir(env, homedir);
return raw ? path.resolve(raw) : undefined;
}
function resolveRawHomeDir(env: NodeJS.ProcessEnv, homedir: () => string): string | undefined {
const explicitHome = normalize(env.OPENCLAW_HOME); const explicitHome = normalize(env.OPENCLAW_HOME);
if (explicitHome) { if (explicitHome) {
if (explicitHome === "~" || explicitHome.startsWith("~/") || explicitHome.startsWith("~\\")) { if (explicitHome === "~" || explicitHome.startsWith("~/") || explicitHome.startsWith("~\\")) {
@@ -47,7 +53,7 @@ export function resolveRequiredHomeDir(
env: NodeJS.ProcessEnv = process.env, env: NodeJS.ProcessEnv = process.env,
homedir: () => string = os.homedir, homedir: () => string = os.homedir,
): string { ): string {
return resolveEffectiveHomeDir(env, homedir) ?? process.cwd(); return resolveEffectiveHomeDir(env, homedir) ?? path.resolve(process.cwd());
} }
export function expandHomePrefix( export function expandHomePrefix(

View File

@@ -142,7 +142,7 @@ describe("resolveHomeDir", () => {
vi.stubEnv("OPENCLAW_HOME", "/srv/openclaw-home"); vi.stubEnv("OPENCLAW_HOME", "/srv/openclaw-home");
vi.stubEnv("HOME", "/home/other"); vi.stubEnv("HOME", "/home/other");
expect(resolveHomeDir()).toBe("/srv/openclaw-home"); expect(resolveHomeDir()).toBe(path.resolve("/srv/openclaw-home"));
vi.unstubAllEnvs(); vi.unstubAllEnvs();
}); });
@@ -153,7 +153,7 @@ describe("shortenHomePath", () => {
vi.stubEnv("OPENCLAW_HOME", "/srv/openclaw-home"); vi.stubEnv("OPENCLAW_HOME", "/srv/openclaw-home");
vi.stubEnv("HOME", "/home/other"); vi.stubEnv("HOME", "/home/other");
expect(shortenHomePath("/srv/openclaw-home/.openclaw/openclaw.json")).toBe( expect(shortenHomePath(`${path.resolve("/srv/openclaw-home")}/.openclaw/openclaw.json`)).toBe(
"$OPENCLAW_HOME/.openclaw/openclaw.json", "$OPENCLAW_HOME/.openclaw/openclaw.json",
); );
@@ -166,9 +166,9 @@ describe("shortenHomeInString", () => {
vi.stubEnv("OPENCLAW_HOME", "/srv/openclaw-home"); vi.stubEnv("OPENCLAW_HOME", "/srv/openclaw-home");
vi.stubEnv("HOME", "/home/other"); vi.stubEnv("HOME", "/home/other");
expect(shortenHomeInString("config: /srv/openclaw-home/.openclaw/openclaw.json")).toBe( expect(
"config: $OPENCLAW_HOME/.openclaw/openclaw.json", shortenHomeInString(`config: ${path.resolve("/srv/openclaw-home")}/.openclaw/openclaw.json`),
); ).toBe("config: $OPENCLAW_HOME/.openclaw/openclaw.json");
vi.unstubAllEnvs(); vi.unstubAllEnvs();
}); });