From 8ee620f469d4861db36a848be5e3a823682b0dc8 Mon Sep 17 00:00:00 2001 From: johnnyfish Date: Mon, 15 Jun 2026 10:13:00 +0300 Subject: [PATCH] fix: rewrite container CODEX_HOME to local ~/.codex on run --- cmd/onecli/run.go | 34 ++++++++++++++++++++++++++++++++++ cmd/onecli/run_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/cmd/onecli/run.go b/cmd/onecli/run.go index b9f5ff8..46b1d5d 100644 --- a/cmd/onecli/run.go +++ b/cmd/onecli/run.go @@ -78,6 +78,15 @@ func (c *RunCmd) Run(out *output.Writer) error { // Keeping it in the env triggers a first-run confirmation prompt in Claude Code. delete(cfg.Env, "ANTHROPIC_API_KEY") + // Some agents read their home from an env var the server returns as a + // container path (e.g. CODEX_HOME=/home/node/.codex), which doesn't exist + // on the host — Codex aborts with "path does not exist". Rewrite these to + // the local equivalent under the user's home, where onecli writes the auth + // stub and native proxy config below. + if home, err := os.UserHomeDir(); err == nil { + rewriteContainerHomeEnv(cfg.Env, home) + } + // Dry-run: print resolved config without side effects (no CA write, // no skill install, no exec). if c.DryRun { @@ -309,6 +318,31 @@ func resolveLocalGatewayHost() string { return u.Hostname() } +// containerHomeEnv maps env vars that the server returns as container-internal +// home paths to their home-relative local equivalent. A local agent process +// needs host paths (where onecli writes the agent's auth stub and config), not +// the Docker sandbox paths the server returns (e.g. CODEX_HOME=/home/node/.codex). +var containerHomeEnv = map[string]string{ + "CODEX_HOME": ".codex", +} + +// rewriteContainerHomeEnv replaces container-internal home paths in the server +// env with the local equivalent under home. Codex aborts when CODEX_HOME points +// at a path that does not exist on the host, so the container path must be +// translated before exec. Mutating cfg.Env (rather than only appending later) +// also ensures buildChildEnv strips any stale inherited value, so the container +// path can't shadow the rewritten one. +func rewriteContainerHomeEnv(env map[string]string, home string) { + if home == "" { + return + } + for k, rel := range containerHomeEnv { + if _, ok := env[k]; ok { + env[k] = filepath.Join(home, rel) + } + } +} + // rewriteProxyEnvHosts replaces Docker-internal hostnames in proxy URL values // with the given local host, keeping the port and credentials intact. // Only rewrites values that look like proxy URLs (contain "://"). diff --git a/cmd/onecli/run_test.go b/cmd/onecli/run_test.go index c0d9111..80f2fe6 100644 --- a/cmd/onecli/run_test.go +++ b/cmd/onecli/run_test.go @@ -95,6 +95,42 @@ func TestStripProxyCredentials(t *testing.T) { } } +func TestRewriteContainerHomeEnv(t *testing.T) { + t.Run("rewrites container CODEX_HOME to local", func(t *testing.T) { + env := map[string]string{ + "CODEX_HOME": "/home/node/.codex", + "HTTPS_PROXY": "https://proxy:8080", + } + rewriteContainerHomeEnv(env, "/Users/me") + if got, want := env["CODEX_HOME"], filepath.Join("/Users/me", ".codex"); got != want { + t.Errorf("CODEX_HOME = %q, want %q", got, want) + } + if env["HTTPS_PROXY"] != "https://proxy:8080" { + t.Errorf("HTTPS_PROXY mutated: %q", env["HTTPS_PROXY"]) + } + }) + + t.Run("no-op when var absent", func(t *testing.T) { + env := map[string]string{"PATH": "/usr/bin"} + rewriteContainerHomeEnv(env, "/Users/me") + if _, ok := env["CODEX_HOME"]; ok { + t.Error("CODEX_HOME should not be added when absent") + } + }) + + t.Run("no-op when home empty", func(t *testing.T) { + env := map[string]string{"CODEX_HOME": "/home/node/.codex"} + rewriteContainerHomeEnv(env, "") + if env["CODEX_HOME"] != "/home/node/.codex" { + t.Errorf("CODEX_HOME = %q, want unchanged", env["CODEX_HOME"]) + } + }) + + t.Run("nil map is safe", func(t *testing.T) { + rewriteContainerHomeEnv(nil, "/Users/me") + }) +} + func TestAgentSkillDir(t *testing.T) { tests := []struct { cmd string