From 52f88bba4aafa291175870521a892349d7ffcf34 Mon Sep 17 00:00:00 2001 From: Stephen <939775+thecodeassassin@users.noreply.github.com> Date: Thu, 9 Jul 2026 21:46:22 +0000 Subject: [PATCH] fix: install codex-code-mode-host alongside codex (code review) --- internal/agents/codex/codex.go | 14 ++++++++++ internal/agents/codex/codex_test.go | 19 +++++++++++++ internal/agents/codex/docker.go | 42 +++++++++++++++++++++++++---- 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/internal/agents/codex/codex.go b/internal/agents/codex/codex.go index 4f641eb..25b7e6b 100644 --- a/internal/agents/codex/codex.go +++ b/internal/agents/codex/codex.go @@ -55,6 +55,20 @@ func (c *Codex) BinaryName() string { } } +// CodeModeHostBinaryName returns the release tarball for codex-code-mode-host, the +// sibling binary recent Codex versions spawn for code review / "code mode". It ships +// as a separate release asset and must be installed alongside the codex binary. +func (c *Codex) CodeModeHostBinaryName() string { + switch runtime.GOARCH { + case "amd64": + return "codex-code-mode-host-x86_64-unknown-linux-musl.tar.gz" + case "arm64": + return "codex-code-mode-host-aarch64-unknown-linux-musl.tar.gz" + default: + return "" + } +} + func (c *Codex) HostConfigPaths() []string { home := os.Getenv("HOME") return []string{ diff --git a/internal/agents/codex/codex_test.go b/internal/agents/codex/codex_test.go index ce8b1cc..9271558 100644 --- a/internal/agents/codex/codex_test.go +++ b/internal/agents/codex/codex_test.go @@ -47,6 +47,19 @@ func TestCodexAgent(t *testing.T) { } } + // CodeModeHostBinaryName + hbn := c.CodeModeHostBinaryName() + switch runtime.GOARCH { + case "amd64": + if hbn != "codex-code-mode-host-x86_64-unknown-linux-musl.tar.gz" { + t.Errorf("CodeModeHostBinaryName() = %q on amd64", hbn) + } + case "arm64": + if hbn != "codex-code-mode-host-aarch64-unknown-linux-musl.tar.gz" { + t.Errorf("CodeModeHostBinaryName() = %q on arm64", hbn) + } + } + // HostConfigPaths paths := c.HostConfigPaths() if len(paths) != 2 { @@ -73,6 +86,12 @@ func TestCodexAgent(t *testing.T) { if !strings.Contains(df, "apk add --no-cache bubblewrap") { t.Error("GetDockerfileInstall() should install bubblewrap") } + if !strings.Contains(df, "$HOME/.local/bin/codex-code-mode-host") { + t.Error("GetDockerfileInstall() should install codex-code-mode-host alongside codex") + } + if !strings.Contains(df, "CODEX_CODE_MODE_HOST_CHECKSUM") { + t.Error("GetDockerfileInstall() should verify the codex-code-mode-host checksum") + } // GetFullDockerfile full, err := c.GetFullDockerfile("v0.1.0") diff --git a/internal/agents/codex/docker.go b/internal/agents/codex/docker.go index 9103ed0..a078fdf 100644 --- a/internal/agents/codex/docker.go +++ b/internal/agents/codex/docker.go @@ -30,20 +30,33 @@ func (c *Codex) GetDockerfileInstall(buildCtx string) (string, error) { if binaryName == "" { return "", fmt.Errorf("unsupported architecture for Codex") } + hostBinaryName := c.CodeModeHostBinaryName() + if hostBinaryName == "" { + return "", fmt.Errorf("unsupported architecture for Codex") + } binaryInside := strings.TrimSuffix(binaryName, ".tar.gz") + hostBinaryInside := strings.TrimSuffix(hostBinaryName, ".tar.gz") - return fmt.Sprintf(`# Install Codex runtime dependencies and binary with SHA-256 verification + return fmt.Sprintf(`# Install Codex runtime dependencies and binaries with SHA-256 verification. +# codex-code-mode-host is a sibling binary recent Codex versions spawn for code +# review / "code mode"; it ships as a separate release asset and must sit next to +# the codex binary or the feature fails with "codex-code-mode-host is missing". RUN apk add --no-cache bubblewrap ARG CODEX_VERSION ARG CODEX_CHECKSUM +ARG CODEX_CODE_MODE_HOST_CHECKSUM COPY %s /tmp/codex.tar.gz +COPY %s /tmp/codex-code-mode-host.tar.gz RUN echo "${CODEX_CHECKSUM} /tmp/codex.tar.gz" | sha256sum -c - && \ + echo "${CODEX_CODE_MODE_HOST_CHECKSUM} /tmp/codex-code-mode-host.tar.gz" | sha256sum -c - && \ mkdir -p $HOME/.local/bin && \ tar -xzf /tmp/codex.tar.gz -C /tmp && \ + tar -xzf /tmp/codex-code-mode-host.tar.gz -C /tmp && \ mv /tmp/%s $HOME/.local/bin/codex && \ - chmod +x $HOME/.local/bin/codex && \ - rm -f /tmp/codex.tar.gz && \ - $HOME/.local/bin/codex --version`, binaryName, binaryInside), nil + mv /tmp/%s $HOME/.local/bin/codex-code-mode-host && \ + chmod +x $HOME/.local/bin/codex $HOME/.local/bin/codex-code-mode-host && \ + rm -f /tmp/codex.tar.gz /tmp/codex-code-mode-host.tar.gz && \ + $HOME/.local/bin/codex --version`, binaryName, hostBinaryName, binaryInside, hostBinaryInside), nil } func (c *Codex) GetFullDockerfile(version string) (string, error) { @@ -72,6 +85,10 @@ func (c *Codex) PrepareBuild(in agent.PrepareBuildInput) error { if binaryName == "" { return fmt.Errorf("unsupported architecture for Codex") } + hostBinaryName := c.CodeModeHostBinaryName() + if hostBinaryName == "" { + return fmt.Errorf("unsupported architecture for Codex") + } if in.Download == nil || in.FileSHA256 == nil { return fmt.Errorf("PrepareBuildInput.Download and FileSHA256 are required for Codex") } @@ -87,7 +104,22 @@ func (c *Codex) PrepareBuild(in agent.PrepareBuildInput) error { if in.Logf != nil { in.Logf("Codex SHA-256: %s", checksum) } - df := fmt.Sprintf("FROM exitbox-base\n\nARG CODEX_VERSION=%s\nARG CODEX_CHECKSUM=%s\n", version, checksum) + + // codex-code-mode-host is a separate release asset installed alongside codex. + hostURL := fmt.Sprintf("https://github.com/%s/releases/download/%s/%s", codexGitHubRepo, version, hostBinaryName) + if in.Logf != nil { + in.Logf("Downloading codex-code-mode-host %s...", version) + } + hostDlPath := filepath.Join(in.BuildDir, hostBinaryName) + if err := in.Download(in.Ctx, hostURL, hostDlPath); err != nil { + return fmt.Errorf("failed to download codex-code-mode-host: %w", err) + } + hostChecksum := in.FileSHA256(hostDlPath) + if in.Logf != nil { + in.Logf("codex-code-mode-host SHA-256: %s", hostChecksum) + } + + df := fmt.Sprintf("FROM exitbox-base\n\nARG CODEX_VERSION=%s\nARG CODEX_CHECKSUM=%s\nARG CODEX_CODE_MODE_HOST_CHECKSUM=%s\n", version, checksum, hostChecksum) install, err := c.GetDockerfileInstall(in.BuildDir) if err != nil { return fmt.Errorf("failed to get Codex install instructions: %w", err)