Skip to content

Commit 710e0ef

Browse files
committed
fix(exec): expand tilde workdirs from OS home
1 parent 2074df3 commit 710e0ef

3 files changed

Lines changed: 33 additions & 42 deletions

File tree

src/agents/bash-tools.shared.test.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ import { mkdir, mkdtemp, rm } from "node:fs/promises";
77
import os from "node:os";
88
import path from "node:path";
99
import { afterEach, describe, expect, it, vi } from "vitest";
10-
import { deriveSessionName, readEnvInt, resolveSandboxWorkdir } from "./bash-tools.shared.js";
10+
import {
11+
deriveSessionName,
12+
readEnvInt,
13+
resolveSandboxWorkdir,
14+
resolveWorkdir,
15+
} from "./bash-tools.shared.js";
1116

1217
async function withTempDir(run: (dir: string) => Promise<void>) {
1318
const dir = await mkdtemp(path.join(os.tmpdir(), "openclaw-bash-workdir-"));
@@ -144,3 +149,26 @@ describe("deriveSessionName", () => {
144149
}
145150
});
146151
});
152+
153+
describe("resolveWorkdir", () => {
154+
afterEach(() => {
155+
vi.unstubAllEnvs();
156+
});
157+
158+
it("expands tilde using the OS home instead of OPENCLAW_HOME", async () => {
159+
await withTempDir(async (root) => {
160+
const osHome = path.join(root, "os-home");
161+
const openclawHome = path.join(root, "openclaw-home");
162+
const workspace = path.join(osHome, "workspace");
163+
await mkdir(workspace, { recursive: true });
164+
await mkdir(openclawHome, { recursive: true });
165+
vi.stubEnv("HOME", osHome);
166+
vi.stubEnv("OPENCLAW_HOME", openclawHome);
167+
168+
const warnings: string[] = [];
169+
170+
expect(resolveWorkdir("~/workspace", warnings)).toBe(workspace);
171+
expect(warnings).toStrictEqual([]);
172+
});
173+
});
174+
});

src/agents/bash-tools.shared.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import fs from "node:fs/promises";
88
import { homedir } from "node:os";
99
import path from "node:path";
1010
import { parseStrictInteger } from "@openclaw/normalization-core/number-coercion";
11+
import { expandHomePrefix, resolveRequiredOsHomeDir } from "../infra/home-dir.js";
1112
import { sliceUtf16Safe } from "../utils.js";
1213
import { assertSandboxPath } from "./sandbox-paths.js";
1314
import type { SandboxBackendExecSpec } from "./sandbox/backend-handle.types.js";
@@ -181,18 +182,13 @@ function normalizeContainerPath(input: string): string {
181182

182183
/** Resolves a host workdir, falling back to a safe cwd/home path with a warning. */
183184
export function resolveWorkdir(workdir: string, warnings: string[]) {
184-
// Expand leading ~/ (or bare ~) to the user's home directory.
185-
if (workdir.startsWith("~/")) {
186-
workdir = homedir() + workdir.slice(1);
187-
} else if (workdir === "~") {
188-
workdir = homedir();
189-
}
190185
const current = safeCwd();
191186
const fallback = current ?? homedir();
187+
const expandedWorkdir = expandHomePrefix(workdir, { home: resolveRequiredOsHomeDir() });
192188
try {
193-
const stats = statSync(workdir);
189+
const stats = statSync(expandedWorkdir);
194190
if (stats.isDirectory()) {
195-
return workdir;
191+
return expandedWorkdir;
196192
}
197193
} catch {
198194
// ignore, fallback below

src/agents/bash-tools.test.ts

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import {
2727
type ProcessSession,
2828
} from "./bash-process-registry.js";
2929
import { createExecTool, createProcessTool } from "./bash-tools.js";
30-
import { resolveWorkdir } from "./bash-tools.shared.js";
3130
import { resolveShellFromPath, sanitizeBinaryOutput } from "./shell-utils.js";
3231

3332
vi.mock("../infra/channel-summary.js", () => ({
@@ -1103,35 +1102,3 @@ describe("exec backgrounded onUpdate suppression", () => {
11031102
isWin ? 10_000 : 5_000,
11041103
);
11051104
});
1106-
1107-
describe("resolveWorkdir", () => {
1108-
// FIX #94434: tilde expansion - expanded ~/ no longer contains literal "~"
1109-
it("expands leading ~/ so warning shows expanded path not literal tilde", () => {
1110-
const warnings: string[] = [];
1111-
resolveWorkdir("~/nonexistent-94434-test", warnings);
1112-
expect(warnings.length).toBeGreaterThan(0);
1113-
// Warning must reference the expanded path, not the literal "~/"
1114-
expect(warnings[0]).not.toContain('"~/');
1115-
// Warning must contain the home directory
1116-
expect(warnings[0]).toContain("/home/");
1117-
});
1118-
1119-
// FIX #94434: bare ~ expands to homedir and succeeds when homedir exists
1120-
it("expands bare ~ to homedir successfully", () => {
1121-
const warnings: string[] = [];
1122-
const result = resolveWorkdir("~", warnings);
1123-
// ~ expands to homedir; homedir exists so no warning
1124-
expect(warnings).toHaveLength(0);
1125-
// Result is a non-empty string (the homedir path)
1126-
expect(result.length).toBeGreaterThan(0);
1127-
expect(result.startsWith("/home/") || result.startsWith("/root/")).toBe(true);
1128-
});
1129-
1130-
it("falls back with warning when absolute path does not exist", () => {
1131-
const warnings: string[] = [];
1132-
const result = resolveWorkdir("/nonexistent-94434-test-path", warnings);
1133-
expect(warnings.length).toBeGreaterThan(0);
1134-
expect(warnings[0]).toContain("Warning: workdir");
1135-
expect(result).toEqual(expect.any(String));
1136-
});
1137-
});

0 commit comments

Comments
 (0)