From b46588f247f5f6bb72145260370f4544490a3bf4 Mon Sep 17 00:00:00 2001 From: Devvyn <22340871+opticon454@users.noreply.github.com> Date: Thu, 24 Sep 2026 11:33:00 +0800 Subject: [PATCH 1/9] fix(docker): install pnpm in the Compose server image `dsh plugin` spawns a literal `pnpm` with no npm fallback, so the Run menu's "DeepSeek - add a terminal profile" button failed with `dsh: pnpm not found on PATH` (exit 127) on the server image. The agent image already installs pnpm for the same reason (#352). Pin pnpm@12.6.0 in the runtime-writable CLI prefix and note it in the DeepSeek doc. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01GuHtuPiHXdykq9T6rKQJ9n --- .changeset/serverpnpm3.md | 5 +++++ docker/server.Dockerfile | 8 ++++++++ docs/deepseek-integration.md | 4 +++- 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 .changeset/serverpnpm3.md diff --git a/.changeset/serverpnpm3.md b/.changeset/serverpnpm3.md new file mode 100644 index 00000000..86fe7bb7 --- /dev/null +++ b/.changeset/serverpnpm3.md @@ -0,0 +1,5 @@ +--- +"aicodeman": patch +--- + +Install pnpm in the Docker Compose server image. `dsh plugin` spawns a literal `pnpm` with no npm fallback, so the Run menu's "DeepSeek - add a terminal profile" button failed with `dsh: pnpm not found on PATH` in that image. Because this changes `server.Dockerfile`, the in-app updater will ask Compose deployments to rebuild the image (`Update-Codeman.sh`) rather than apply this release in place. diff --git a/docker/server.Dockerfile b/docker/server.Dockerfile index 09b4607a..8e957d26 100644 --- a/docker/server.Dockerfile +++ b/docker/server.Dockerfile @@ -214,11 +214,19 @@ RUN set -eux; \ # system directories for the root part of the start. ENV NPM_CONFIG_PREFIX=/opt/codeman-cli ENV PATH=$PATH:/opt/codeman-cli/bin +# pnpm is not an agent CLI: it is here because `dsh plugin` (DeepSeek Harness, which +# this image leaves to be installed at runtime, see SERVER_INTENTIONAL_OMISSIONS in +# test/docker-agent-image-coverage.test.ts) spawns a literal `pnpm` with no npm +# fallback, so the Run menu's "DeepSeek - add a terminal profile" button failed +# with `dsh: pnpm not found on PATH` (exit 127) on this image. The agent image +# already carries it for the same reason (#352). It lives in the same +# runtime-writable prefix as the CLIs, so a session can update it in place. RUN npm install --global \ @anthropic-ai/claude-code@2.1.258 \ @google/gemini-cli@0.58.0 \ @openai/codex@0.152.1 \ opencode-ai@1.18.26 \ + pnpm@12.6.0 \ && npm cache clean --force # Keep the web server and every local Codeman session unprivileged. PUID and diff --git a/docs/deepseek-integration.md b/docs/deepseek-integration.md index 8232af92..6b4fd61c 100644 --- a/docs/deepseek-integration.md +++ b/docs/deepseek-integration.md @@ -53,7 +53,9 @@ that spawns a literal `pnpm` with no npm fallback, so without one it exits 127 w surfaces that same line as the install error. `npm install -g pnpm` (or `corepack enable pnpm`) is the fix. This is what broke the Docker agent image in [#352](https://github.com/Ark0N/Codeman/issues/352); the image now installs pnpm -alongside `dsh`. +alongside `dsh`. The Compose server image (`docker/server.Dockerfile`) does not +ship `dsh`, since it is installed at runtime, but it does ship pnpm so the UI +button works there too. Codeman's default is `@deepseek-harness-tui/dsh-tui` because it is by a wide margin the most used community TUI, it is MIT, and it implements the status From a5283c565db6aadd176852822f5ba3fd23d07a31 Mon Sep 17 00:00:00 2001 From: Devvyn <22340871+opticon454@users.noreply.github.com> Date: Thu, 24 Sep 2026 21:04:18 +0800 Subject: [PATCH 2/9] feat(docker): install uv and uvx in server and agent images Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01GuHtuPiHXdykq9T6rKQJ9n --- .changeset/uvxdocker1.md | 5 +++++ docker/agent.Dockerfile | 4 ++++ docker/server.Dockerfile | 4 ++++ 3 files changed, 13 insertions(+) create mode 100644 .changeset/uvxdocker1.md diff --git a/.changeset/uvxdocker1.md b/.changeset/uvxdocker1.md new file mode 100644 index 00000000..32e2d9b8 --- /dev/null +++ b/.changeset/uvxdocker1.md @@ -0,0 +1,5 @@ +--- +"aicodeman": patch +--- + +Install `uv` and `uvx` in the Compose server image and the agent image, so MCP servers launched with `uvx` (such as the Nginx Proxy Manager MCP) can be enabled by Codex instead of failing with `uvx` not found. The server image also carries `pnpm` for `dsh plugin`. diff --git a/docker/agent.Dockerfile b/docker/agent.Dockerfile index f7b46058..1082ee71 100644 --- a/docker/agent.Dockerfile +++ b/docker/agent.Dockerfile @@ -126,6 +126,10 @@ RUN set -eux; \ # A different order is a different RUN string, which is a different layer hash and # so a needless cache miss between a bare `docker build` and a scripted one. ARG CLI_NPM_PACKAGES="@anthropic-ai/claude-code opencode-ai @openai/codex @google/gemini-cli" +# uv/uvx: MCP servers are commonly launched with `uvx ` (e.g. the Nginx +# Proxy Manager MCP), and Codex failed to enable them with "uvx not found". Copied +# from the pinned upstream image into root-owned /usr/local/bin, never pip-installed. +COPY --from=ghcr.io/astral-sh/uv:0.9 /uv /uvx /usr/local/bin/ RUN npm install -g ${CLI_NPM_PACKAGES} \ && npm cache clean --force diff --git a/docker/server.Dockerfile b/docker/server.Dockerfile index 8e957d26..939cf3fa 100644 --- a/docker/server.Dockerfile +++ b/docker/server.Dockerfile @@ -212,6 +212,10 @@ RUN set -eux; \ # minimal image of this exact shape). The four CLIs live only in this prefix, # so they still resolve; entrypoint.sh additionally pins its own PATH to the # system directories for the root part of the start. +# uv/uvx: MCP servers are commonly launched with `uvx ` (e.g. the Nginx +# Proxy Manager MCP), and Codex failed to enable them with "uvx not found". Copied +# from the pinned upstream image into root-owned /usr/local/bin, never pip-installed. +COPY --from=ghcr.io/astral-sh/uv:0.9 /uv /uvx /usr/local/bin/ ENV NPM_CONFIG_PREFIX=/opt/codeman-cli ENV PATH=$PATH:/opt/codeman-cli/bin # pnpm is not an agent CLI: it is here because `dsh plugin` (DeepSeek Harness, which From 3e3a4612e66a0dbd024e86d061947badbe5fbc52 Mon Sep 17 00:00:00 2001 From: Devvyn <22340871+opticon454@users.noreply.github.com> Date: Thu, 24 Sep 2026 22:03:24 +0800 Subject: [PATCH 3/9] feat(docker): install libsecret-1-0 for the Azure DevOps MCP Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01GuHtuPiHXdykq9T6rKQJ9n --- .changeset/uvxdocker1.md | 2 ++ docker/agent.Dockerfile | 1 + docker/server.Dockerfile | 1 + 3 files changed, 4 insertions(+) diff --git a/.changeset/uvxdocker1.md b/.changeset/uvxdocker1.md index 32e2d9b8..b4c74474 100644 --- a/.changeset/uvxdocker1.md +++ b/.changeset/uvxdocker1.md @@ -3,3 +3,5 @@ --- Install `uv` and `uvx` in the Compose server image and the agent image, so MCP servers launched with `uvx` (such as the Nginx Proxy Manager MCP) can be enabled by Codex instead of failing with `uvx` not found. The server image also carries `pnpm` for `dsh plugin`. + +Both images also install `libsecret-1-0`, the native library the `keytar` dependency of the Azure DevOps MCP (`@azure-devops/mcp`) needs; without it the server crashes before answering the MCP initialize handshake. diff --git a/docker/agent.Dockerfile b/docker/agent.Dockerfile index 1082ee71..fa7dfdd9 100644 --- a/docker/agent.Dockerfile +++ b/docker/agent.Dockerfile @@ -17,6 +17,7 @@ FROM node:22-bookworm-slim RUN apt-get update \ && apt-get install -y --no-install-recommends \ git \ + libsecret-1-0 \ tmux \ ripgrep \ curl \ diff --git a/docker/server.Dockerfile b/docker/server.Dockerfile index 939cf3fa..99142abc 100644 --- a/docker/server.Dockerfile +++ b/docker/server.Dockerfile @@ -39,6 +39,7 @@ RUN apt-get update \ curl \ g++ \ git \ + libsecret-1-0 \ make \ openssh-client \ procps \ From e98127a80414736c47f5c349038dacd23d2948c2 Mon Sep 17 00:00:00 2001 From: Devvyn <22340871+opticon454@users.noreply.github.com> Date: Thu, 24 Sep 2026 22:17:22 +0800 Subject: [PATCH 4/9] feat(docker): add sudo to the agent image Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01GuHtuPiHXdykq9T6rKQJ9n --- .changeset/uvxdocker1.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.changeset/uvxdocker1.md b/.changeset/uvxdocker1.md index b4c74474..0535c953 100644 --- a/.changeset/uvxdocker1.md +++ b/.changeset/uvxdocker1.md @@ -5,3 +5,5 @@ Install `uv` and `uvx` in the Compose server image and the agent image, so MCP servers launched with `uvx` (such as the Nginx Proxy Manager MCP) can be enabled by Codex instead of failing with `uvx` not found. The server image also carries `pnpm` for `dsh plugin`. Both images also install `libsecret-1-0`, the native library the `keytar` dependency of the Azure DevOps MCP (`@azure-devops/mcp`) needs; without it the server crashes before answering the MCP initialize handshake. + +The agent image also installs `sudo` with passwordless access for the `agent` user, so a session can install system packages itself. The Compose server image is unchanged here: it runs with `no-new-privileges` and `cap_drop: ALL`, where `sudo` cannot work. From b070c9ee65fb9ef47af105bea96396fd5894bf45 Mon Sep 17 00:00:00 2001 From: Devvyn <22340871+opticon454@users.noreply.github.com> Date: Thu, 24 Sep 2026 22:17:38 +0800 Subject: [PATCH 5/9] feat(docker): install sudo with passwordless access for the agent user Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01GuHtuPiHXdykq9T6rKQJ9n --- docker/agent.Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker/agent.Dockerfile b/docker/agent.Dockerfile index fa7dfdd9..359fd8d9 100644 --- a/docker/agent.Dockerfile +++ b/docker/agent.Dockerfile @@ -18,6 +18,7 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends \ git \ libsecret-1-0 \ + sudo \ tmux \ ripgrep \ curl \ @@ -244,6 +245,8 @@ ENV HOME=/home/agent # Codeman's own host-side history/resume reads), and neither kind of artifact # creates its own parent directory. RUN useradd -g 0 -m -d /home/agent -s /bin/bash agent \ + && echo 'agent ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/agent \ + && chmod 0440 /etc/sudoers.d/agent \ && mkdir -p /home/agent/.npm /home/agent/.cache /home/agent/.config /home/agent/.codeman \ /home/agent/.claude/projects /home/agent/.codex/sessions /home/agent/.pi/agent /home/agent/.grok \ /home/agent/.dsh /home/agent/.omp/agent \ From 10c263a5b832e33bfc9b76e4a2af49fe2959dc08 Mon Sep 17 00:00:00 2001 From: Devvyn <22340871+opticon454@users.noreply.github.com> Date: Thu, 24 Sep 2026 22:19:13 +0800 Subject: [PATCH 6/9] Revert "feat(docker): install sudo with passwordless access for the agent user" This reverts commit b070c9ee65fb9ef47af105bea96396fd5894bf45. --- docker/agent.Dockerfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/docker/agent.Dockerfile b/docker/agent.Dockerfile index 359fd8d9..fa7dfdd9 100644 --- a/docker/agent.Dockerfile +++ b/docker/agent.Dockerfile @@ -18,7 +18,6 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends \ git \ libsecret-1-0 \ - sudo \ tmux \ ripgrep \ curl \ @@ -245,8 +244,6 @@ ENV HOME=/home/agent # Codeman's own host-side history/resume reads), and neither kind of artifact # creates its own parent directory. RUN useradd -g 0 -m -d /home/agent -s /bin/bash agent \ - && echo 'agent ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/agent \ - && chmod 0440 /etc/sudoers.d/agent \ && mkdir -p /home/agent/.npm /home/agent/.cache /home/agent/.config /home/agent/.codeman \ /home/agent/.claude/projects /home/agent/.codex/sessions /home/agent/.pi/agent /home/agent/.grok \ /home/agent/.dsh /home/agent/.omp/agent \ From d6c3386102482c2a18f02a194aa059426aa64394 Mon Sep 17 00:00:00 2001 From: Devvyn <22340871+opticon454@users.noreply.github.com> Date: Thu, 24 Sep 2026 22:19:20 +0800 Subject: [PATCH 7/9] Revert "feat(docker): add sudo to the agent image" This reverts commit e98127a80414736c47f5c349038dacd23d2948c2. --- .changeset/uvxdocker1.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/.changeset/uvxdocker1.md b/.changeset/uvxdocker1.md index 0535c953..b4c74474 100644 --- a/.changeset/uvxdocker1.md +++ b/.changeset/uvxdocker1.md @@ -5,5 +5,3 @@ Install `uv` and `uvx` in the Compose server image and the agent image, so MCP servers launched with `uvx` (such as the Nginx Proxy Manager MCP) can be enabled by Codex instead of failing with `uvx` not found. The server image also carries `pnpm` for `dsh plugin`. Both images also install `libsecret-1-0`, the native library the `keytar` dependency of the Azure DevOps MCP (`@azure-devops/mcp`) needs; without it the server crashes before answering the MCP initialize handshake. - -The agent image also installs `sudo` with passwordless access for the `agent` user, so a session can install system packages itself. The Compose server image is unchanged here: it runs with `no-new-privileges` and `cap_drop: ALL`, where `sudo` cannot work. From 8cef31086b487701686edbc1e5d505fe0558e726 Mon Sep 17 00:00:00 2001 From: Devvyn <22340871+opticon454@users.noreply.github.com> Date: Fri, 25 Sep 2026 08:56:20 +0800 Subject: [PATCH 8/9] fix(docker): persist CLIs installed from Settings across container updates The image sets NPM_CONFIG_PREFIX=/opt/codeman-cli, which is image content, so Update-Codeman.sh discarded every npm-installed CLI (dsh, pi). In the Compose container, POST /api/clis/:id/install now installs into ~/.local on the persistent home mount, and ~/.local/bin is appended to the image PATH. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01GuHtuPiHXdykq9T6rKQJ9n --- .changeset/cli9a1d.md | 5 +++++ docker/server.Dockerfile | 3 +++ src/web/routes/cli-registry-routes.ts | 7 +++++++ test/routes/cli-registry-routes.test.ts | 6 ++++++ 4 files changed, 21 insertions(+) create mode 100644 .changeset/cli9a1d.md diff --git a/.changeset/cli9a1d.md b/.changeset/cli9a1d.md new file mode 100644 index 00000000..0ecbf8df --- /dev/null +++ b/.changeset/cli9a1d.md @@ -0,0 +1,5 @@ +--- +"aicodeman": patch +--- + +Docker Compose: CLIs installed from Settings (DeepSeek, Pi and any other npm-based CLI) survive `Update-Codeman.sh`. The image's `NPM_CONFIG_PREFIX` (`/opt/codeman-cli`) is image content and was discarded when the container was recreated; `POST /api/clis/:id/install` now installs into `~/.local` on the persistent home mount when running in the container, and `~/.local/bin` is on the image PATH. diff --git a/docker/server.Dockerfile b/docker/server.Dockerfile index 99142abc..305fc392 100644 --- a/docker/server.Dockerfile +++ b/docker/server.Dockerfile @@ -219,6 +219,9 @@ RUN set -eux; \ COPY --from=ghcr.io/astral-sh/uv:0.9 /uv /uvx /usr/local/bin/ ENV NPM_CONFIG_PREFIX=/opt/codeman-cli ENV PATH=$PATH:/opt/codeman-cli/bin +# CLIs installed at runtime (Settings -> CLIs, npm redirected to ~/.local by installEnv()) live on the +# persistent home mount, so they survive a container recreate. Appended for the same reason as above. +ENV PATH=$PATH:/home/${CODEMAN_RUNTIME_USER}/.local/bin # pnpm is not an agent CLI: it is here because `dsh plugin` (DeepSeek Harness, which # this image leaves to be installed at runtime, see SERVER_INTENTIONAL_OMISSIONS in # test/docker-agent-image-coverage.test.ts) spawns a literal `pnpm` with no npm diff --git a/src/web/routes/cli-registry-routes.ts b/src/web/routes/cli-registry-routes.ts index 5b79b428..62a54e4f 100644 --- a/src/web/routes/cli-registry-routes.ts +++ b/src/web/routes/cli-registry-routes.ts @@ -217,6 +217,13 @@ export function installEnv(source: NodeJS.ProcessEnv = process.env): NodeJS.Proc for (const [key, value] of Object.entries(source)) { if (!key.startsWith('CODEMAN_')) env[key] = value; } + // ⚠️ In the Docker Compose deployment the image sets NPM_CONFIG_PREFIX=/opt/codeman-cli, which is IMAGE + // content: `Update-Codeman.sh` recreates the container and every CLI installed there (dsh, pi, ...) + // vanishes. HOME is the persistent bind mount and `~/.local/bin` is already on every resolver's search + // list, so npm-based installs are redirected there. curl|bash installers already target HOME. + if (source.CODEMAN_IN_CONTAINER === '1' && source.HOME) { + env.NPM_CONFIG_PREFIX = `${source.HOME}/.local`; + } return env; } diff --git a/test/routes/cli-registry-routes.test.ts b/test/routes/cli-registry-routes.test.ts index 34d7cda0..bc33b1f8 100644 --- a/test/routes/cli-registry-routes.test.ts +++ b/test/routes/cli-registry-routes.test.ts @@ -713,6 +713,12 @@ describe('registry writes are serialized and never clobber a file the reader wou } expect(installEnv({ CODEMAN_PASSWORD: 'x', HOME: '/h' })).toEqual({ HOME: '/h' }); }); + + it('redirects npm installs to the persistent HOME inside the Compose container', () => { + expect( + installEnv({ CODEMAN_IN_CONTAINER: '1', HOME: '/home/codeman', NPM_CONFIG_PREFIX: '/opt/codeman-cli' }) + ).toEqual({ HOME: '/home/codeman', NPM_CONFIG_PREFIX: '/home/codeman/.local' }); + }); }); /** From 95a3b87062a29f7ff57fc7372b94b0bba80c69cd Mon Sep 17 00:00:00 2001 From: Devvyn <22340871+opticon454@users.noreply.github.com> Date: Fri, 25 Sep 2026 11:14:47 +0800 Subject: [PATCH 9/9] chore: drop changesets already released in 1.33.1 The pnpm and uv/uvx changesets describe work upstream shipped in 1.33.1 (#485, #487), so keeping them would repeat those notes in the next release. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01GuHtuPiHXdykq9T6rKQJ9n --- .changeset/serverpnpm3.md | 5 ----- .changeset/uvxdocker1.md | 7 ------- 2 files changed, 12 deletions(-) delete mode 100644 .changeset/serverpnpm3.md delete mode 100644 .changeset/uvxdocker1.md diff --git a/.changeset/serverpnpm3.md b/.changeset/serverpnpm3.md deleted file mode 100644 index 86fe7bb7..00000000 --- a/.changeset/serverpnpm3.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"aicodeman": patch ---- - -Install pnpm in the Docker Compose server image. `dsh plugin` spawns a literal `pnpm` with no npm fallback, so the Run menu's "DeepSeek - add a terminal profile" button failed with `dsh: pnpm not found on PATH` in that image. Because this changes `server.Dockerfile`, the in-app updater will ask Compose deployments to rebuild the image (`Update-Codeman.sh`) rather than apply this release in place. diff --git a/.changeset/uvxdocker1.md b/.changeset/uvxdocker1.md deleted file mode 100644 index b4c74474..00000000 --- a/.changeset/uvxdocker1.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"aicodeman": patch ---- - -Install `uv` and `uvx` in the Compose server image and the agent image, so MCP servers launched with `uvx` (such as the Nginx Proxy Manager MCP) can be enabled by Codex instead of failing with `uvx` not found. The server image also carries `pnpm` for `dsh plugin`. - -Both images also install `libsecret-1-0`, the native library the `keytar` dependency of the Azure DevOps MCP (`@azure-devops/mcp`) needs; without it the server crashes before answering the MCP initialize handshake.