Translate PWD for WSL-launched Windows agents (#7111)
Bridge PWD through WSLENV /p when launching a Windows npm shim from WSL so project-root discovery uses the live cwd. The no-launch recipe adds PWD/p without freezing PWD; the concrete cwd override applies only on direct launch.
This commit is contained in:
parent
e0132b6d6c
commit
39497e6516
2 changed files with 28 additions and 4 deletions
|
|
@ -1274,6 +1274,16 @@ def _install_agent(name: str, install_hint: str) -> Optional[str]:
|
|||
return executable
|
||||
|
||||
|
||||
def _wsl_shim_env(command: list, env: dict, unset_env: tuple) -> tuple[dict, tuple]:
|
||||
wsl_env_bridge = _wsl_bridge_names(env, unset_env) if _wsl_windows_executable(command) else ()
|
||||
if not wsl_env_bridge:
|
||||
return env, wsl_env_bridge
|
||||
# Bridge PWD via WSLENV (PWD/p) so the Windows shim finds its project root from the
|
||||
# live cwd, not a stale inherited Linux PWD. Don't freeze env["PWD"]: a --no-launch
|
||||
# recipe must translate the live PWD when run, not when generated; _launch overrides it.
|
||||
return env, (*wsl_env_bridge, "PWD/p")
|
||||
|
||||
|
||||
def _launch(
|
||||
command: list,
|
||||
env: dict,
|
||||
|
|
@ -1283,9 +1293,11 @@ def _launch(
|
|||
executable = shutil.which(command[0]) or _install_agent(command[0], install_hint)
|
||||
if executable is None:
|
||||
_fail(f"`{command[0]}` not found on PATH. Install it with: {install_hint}")
|
||||
wsl_env_bridge = _wsl_bridge_names(env, unset_env) if _wsl_windows_executable(command) else ()
|
||||
env, wsl_env_bridge = _wsl_shim_env(command, env, unset_env)
|
||||
child_env = dict(os.environ)
|
||||
if wsl_env_bridge:
|
||||
# Override stale inherited PWD with the real cwd so the shim resolves the project root.
|
||||
env = {**env, "PWD": os.getcwd()}
|
||||
child_env["WSLENV"] = _merge_wslenv(child_env.get("WSLENV", ""), wsl_env_bridge)
|
||||
for name in unset_env:
|
||||
child_env[name] = ""
|
||||
|
|
@ -1353,8 +1365,8 @@ def _run(
|
|||
if launch and clear_screen:
|
||||
click.clear()
|
||||
typer.echo(f"Unsloth {base} · model {entry['id']}")
|
||||
wsl_env_bridge = _wsl_bridge_names(env, unset_env) if _wsl_windows_executable(command) else ()
|
||||
if not launch:
|
||||
env, wsl_env_bridge = _wsl_shim_env(command, env, unset_env)
|
||||
_print_env(env, command, unset_env = unset_env, wsl_env_bridge = wsl_env_bridge)
|
||||
return
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -476,8 +476,10 @@ def test_connect_claude_launch_scrubs_conflicting_auth_env(fake_studio, monkeypa
|
|||
reason = "WSL-from-Linux scenario (calling a Windows agent .exe from inside WSL); "
|
||||
"os.name is 'posix' under WSL, so this path can't run on a native Windows runner.",
|
||||
)
|
||||
def test_connect_claude_windows_shim_from_wsl_bridges_env(fake_studio, monkeypatch):
|
||||
def test_connect_claude_windows_shim_from_wsl_bridges_env(fake_studio, monkeypatch, tmp_path):
|
||||
captured = {}
|
||||
monkeypatch.chdir(tmp_path)
|
||||
monkeypatch.setenv("PWD", "/stale/outer/repo")
|
||||
monkeypatch.setenv("WSL_DISTRO_NAME", "Ubuntu")
|
||||
monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-anthropic-stale")
|
||||
monkeypatch.setenv("CLAUDE_CODE_OAUTH_TOKEN", "oauth-stale")
|
||||
|
|
@ -505,6 +507,8 @@ def test_connect_claude_windows_shim_from_wsl_bridges_env(fake_studio, monkeypat
|
|||
assert captured["env"]["ANTHROPIC_AUTH_TOKEN"] == "sk-unsloth-feedfacefeedface"
|
||||
assert captured["env"]["ANTHROPIC_BASE_URL"] == BASE
|
||||
assert captured["env"]["ANTHROPIC_MODEL"] == MODEL["id"]
|
||||
assert captured["env"]["PWD"] == str(tmp_path)
|
||||
assert "PWD/p" in captured["env"]["WSLENV"].split(":")
|
||||
for name in (
|
||||
"ANTHROPIC_AUTH_TOKEN",
|
||||
"ANTHROPIC_BASE_URL",
|
||||
|
|
@ -520,7 +524,11 @@ def test_connect_claude_windows_shim_from_wsl_bridges_env(fake_studio, monkeypat
|
|||
reason = "WSL-from-Linux scenario (calling a Windows agent .exe from inside WSL); "
|
||||
"os.name is 'posix' under WSL, so this path can't run on a native Windows runner.",
|
||||
)
|
||||
def test_connect_claude_no_launch_windows_shim_from_wsl_prints_wslenv(fake_studio, monkeypatch):
|
||||
def test_connect_claude_no_launch_windows_shim_from_wsl_prints_wslenv(
|
||||
fake_studio, monkeypatch, tmp_path
|
||||
):
|
||||
monkeypatch.chdir(tmp_path)
|
||||
monkeypatch.setenv("PWD", "/stale/outer/repo")
|
||||
monkeypatch.setenv("WSL_DISTRO_NAME", "Ubuntu")
|
||||
monkeypatch.setattr(
|
||||
start.shutil, "which", lambda _: "/mnt/c/Users/samle/AppData/Roaming/npm/claude"
|
||||
|
|
@ -532,6 +540,10 @@ def test_connect_claude_no_launch_windows_shim_from_wsl_prints_wslenv(fake_studi
|
|||
assert "export ANTHROPIC_API_KEY=" in result.output
|
||||
assert "export CLAUDE_CODE_OAUTH_TOKEN=" in result.output
|
||||
assert "export WSLENV=" in result.output
|
||||
# PWD must NOT be frozen into the recipe (no `export PWD=`): WSLENV PWD/p translates the
|
||||
# shell's live PWD at run time, so a recipe reused from another dir resolves the project root.
|
||||
assert "export PWD=" not in result.output
|
||||
assert "PWD/p" in result.output
|
||||
assert "ANTHROPIC_AUTH_TOKEN" in result.output
|
||||
assert "CLAUDE_CODE_OAUTH_TOKEN" in result.output
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue