Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions cmd/onecli/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 "://").
Expand Down
36 changes: 36 additions & 0 deletions cmd/onecli/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading