From a1981d2ef6c8d5f9c310924dba34ae47b811b0b2 Mon Sep 17 00:00:00 2001 From: zengsipei Date: Tue, 7 Jul 2026 14:52:48 +0800 Subject: [PATCH 1/4] Slim Prebuilt Image to agent-first baseline (#8) Per ADR 0002: the image bakes only what both the AI Agent Toolchain and a debugging Human Supervisor need. - Drop tmux, bat, eza, xclip from apt; drop htop/tree from our layer (base-image copies are left alone -- purging upstream content costs more maintenance than the ~20MB it saves) - Drop zsh plugins, zshrc-addon, tmux.conf + TPM; remove configs/ - common-utils: installZsh/installOhMyZsh -> false, no configureZshAsDefaultShell; login shell is bash (base default) - Symlink fdfind -> fd so agents and humans call it directly - dev-cache mappings: remove zsh/tmux volumes, keep package caches - Template terminal profile zsh -> bash (would break with no zsh config) - devimage-build/README rewritten to describe the slimmed reality --- .devcontainer/devcontainer.json | 2 +- devimage-build/.devcontainer/Dockerfile | 30 ++----- .../.devcontainer/configs/tmux.conf | 20 ----- .../.devcontainer/configs/zshrc-addon | 18 ----- .../.devcontainer/devcontainer.json | 5 +- devimage-build/README.md | 79 +++---------------- 6 files changed, 17 insertions(+), 137 deletions(-) delete mode 100644 devimage-build/.devcontainer/configs/tmux.conf delete mode 100644 devimage-build/.devcontainer/configs/zshrc-addon diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 9d079ed..2cb8271 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -28,7 +28,7 @@ "biomejs.biome" ], "settings": { - "terminal.integrated.defaultProfile.linux": "zsh", + "terminal.integrated.defaultProfile.linux": "bash", "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.tabSize": 2, diff --git a/devimage-build/.devcontainer/Dockerfile b/devimage-build/.devcontainer/Dockerfile index 747e43e..e7b2d35 100644 --- a/devimage-build/.devcontainer/Dockerfile +++ b/devimage-build/.devcontainer/Dockerfile @@ -4,17 +4,13 @@ FROM mcr.microsoft.com/devcontainers/base:ubuntu ENV DEBIAN_FRONTEND=noninteractive # 安装系统依赖(Features 未覆盖的工具) +# fd-find 的二进制名为 fdfind;符号链接为 fd,便于 agent 与 Human Supervisor 直接调用 RUN apt-get update && apt-get install -y \ jq \ ripgrep \ fd-find \ - bat \ - eza \ - htop \ - tree \ - tmux \ - xclip \ - && rm -rf /var/lib/apt/lists/* + && rm -rf /var/lib/apt/lists/* \ + && ln -sf "$(command -v fdfind)" /usr/local/bin/fd # 配置 Git(避免 Windows 换行符问题) RUN git config --global core.autocrlf input \ @@ -25,31 +21,15 @@ RUN git config --global core.autocrlf input \ USER vscode WORKDIR /home/vscode -# 创建缓存目录和符号链接 +# 创建缓存目录和符号链接(包管理器缓存持久化到 dev-cache volume) RUN mkdir -p ~/.cache-volumes/npm \ && mkdir -p ~/.cache-volumes/pnpm \ && mkdir -p ~/.cache-volumes/pip \ && mkdir -p ~/.cache-volumes/poetry \ && mkdir -p ~/.cache-volumes/vscode-extensions \ - && mkdir -p ~/.cache-volumes/zsh \ - && mkdir -p ~/.cache-volumes/tmux/plugins \ && mkdir -p ~/.local/share ~/.cache ~/.vscode-server \ && ln -sf ~/.cache-volumes/npm ~/.npm \ && ln -sf ~/.cache-volumes/pnpm ~/.local/share/pnpm \ && ln -sf ~/.cache-volumes/pip ~/.cache/pip \ && ln -sf ~/.cache-volumes/poetry ~/.cache/pypoetry \ - && ln -sf ~/.cache-volumes/vscode-extensions ~/.vscode-server/extensions \ - && ln -sf ~/.cache-volumes/zsh ~/.zsh \ - && ln -sf ~/.cache-volumes/tmux ~/.tmux - -# 配置 tmux(持久化到 dev-cache) -RUN git clone https://github.com/tmux-plugins/tpm ~/.cache-volumes/tmux/plugins/tpm -COPY --chown=vscode:vscode configs/tmux.conf /home/vscode/.cache-volumes/tmux/.tmux.conf -RUN ln -sf ~/.cache-volumes/tmux/.tmux.conf ~/.tmux.conf - -# 配置 zsh(持久化到 dev-cache) -RUN git clone https://github.com/zsh-users/zsh-autosuggestions ~/.cache-volumes/zsh/zsh-autosuggestions \ - && git clone https://github.com/zsh-users/zsh-syntax-highlighting ~/.cache-volumes/zsh/zsh-syntax-highlighting -COPY --chown=vscode:vscode configs/zshrc-addon /tmp/zshrc-addon -RUN cat /tmp/zshrc-addon >> ~/.zshrc \ - && rm /tmp/zshrc-addon + && ln -sf ~/.cache-volumes/vscode-extensions ~/.vscode-server/extensions diff --git a/devimage-build/.devcontainer/configs/tmux.conf b/devimage-build/.devcontainer/configs/tmux.conf deleted file mode 100644 index a3617b9..0000000 --- a/devimage-build/.devcontainer/configs/tmux.conf +++ /dev/null @@ -1,20 +0,0 @@ -# tmux 基础配置 -set -g @plugin 'tmux-plugins/tpm' -set -g @plugin 'tmux-plugins/tmux-sensible' - -# 启用鼠标 -set -g mouse on - -# 修改前缀键为 Ctrl-a -set -g prefix C-a -bind C-a send-prefix - -# 分屏快捷键 -bind | split-window -h -bind - split-window -v - -# 窗口导航 -bind h select-pane -L -bind j select-pane -D -bind k select-pane -U -bind l select-pane -R diff --git a/devimage-build/.devcontainer/configs/zshrc-addon b/devimage-build/.devcontainer/configs/zshrc-addon deleted file mode 100644 index 11df35f..0000000 --- a/devimage-build/.devcontainer/configs/zshrc-addon +++ /dev/null @@ -1,18 +0,0 @@ -# zsh 主题(随机) -ZSH_THEME="random" - -# zsh 插件 -plugins=(git z sudo zsh-autosuggestions zsh-syntax-highlighting) - -# 实用别名 -alias ll="eza -la" -alias bat="batcat" -alias cat="bat" -alias find="fd" -alias grep="rg" - -# tmux 快捷键 -alias ta='tmux attach -t' -alias tn='tmux new -s' -alias tl='tmux ls' -alias tk='tmux kill-session -t' diff --git a/devimage-build/.devcontainer/devcontainer.json b/devimage-build/.devcontainer/devcontainer.json index cc874ce..0ee1427 100644 --- a/devimage-build/.devcontainer/devcontainer.json +++ b/devimage-build/.devcontainer/devcontainer.json @@ -4,9 +4,8 @@ }, "features": { "ghcr.io/devcontainers/features/common-utils:2": { - "installZsh": true, - "configureZshAsDefaultShell": true, - "installOhMyZsh": true, + "installZsh": false, + "installOhMyZsh": false, "upgradePackages": true }, "ghcr.io/devcontainers/features/node:2.0.0": { diff --git a/devimage-build/README.md b/devimage-build/README.md index 831a652..9d2c9db 100644 --- a/devimage-build/README.md +++ b/devimage-build/README.md @@ -9,10 +9,7 @@ devimage-build/ └── .devcontainer/ ├── Dockerfile # 镜像定义 ├── devcontainer.json # Features 配置 - ├── devcontainer-lock.json # Features 锁定文件 - └── configs/ # 配置文件 - ├── tmux.conf # tmux 配置 - └── zshrc-addon # zsh 配置追加内容 + └── devcontainer-lock.json # Features 锁定文件 ``` ## 镜像信息 @@ -33,22 +30,18 @@ devimage-build/ ### 系统工具(通过 Dockerfile) +镜像走 **agent-first** 基线(见 [ADR 0002](../docs/adr/0002-agent-first-baseline.md)):只烘焙 AI Agent Toolchain 与 Human Supervisor debug 双方都需要的工具,本镜像不再安装或配置任何交互式 shell 工效层——`tmux` / `bat` / `eza` / `xclip` 已移除,zsh 插件与 tmux 配置不再烘焙。基础镜像 `devcontainers/base:ubuntu` 自带的 zsh / oh-my-zsh / htop / tree **不主动清除**(逆上游 purge 维护成本高、体积收益小),但没有任何本仓库的层去配置或依赖它们。 + - jq - ripgrep (rg) -- fd-find -- bat -- eza -- htop -- tree -- tmux -- xclip +- fd-find(以 `fd` 暴露:`fdfind` 在 `PATH` 上符号链接为 `fd`) ### 用户配置(通过 Dockerfile) - Git 配置(避免 Windows 换行符问题) -- tmux 配置 + TPM 插件管理器 -- zsh 配置 + 插件(autosuggestions, syntax-highlighting) -- 缓存目录符号链接 +- 包管理器缓存目录符号链接(npm / pnpm / pip / poetry / vscode-extensions) + +> 默认登录 shell 为 `bash`(基础镜像默认,验证:`getent passwd vscode` → `/bin/bash`):`common-utils` Feature 设 `installZsh: false` / `installOhMyZsh: false` 且不 `configureZshAsDefaultShell`。Human Supervisor 进容器即得到可用的 `bash`;模板侧终端 profile(`.devcontainer/devcontainer.json`)同样默认 `bash`。 ## 本地构建 @@ -134,58 +127,6 @@ RUN apt-get update && apt-get install -y \ } ``` -### 修改 tmux 配置 - -编辑 `configs/tmux.conf`: - -```bash -# tmux 基础配置 -set -g @plugin 'tmux-plugins/tpm' -set -g @plugin 'tmux-plugins/tmux-sensible' - -# 启用鼠标 -set -g mouse on - -# 修改前缀键为 Ctrl-a -set -g prefix C-a -bind C-a send-prefix - -# 分屏快捷键 -bind | split-window -h -bind - split-window -v - -# 窗口导航 -bind h select-pane -L -bind j select-pane -D -bind k select-pane -U -bind l select-pane -R -``` - -### 修改 zsh 配置 - -编辑 `configs/zshrc-addon`: - -```bash -# zsh 主题(随机) -ZSH_THEME="random" - -# zsh 插件 -plugins=(git z sudo zsh-autosuggestions zsh-syntax-highlighting) - -# 实用别名 -alias ll="eza -la" -alias bat="batcat" -alias cat="bat" -alias find="fd" -alias grep="rg" - -# tmux 快捷键 -alias ta='tmux attach -t' -alias tn='tmux new -s' -alias tl='tmux ls' -alias tk='tmux kill-session -t' -``` - ## 缓存持久化策略 ### 符号链接映射 @@ -198,16 +139,14 @@ alias tk='tmux kill-session -t' ├── pnpm/ # → ~/.local/share/pnpm ├── pip/ # → ~/.cache/pip ├── poetry/ # → ~/.cache/pypoetry -├── vscode-extensions/ # → ~/.vscode-server/extensions -├── tmux/ # → ~/.tmux -└── zsh/ # → ~/.zsh +└── vscode-extensions/ # → ~/.vscode-server/extensions ``` ### 实现方式 ```dockerfile # 创建缓存目录 -RUN mkdir -p ~/.cache-volumes/{npm,pnpm,pip,poetry,vscode-extensions,tmux/plugins,zsh} +RUN mkdir -p ~/.cache-volumes/{npm,pnpm,pip,poetry,vscode-extensions} # 创建符号链接 RUN ln -sf ~/.cache-volumes/npm ~/.npm \ From 1b9e868c38b58053356f0bcfd185d709ea30e079 Mon Sep 17 00:00:00 2001 From: zengsipei Date: Tue, 7 Jul 2026 16:06:30 +0800 Subject: [PATCH 2/4] Rebase Prebuilt Image onto bare debian:trixie-slim (#8) devcontainers/base:* ships common-utils defaults pre-baked (zsh, oh-my-zsh, etc.), so slimming our own layer was not enough. Switch to a bare distro base and let the common-utils Feature (installZsh: false) supply the devcontainer essentials instead. - Create the vscode user (uid/gid 1000, bash) in the Dockerfile since a bare base has no non-root user; common-utils adopts it and adds passwordless sudo - Move git config --global below USER vscode: it previously ran as root and wrote /root/.gitconfig, never reaching the actual user - add ca-certificates/curl (Feature scripts need them) and git (no longer inherited from the devcontainers base) - htop/tree return via common-utils base packages; counted as Human-Supervisor debug tools (keep side), not leakage Verified locally: all agent tools present (node 24 / python 3.13 / rust / gh / rg 14 / fd / jq), zsh+omz/tmux/bat/eza/xclip absent, bash login shell, sudo works, cache symlinks intact. Size: 2.61GB -> 2.03GB (-22%). --- devimage-build/.devcontainer/Dockerfile | 25 ++++++++++++++----- .../.devcontainer/devcontainer.json | 3 ++- devimage-build/README.md | 14 ++++++----- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/devimage-build/.devcontainer/Dockerfile b/devimage-build/.devcontainer/Dockerfile index e7b2d35..2d38135 100644 --- a/devimage-build/.devcontainer/Dockerfile +++ b/devimage-build/.devcontainer/Dockerfile @@ -1,26 +1,39 @@ -FROM mcr.microsoft.com/devcontainers/base:ubuntu +# 裸 Debian 基线(ADR 0002 agent-first):不用 devcontainers/base:*——那一族预烘焙了 +# common-utils 默认配置,自带 zsh / oh-my-zsh / htop / tree 等交互工效层。 +# devcontainer 必需品(sudo、用户配置等)由 common-utils Feature 在此镜像之上补齐。 +FROM debian:trixie-slim # 避免交互式提示 ENV DEBIAN_FRONTEND=noninteractive -# 安装系统依赖(Features 未覆盖的工具) +# 安装系统依赖:agent 与 Human Supervisor 双方都需要的工具, +# 以及 Features 安装脚本运行所需的基础组件(ca-certificates / curl) # fd-find 的二进制名为 fdfind;符号链接为 fd,便于 agent 与 Human Supervisor 直接调用 RUN apt-get update && apt-get install -y \ + ca-certificates \ + curl \ + git \ jq \ ripgrep \ fd-find \ && rm -rf /var/lib/apt/lists/* \ && ln -sf "$(command -v fdfind)" /usr/local/bin/fd -# 配置 Git(避免 Windows 换行符问题) -RUN git config --global core.autocrlf input \ - && git config --global init.defaultBranch main \ - && git config --global pull.rebase false +# 裸基础镜像没有非 root 用户;先建 vscode(uid/gid 1000), +# common-utils Feature 检测到同名用户已存在时会原样采用并补上 sudo 配置 +RUN groupadd --gid 1000 vscode \ + && useradd --uid 1000 --gid 1000 --create-home --shell /bin/bash vscode # 切换到 vscode 用户 USER vscode WORKDIR /home/vscode +# 配置 Git(避免 Windows 换行符问题);须在 USER vscode 之后执行, +# 否则写入的是 /root/.gitconfig,对实际运行用户无效 +RUN git config --global core.autocrlf input \ + && git config --global init.defaultBranch main \ + && git config --global pull.rebase false + # 创建缓存目录和符号链接(包管理器缓存持久化到 dev-cache volume) RUN mkdir -p ~/.cache-volumes/npm \ && mkdir -p ~/.cache-volumes/pnpm \ diff --git a/devimage-build/.devcontainer/devcontainer.json b/devimage-build/.devcontainer/devcontainer.json index 0ee1427..69655b2 100644 --- a/devimage-build/.devcontainer/devcontainer.json +++ b/devimage-build/.devcontainer/devcontainer.json @@ -6,7 +6,8 @@ "ghcr.io/devcontainers/features/common-utils:2": { "installZsh": false, "installOhMyZsh": false, - "upgradePackages": true + "upgradePackages": true, + "username": "vscode" }, "ghcr.io/devcontainers/features/node:2.0.0": { "version": "lts" diff --git a/devimage-build/README.md b/devimage-build/README.md index 9d2c9db..f905f29 100644 --- a/devimage-build/README.md +++ b/devimage-build/README.md @@ -15,22 +15,22 @@ devimage-build/ ## 镜像信息 - **镜像名称**:`xiao806852034/ai-dev-container:latest` -- **基础镜像**:`mcr.microsoft.com/devcontainers/base:ubuntu` +- **基础镜像**:`debian:trixie-slim` - **支持架构**:`linux/amd64`, `linux/arm64` ## 预装内容 ### Features(通过 devcontainer.json) -- Node.js LTS + pnpm -- Python 3.12 + Poetry +- Node.js LTS + pnpm / yarn +- Python 3.13(os-provided)+ pipx - Rust - GitHub CLI - Docker-in-Docker ### 系统工具(通过 Dockerfile) -镜像走 **agent-first** 基线(见 [ADR 0002](../docs/adr/0002-agent-first-baseline.md)):只烘焙 AI Agent Toolchain 与 Human Supervisor debug 双方都需要的工具,本镜像不再安装或配置任何交互式 shell 工效层——`tmux` / `bat` / `eza` / `xclip` 已移除,zsh 插件与 tmux 配置不再烘焙。基础镜像 `devcontainers/base:ubuntu` 自带的 zsh / oh-my-zsh / htop / tree **不主动清除**(逆上游 purge 维护成本高、体积收益小),但没有任何本仓库的层去配置或依赖它们。 +镜像走 **agent-first** 基线(见 [ADR 0002](../docs/adr/0002-agent-first-baseline.md)):只烘焙 AI Agent Toolchain 与 Human Supervisor debug 双方都需要的工具。基础镜像用**裸 `debian:trixie-slim`** 而非 `devcontainers/base:*`——后者预烘焙了 common-utils 默认配置,自带 zsh / oh-my-zsh 等交互工效层。devcontainer 必需品(非 root 用户、sudo、locale 等)由 Dockerfile 自建 `vscode` 用户 + `common-utils` Feature(`installZsh: false`)补齐;`tmux` / `bat` / `eza` / `xclip`、zsh 插件与 tmux 配置均不安装(`htop` / `tree` 随 common-utils 基础包进入,算 debug 工具的 keep 侧,不视为泄漏)。基线由 CI 把关:`.github/workflows/image-build-check.yml` 在 PR 上构建镜像并断言上述工具的存在/缺席。 - jq - ripgrep (rg) @@ -41,7 +41,7 @@ devimage-build/ - Git 配置(避免 Windows 换行符问题) - 包管理器缓存目录符号链接(npm / pnpm / pip / poetry / vscode-extensions) -> 默认登录 shell 为 `bash`(基础镜像默认,验证:`getent passwd vscode` → `/bin/bash`):`common-utils` Feature 设 `installZsh: false` / `installOhMyZsh: false` 且不 `configureZshAsDefaultShell`。Human Supervisor 进容器即得到可用的 `bash`;模板侧终端 profile(`.devcontainer/devcontainer.json`)同样默认 `bash`。 +> 默认登录 shell 为 `bash`(Dockerfile `useradd --shell /bin/bash` 创建 `vscode` 用户):`common-utils` Feature 设 `installZsh: false` / `installOhMyZsh: false` 且不 `configureZshAsDefaultShell`。Human Supervisor 进容器即得到可用的 `bash`;模板侧终端 profile(`.devcontainer/devcontainer.json`)同样默认 `bash`。 ## 本地构建 @@ -221,9 +221,11 @@ QEMU + Docker Buildx 提供多架构能力,实际构建与推送由 devcontain 修改 `Dockerfile` 第一行: ```dockerfile -FROM mcr.microsoft.com/devcontainers/base:ubuntu-22.04 +FROM debian:trixie-slim ``` +> 注意保持裸发行版镜像:换成 `devcontainers/base:*` 会重新引入预烘焙的 zsh / oh-my-zsh 等工效层,违背 ADR 0002 的 agent-first 基线。 + ### Q: 如何添加新的缓存目录? 1. 在 Dockerfile 中创建目录: From 7bd35ab89b5f5daf8b22ad28f12bda723f4ea070 Mon Sep 17 00:00:00 2001 From: zengsipei Date: Tue, 7 Jul 2026 16:06:31 +0800 Subject: [PATCH 3/4] Add PR-gated image build check (#8) devcontainer build was only exercised at release time, so an image regression surfaced after tagging. Build the image (no push) on every PR touching devimage-build/ and assert the ADR 0002 agent-first baseline inside the built image: required tools present, ergonomics layer absent, bash login shell, passwordless sudo, dev-cache symlinks, git config owned by the vscode user. --- .github/workflows/image-build-check.yml | 62 +++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .github/workflows/image-build-check.yml diff --git a/.github/workflows/image-build-check.yml b/.github/workflows/image-build-check.yml new file mode 100644 index 0000000..59ab4e2 --- /dev/null +++ b/.github/workflows/image-build-check.yml @@ -0,0 +1,62 @@ +# PR validation for the Prebuilt Image definition. NOT part of the release mechanism +# (that is build-image.yml, contract: docs/adr/0001-image-release-contract.md). +# This never pushes: it only proves `devcontainer build` succeeds and the image +# satisfies the agent-first baseline (ADR 0002) before a change can merge. +name: Image Build Check + +on: + pull_request: + paths: + - 'devimage-build/**' + - '.github/workflows/image-build-check.yml' + +jobs: + build-check: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Build image (single-arch, no push) + run: | + npm install -g @devcontainers/cli + devcontainer build \ + --workspace-folder ./devimage-build \ + --image-name image-build-check:pr + + - name: Verify agent-first baseline (ADR 0002) + run: | + docker run --rm --user vscode image-build-check:pr bash -c ' + set -e + + echo "--- 必备工具(agent 与 Human Supervisor 双方所需)---" + for tool in bash git jq rg fd node npm python3 cargo gh; do + command -v "$tool" >/dev/null || { echo "::error::missing tool: $tool"; exit 1; } + echo "ok: $tool" + done + + echo "--- 工效层不得存在(zsh / oh-my-zsh / tmux / 装饰性 CLI)---" + # htop / tree 不在此列:它们由 common-utils Feature 作为基础包安装, + # 且是 Human Supervisor debug 实际会用的工具(ADR 0002 的 keep 侧)。 + for tool in zsh tmux bat batcat eza xclip; do + if command -v "$tool" >/dev/null; then echo "::error::ergonomics layer leaked in: $tool"; exit 1; fi + echo "absent: $tool" + done + [ ! -e "$HOME/.oh-my-zsh" ] || { echo "::error::oh-my-zsh present"; exit 1; } + + echo "--- 默认 shell 与用户 ---" + [ "$(getent passwd vscode | cut -d: -f7)" = "/bin/bash" ] || { echo "::error::vscode login shell is not /bin/bash"; exit 1; } + sudo -n true 2>/dev/null || { echo "::error::vscode has no passwordless sudo"; exit 1; } + + echo "--- dev-cache 符号链接 ---" + for link in "$HOME/.npm" "$HOME/.local/share/pnpm" "$HOME/.cache/pip" "$HOME/.cache/pypoetry" "$HOME/.vscode-server/extensions"; do + [ -L "$link" ] || { echo "::error::missing cache symlink: $link"; exit 1; } + echo "ok: $link -> $(readlink "$link")" + done + + echo "--- git 全局配置写在 vscode 用户下 ---" + [ "$(git config --global core.autocrlf)" = "input" ] || { echo "::error::git core.autocrlf not configured for vscode user"; exit 1; } + + echo "✅ agent-first baseline holds" + ' From f304b6efd6cc6cdc68f0d61c32d51a8813bf0938 Mon Sep 17 00:00:00 2001 From: zengsipei Date: Tue, 7 Jul 2026 16:06:31 +0800 Subject: [PATCH 4/4] Add agent-compose: run coding agents without devcontainer A plain Docker Compose entry point for the same Prebuilt Image, for terminals/servers/CI where the VS Code devcontainer machinery is not available or wanted. - Shares the dev-home / dev-cache named volumes with .devcontainer, so agent login state and package caches work in both worlds - bootstrap.sh mirrors post-create.sh's Startup Install (claude / codex / gemini) minus the HAPI Local Hub, which stays owned by the devcontainer flow - WORKSPACE_DIR env points the container at any host project; docker compose -p gives one agent container per project --- agent-compose/.env.example | 18 ++++++++++++ agent-compose/README.md | 57 ++++++++++++++++++++++++++++++++++++++ agent-compose/bootstrap.sh | 43 ++++++++++++++++++++++++++++ agent-compose/compose.yaml | 49 ++++++++++++++++++++++++++++++++ 4 files changed, 167 insertions(+) create mode 100644 agent-compose/.env.example create mode 100644 agent-compose/README.md create mode 100644 agent-compose/bootstrap.sh create mode 100644 agent-compose/compose.yaml diff --git a/agent-compose/.env.example b/agent-compose/.env.example new file mode 100644 index 0000000..9fe59a0 --- /dev/null +++ b/agent-compose/.env.example @@ -0,0 +1,18 @@ +# 复制为 .env 后按需修改;全部变量都有默认值,不建 .env 也能直接用。 + +# 要让 agent 操作的项目目录(宿主机路径;默认挂本仓库根目录) +# WORKSPACE_DIR=F:/zsp/Projects/my-app + +# 镜像来源(fork 者改成自己的 registry;与 .devcontainer/.env 语义一致) +# DOCKERHUB_USERNAME=xiao806852034 +# IMAGE_NAME=ai-dev-container + +# pin 到具体版本(默认 latest;可复现场景设为 vX.Y.Z) +# IMAGE_TAG=v2.0.0 + +# Agent Home volume(默认与 devcontainer 共享 dev-home,登录一次两边可用) +# WSL_HOME=dev-home + +# 通过环境变量传 API key 时,先在 compose.yaml 里取消 environment 段注释 +# ANTHROPIC_API_KEY= +# OPENAI_API_KEY= diff --git a/agent-compose/README.md b/agent-compose/README.md new file mode 100644 index 0000000..93ffad9 --- /dev/null +++ b/agent-compose/README.md @@ -0,0 +1,57 @@ +# agent-compose —— 不用 devcontainer 跑 coding agent + +用纯 Docker Compose 在 Prebuilt Image 里运行 claude / codex / gemini 这类 coding agent,**不依赖 VS Code devcontainer**。适合: + +- 只想在终端里跑 agent,不开 VS Code +- 在服务器 / CI 等没有 devcontainer 工具链的环境复用同一套镜像 +- 同时给多个项目各起一个 agent 容器 + +复用与 `.devcontainer/` 相同的镜像和同名 volume(`dev-home` / `dev-cache`),因此 **agent 登录态与缓存两边共享**——在 devcontainer 里登录过的 claude,这里直接可用,反之亦然。 + +## 使用 + +```bash +cd agent-compose + +# (可选)配置目标项目目录等 +cp .env.example .env + +docker compose up -d # 启动并完成 Startup Install +docker compose exec agent claude # 或 codex / gemini / bash +docker compose down # 用完关掉 +``` + +首次 `up` 时引导脚本(`bootstrap.sh`)会安装缺失的 agent CLI(Startup Install,镜像刻意不烘焙,见 [ADR 0001](../docs/adr/0001-image-release-contract.md));agent 配置目录(`~/.claude` 等)链接到 `dev-home` volume,重建容器不丢登录态。 + +## 换一个项目 + +编辑 `.env` 的 `WORKSPACE_DIR` 指向目标项目,然后 `docker compose up -d --force-recreate`。 + +要同时跑多个项目,用 `-p` 起独立实例: + +```bash +WORKSPACE_DIR=/path/to/app docker compose -p agent-app up -d +docker compose -p agent-app exec agent claude +``` + +## 更新镜像 + +这里没有 devcontainer 的 `initializeCommand` 自动拉取(那是给 VS Code rebuild 用的,见 [ADR 0001](../docs/adr/0001-image-release-contract.md)),手动拉: + +```bash +docker compose pull && docker compose up -d --force-recreate +``` + +pin 版本:`.env` 里设 `IMAGE_TAG=vX.Y.Z`。 + +## 与 `.devcontainer/` 的关系 + +| | `.devcontainer/` | `agent-compose/` | +|---|---|---| +| 入口 | VS Code Reopen in Container | `docker compose up -d` | +| 镜像 | 同一个 Prebuilt Image | 同一个 Prebuilt Image | +| Agent 登录态 | `dev-home` volume | 同一个 `dev-home` volume | +| 缓存 | `dev-cache` volume | 同一个 `dev-cache` volume | +| Startup Install | `post-create.sh`(含 HAPI Local Hub) | `bootstrap.sh`(仅 agent CLI) | + +> HAPI Local Hub 属于 devcontainer 工作流,本目录不拉起;需要时进容器手动 `npm i -g @twsxtd/hapi` 使用。 diff --git a/agent-compose/bootstrap.sh b/agent-compose/bootstrap.sh new file mode 100644 index 0000000..5bb598e --- /dev/null +++ b/agent-compose/bootstrap.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# ============================================ +# agent-compose 容器引导脚本 +# ============================================ +# 由 compose.yaml 作为容器 command 运行(对应 devcontainer 流程的 post-create.sh): +# 1. 链接 Agent Home(dev-home volume)里的 agent 配置到 ~ +# 2. Startup Install:安装缺失的 coding agent CLI(镜像刻意不烘焙,见 ADR 0001) +# 3. sleep infinity 常驻,等待 `docker compose exec agent ` 进入 +set -e + +AGENT_HOME=/home/vscode/wsl-home + +# ============================================ +# 链接 AI agent 配置(与 devcontainer 共享同一 dev-home volume) +# ============================================ +for dir in .claude .codex .gemini; do + mkdir -p "$AGENT_HOME/$dir" + ln -sfn "$AGENT_HOME/$dir" "$HOME/$dir" + echo "📦 Linked $dir -> Agent Home" +done + +# ============================================ +# 安装 AI Agents(Startup Install,幂等) +# ============================================ +if ! command -v claude &> /dev/null; then + echo "📦 Installing Claude Code..." + npm install -g @anthropic-ai/claude-code +fi + +if ! command -v codex &> /dev/null; then + echo "📦 Installing OpenAI Codex..." + npm install -g @openai/codex +fi + +if ! command -v gemini &> /dev/null; then + echo "📦 Installing Gemini CLI..." + npm install -g @google/gemini-cli +fi + +echo "✅ Agent 环境就绪。进入方式:docker compose exec agent claude(或 codex / gemini / bash)" + +# 常驻(PID 1 由 compose 的 init: true 托管,可正常响应 stop 信号) +exec sleep infinity diff --git a/agent-compose/compose.yaml b/agent-compose/compose.yaml new file mode 100644 index 0000000..605ce14 --- /dev/null +++ b/agent-compose/compose.yaml @@ -0,0 +1,49 @@ +# 纯 Docker Compose 的 coding agent 运行环境 —— 不依赖 VS Code devcontainer。 +# 复用同一个 Prebuilt Image 与同名 volume(dev-home / dev-cache), +# 因此 agent 登录态、包管理器缓存与 devcontainer 完全共享。 +# 发布契约见 docs/adr/0001-image-release-contract.md(默认消费 :latest)。 +services: + agent: + # 与 .devcontainer/compose.yaml 相同的参数化引用;fork 者在本目录 .env 覆盖。 + image: ${DOCKERHUB_USERNAME:-xiao806852034}/${IMAGE_NAME:-ai-dev-container}:${IMAGE_TAG:-latest} + + volumes: + # 要让 agent 操作的项目目录;默认挂本仓库根,改 .env 的 WORKSPACE_DIR 指向任意项目 + - ${WORKSPACE_DIR:-..}:/home/vscode/workspace + + # Agent Home(.claude / .codex / .gemini 登录态与配置),与 devcontainer 共享 + - ${WSL_HOME:-dev-home}:/home/vscode/wsl-home:cached + + # 统一缓存目录(npm / pnpm / pip / poetry),与 devcontainer 共享 + - dev-cache:/home/vscode/.cache-volumes + + # 容器内引导脚本:链接 Agent Home、Startup Install agent CLI,然后常驻 + - ./bootstrap.sh:/usr/local/bin/agent-bootstrap:ro + + working_dir: /home/vscode/workspace + + # 引导完成后 sleep infinity 常驻;用 `docker compose exec agent claude` 进入 + command: bash /usr/local/bin/agent-bootstrap + + init: true + + # 需要通过环境变量传 API key 时在此打开(值来自本目录 .env): + # environment: + # ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-} + # OPENAI_API_KEY: ${OPENAI_API_KEY:-} + + # 资源限制(与 devcontainer 一致) + deploy: + resources: + limits: + memory: 8G + reservations: + memory: 4G + +volumes: + # AI agent 配置存储(与 .devcontainer/compose.yaml 同名 → 同一个 volume) + dev-home: + name: dev-home + # 统一缓存存储 + dev-cache: + name: dev-cache