From 0bcc245c9554448cf4fcae57a6ed968a8c41f51e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 3 Apr 2026 12:33:33 +0000 Subject: [PATCH] fix: resolve double-nested home paths in cowork mountPath (#373) The mountPath() methods in HostBackend, BwrapBackend, and KvmBackend joined os.homedir() with a root-relative subpath, causing paths like /home/user/home/user/.config/Claude/... instead of the correct /home/user/.config/Claude/... The subpath parameter is encoded as path.relative("/", absolutePath), making it root-relative. Joining with "/" instead of os.homedir() produces the correct absolute path. Fixes #373 Co-Authored-By: Claude https://claude.ai/code/session_01TEWYXVLaKgBfKVkHY47g9M --- scripts/cowork-vm-service.js | 6 ++--- tests/cowork-path-translation.bats | 40 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/scripts/cowork-vm-service.js b/scripts/cowork-vm-service.js index 4e6cf87..256f648 100644 --- a/scripts/cowork-vm-service.js +++ b/scripts/cowork-vm-service.js @@ -731,7 +731,7 @@ class HostBackend extends LocalBackend { async mountPath(params) { const { subpath } = params; log(`HostBackend mountPath: ${subpath}`); - const guestPath = path.join(os.homedir(), subpath || ''); + const guestPath = path.join('/', subpath || ''); return { guestPath }; } } @@ -910,7 +910,7 @@ class BwrapBackend extends LocalBackend { async mountPath(params) { const { subpath, mountName } = params; log(`BwrapBackend mountPath: ${mountName} -> ${subpath}`); - const hostPath = path.join(os.homedir(), subpath || ''); + const hostPath = path.join('/', subpath || ''); // Store for --bind on next spawn this.mountBinds.set(mountName || subpath, hostPath); return { guestPath: hostPath }; @@ -1720,7 +1720,7 @@ class KvmBackend extends BackendBase { } // No home share — return host path with a warning - const hostPath = path.join(os.homedir(), subpath || ''); + const hostPath = path.join('/', subpath || ''); log('KvmBackend: no home share, returning host path'); return { guestPath: hostPath }; } diff --git a/tests/cowork-path-translation.bats b/tests/cowork-path-translation.bats index f559e5f..5144458 100644 --- a/tests/cowork-path-translation.bats +++ b/tests/cowork-path-translation.bats @@ -823,3 +823,43 @@ assertEqual( " [[ "$status" -eq 0 ]] } + +# ============================================================================= +# mountPath — subpath is root-relative, must NOT be joined with homedir +# ============================================================================= + +# These tests replicate the mountPath() logic from HostBackend and +# BwrapBackend to verify that root-relative subpaths resolve correctly +# (fix for #373: double-nested home directory paths). + +@test "mountPath: root-relative subpath resolves to absolute path, not double-nested" { + run node -e "${NODE_PREAMBLE} +// Simulate what the caller sends: path.relative('/', '/home/user/.config/Claude') +const subpath = 'home/user/.config/Claude'; + +// Fixed logic: join with '/' (root), not os.homedir() +const guestPath = path.join('/', subpath); +assertEqual(guestPath, '/home/user/.config/Claude', + 'root-relative subpath should resolve to single absolute path'); +" + [[ "$status" -eq 0 ]] +} + +@test "mountPath: empty subpath resolves to root" { + run node -e "${NODE_PREAMBLE} +const guestPath = path.join('/', ''); +assertEqual(guestPath, '/', 'empty subpath -> root'); +" + [[ "$status" -eq 0 ]] +} + +@test "mountPath: subpath with nested directories resolves correctly" { + run node -e "${NODE_PREAMBLE} +const subpath = 'home/raycharlizard/.config/Claude/local-agent-mode-sessions/outputs'; +const guestPath = path.join('/', subpath); +assertEqual(guestPath, + '/home/raycharlizard/.config/Claude/local-agent-mode-sessions/outputs', + 'nested subpath should not double the home prefix'); +" + [[ "$status" -eq 0 ]] +}