added
.dockerignore +19 -0 | new file mode 100644 | ||
| @@ -0,0 +1,19 @@ | ||
| 1 | +# Build context of docker-sbx/template/Dockerfile (run from the repository | |
| 2 | +# root). Everything `go build .` does not need stays out, so the context is a | |
| 3 | +# few megabytes rather than the sandboxes/ checkout and the release binaries. | |
| 4 | +.git | |
| 5 | +sandboxes | |
| 6 | +release | |
| 7 | +tmp | |
| 8 | +kits | |
| 9 | +ebook | |
| 10 | +blog | |
| 11 | +mm | |
| 12 | +.mm | |
| 13 | +**/.mm | |
| 14 | +.memory | |
| 15 | +.quality | |
| 16 | +.qlty | |
| 17 | +notes.txt | |
| 18 | +*.env | |
| 19 | +*.tar | |
| new file mode 100644 | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | +# Build context of docker-sbx/template/Dockerfile (run from the repository | ||
| 2 | +# root). Everything `go build .` does not need stays out, so the context is a | ||
| 3 | +# few megabytes rather than the sandboxes/ checkout and the release binaries. | ||
| 4 | +.git | ||
| 5 | +sandboxes | ||
| 6 | +release | ||
| 7 | +tmp | ||
| 8 | +kits | ||
| 9 | +ebook | ||
| 10 | +blog | ||
| 11 | +mm | ||
| 12 | +.mm | ||
| 13 | +**/.mm | ||
| 14 | +.memory | ||
| 15 | +.quality | ||
| 16 | +.qlty | ||
| 17 | +notes.txt | ||
| 18 | +*.env | ||
| 19 | +*.tar | ||
modified
.github/workflows/release.yml +64 -0 | @@ -97,3 +97,67 @@ jobs: | ||
| 97 | 97 | release/${{ github.ref_name }}/SHA256SUMS |
| 98 | 98 | release/${{ github.ref_name }}/README.md |
| 99 | 99 | fail_on_unmatched_files: true |
| 100 | + | |
| 101 | + # The Docker Sandbox template and its two kits, on Docker Hub, at the same | |
| 102 | + # version as the binaries: k33g/mm-sandbox:0.0.2 (the image, linux/amd64 + | |
| 103 | + # linux/arm64), k33g/mm-tui:0.0.2 and k33g/mm-acp:0.0.2 (the kits, OCI | |
| 104 | + # artifacts). The scripts are the ones a laptop runs — docker-sbx/build.sh | |
| 105 | + # with PUSH=1, then docker-sbx/push-kits.sh — so a local publish and a CI | |
| 106 | + # publish are the same pipeline. Runs after `release` so nothing is pushed | |
| 107 | + # to the Hub when the tests failed. | |
| 108 | + # | |
| 109 | + # Credentials: two repository secrets, DOCKER_HANDLE (the Docker Hub | |
| 110 | + # account, which is also the image owner) and DOCKER_PAT (an access token | |
| 111 | + # with read/write scope), exposed to the job as environment variables. | |
| 112 | + docker-sandbox: | |
| 113 | + name: publish the Docker Sandbox template and kits ${{ github.ref_name }} | |
| 114 | + runs-on: ubuntu-latest | |
| 115 | + needs: release | |
| 116 | + env: | |
| 117 | + DOCKER_HANDLE: ${{ secrets.DOCKER_HANDLE }} | |
| 118 | + DOCKER_PAT: ${{ secrets.DOCKER_PAT }} | |
| 119 | + # The sbx release whose CLI publishes the kits (docker/sbx-releases). | |
| 120 | + SBX_VERSION: v0.45.0 | |
| 121 | + steps: | |
| 122 | + - name: Checkout | |
| 123 | + uses: actions/checkout@v4 | |
| 124 | + | |
| 125 | + - name: Check the Docker Hub credentials are set | |
| 126 | + run: | | |
| 127 | + set -euo pipefail | |
| 128 | + if [ -z "${DOCKER_HANDLE}" ] || [ -z "${DOCKER_PAT}" ]; then | |
| 129 | + echo "❌ DOCKER_HANDLE and DOCKER_PAT must be set as repository secrets" | |
| 130 | + exit 1 | |
| 131 | + fi | |
| 132 | + | |
| 133 | + - name: Set up QEMU | |
| 134 | + # The final image stage runs `mm --version` for linux/arm64 on an | |
| 135 | + # amd64 runner; the Go stage itself cross-compiles without emulation. | |
| 136 | + uses: docker/setup-qemu-action@v3 | |
| 137 | + | |
| 138 | + - name: Set up Docker Buildx | |
| 139 | + uses: docker/setup-buildx-action@v3 | |
| 140 | + | |
| 141 | + - name: Log in to Docker Hub | |
| 142 | + run: printf '%s' "${DOCKER_PAT}" | docker login --username "${DOCKER_HANDLE}" --password-stdin | |
| 143 | + | |
| 144 | + - name: Build and push the template image | |
| 145 | + run: PUSH=1 IMAGE_OWNER="${DOCKER_HANDLE}" bash ./docker-sbx/build.sh "${GITHUB_REF_NAME}" | |
| 146 | + | |
| 147 | + - name: Install the sbx CLI | |
| 148 | + # `sbx kit push` talks to the registry directly and reads the Docker | |
| 149 | + # credential store; no sandbox daemon is started here. | |
| 150 | + run: | | |
| 151 | + set -euo pipefail | |
| 152 | + mkdir -p "${RUNNER_TEMP}/sbx" | |
| 153 | + curl -fsSL -o "${RUNNER_TEMP}/sbx.tgz" \ | |
| 154 | + "https://github.com/docker/sbx-releases/releases/download/${SBX_VERSION}/DockerSandboxes-linux-amd64.tar.gz" | |
| 155 | + tar -xzf "${RUNNER_TEMP}/sbx.tgz" -C "${RUNNER_TEMP}/sbx" --strip-components=1 docker-sbx/sbx | |
| 156 | + "${RUNNER_TEMP}/sbx/sbx" version | |
| 157 | + | |
| 158 | + - name: Publish the kits | |
| 159 | + run: IMAGE_OWNER="${DOCKER_HANDLE}" SBX="${RUNNER_TEMP}/sbx/sbx" bash ./docker-sbx/push-kits.sh "${GITHUB_REF_NAME}" | |
| 160 | + | |
| 161 | + - name: Log out of Docker Hub | |
| 162 | + if: always() | |
| 163 | + run: docker logout || true | |
| @@ -97,3 +97,67 @@ jobs: | |||
| 97 | release/${{ github.ref_name }}/SHA256SUMS | 97 | release/${{ github.ref_name }}/SHA256SUMS |
| 98 | release/${{ github.ref_name }}/README.md | 98 | release/${{ github.ref_name }}/README.md |
| 99 | fail_on_unmatched_files: true | 99 | fail_on_unmatched_files: true |
| 100 | + | ||
| 101 | + # The Docker Sandbox template and its two kits, on Docker Hub, at the same | ||
| 102 | + # version as the binaries: k33g/mm-sandbox:0.0.2 (the image, linux/amd64 + | ||
| 103 | + # linux/arm64), k33g/mm-tui:0.0.2 and k33g/mm-acp:0.0.2 (the kits, OCI | ||
| 104 | + # artifacts). The scripts are the ones a laptop runs — docker-sbx/build.sh | ||
| 105 | + # with PUSH=1, then docker-sbx/push-kits.sh — so a local publish and a CI | ||
| 106 | + # publish are the same pipeline. Runs after `release` so nothing is pushed | ||
| 107 | + # to the Hub when the tests failed. | ||
| 108 | + # | ||
| 109 | + # Credentials: two repository secrets, DOCKER_HANDLE (the Docker Hub | ||
| 110 | + # account, which is also the image owner) and DOCKER_PAT (an access token | ||
| 111 | + # with read/write scope), exposed to the job as environment variables. | ||
| 112 | + docker-sandbox: | ||
| 113 | + name: publish the Docker Sandbox template and kits ${{ github.ref_name }} | ||
| 114 | + runs-on: ubuntu-latest | ||
| 115 | + needs: release | ||
| 116 | + env: | ||
| 117 | + DOCKER_HANDLE: ${{ secrets.DOCKER_HANDLE }} | ||
| 118 | + DOCKER_PAT: ${{ secrets.DOCKER_PAT }} | ||
| 119 | + # The sbx release whose CLI publishes the kits (docker/sbx-releases). | ||
| 120 | + SBX_VERSION: v0.45.0 | ||
| 121 | + steps: | ||
| 122 | + - name: Checkout | ||
| 123 | + uses: actions/checkout@v4 | ||
| 124 | + | ||
| 125 | + - name: Check the Docker Hub credentials are set | ||
| 126 | + run: | | ||
| 127 | + set -euo pipefail | ||
| 128 | + if [ -z "${DOCKER_HANDLE}" ] || [ -z "${DOCKER_PAT}" ]; then | ||
| 129 | + echo "❌ DOCKER_HANDLE and DOCKER_PAT must be set as repository secrets" | ||
| 130 | + exit 1 | ||
| 131 | + fi | ||
| 132 | + | ||
| 133 | + - name: Set up QEMU | ||
| 134 | + # The final image stage runs `mm --version` for linux/arm64 on an | ||
| 135 | + # amd64 runner; the Go stage itself cross-compiles without emulation. | ||
| 136 | + uses: docker/setup-qemu-action@v3 | ||
| 137 | + | ||
| 138 | + - name: Set up Docker Buildx | ||
| 139 | + uses: docker/setup-buildx-action@v3 | ||
| 140 | + | ||
| 141 | + - name: Log in to Docker Hub | ||
| 142 | + run: printf '%s' "${DOCKER_PAT}" | docker login --username "${DOCKER_HANDLE}" --password-stdin | ||
| 143 | + | ||
| 144 | + - name: Build and push the template image | ||
| 145 | + run: PUSH=1 IMAGE_OWNER="${DOCKER_HANDLE}" bash ./docker-sbx/build.sh "${GITHUB_REF_NAME}" | ||
| 146 | + | ||
| 147 | + - name: Install the sbx CLI | ||
| 148 | + # `sbx kit push` talks to the registry directly and reads the Docker | ||
| 149 | + # credential store; no sandbox daemon is started here. | ||
| 150 | + run: | | ||
| 151 | + set -euo pipefail | ||
| 152 | + mkdir -p "${RUNNER_TEMP}/sbx" | ||
| 153 | + curl -fsSL -o "${RUNNER_TEMP}/sbx.tgz" \ | ||
| 154 | + "https://github.com/docker/sbx-releases/releases/download/${SBX_VERSION}/DockerSandboxes-linux-amd64.tar.gz" | ||
| 155 | + tar -xzf "${RUNNER_TEMP}/sbx.tgz" -C "${RUNNER_TEMP}/sbx" --strip-components=1 docker-sbx/sbx | ||
| 156 | + "${RUNNER_TEMP}/sbx/sbx" version | ||
| 157 | + | ||
| 158 | + - name: Publish the kits | ||
| 159 | + run: IMAGE_OWNER="${DOCKER_HANDLE}" SBX="${RUNNER_TEMP}/sbx/sbx" bash ./docker-sbx/push-kits.sh "${GITHUB_REF_NAME}" | ||
| 160 | + | ||
| 161 | + - name: Log out of Docker Hub | ||
| 162 | + if: always() | ||
| 163 | + run: docker logout || true | ||
added
docker-sbx/README.md +197 -0 | new file mode 100644 | ||
| @@ -0,0 +1,197 @@ | ||
| 1 | +# mm in a Docker Sandbox | |
| 2 | + | |
| 3 | +Everything needed to run `mm` inside a [Docker Sandbox](https://docs.docker.com/ai/sandboxes/): a **template** (the `shell` sandbox image with `mm` installed) and two **kits** that start it — one for the window mode in your terminal, one for an editor that speaks the Agent Client Protocol. | |
| 4 | + | |
| 5 | +The full guide, with the reasoning, is in [`docs/en/how-to/run-in-a-docker-sandbox.md`](../docs/en/how-to/run-in-a-docker-sandbox.md) ([français](../docs/fr/how-to/run-in-a-docker-sandbox.md)). This page is the short version. | |
| 6 | + | |
| 7 | +``` | |
| 8 | +docker-sbx/ | |
| 9 | +├── build.sh builds the template image, k33g/mm-sandbox:<version> (PUSH=1: multi-arch, pushed) | |
| 10 | +├── sync-kits.sh points the kits at one image: owner + version | |
| 11 | +├── push-kits.sh publishes the kits to Docker Hub as OCI artifacts (sbx kit push) | |
| 12 | +├── template/ | |
| 13 | +│ ├── Dockerfile docker/sandbox-templates:shell + mm (built from this checkout) | |
| 14 | +│ ├── mm-tui entry point of the mm-tui kit: `mm -tui "$@"` | |
| 15 | +│ └── mm-acp-info entry point of the mm-acp kit: prints the editor wiring, opens a shell | |
| 16 | +└── kits/ | |
| 17 | + ├── mm-tui/spec.yaml kind: sandbox — `sbx run` opens mm's window on the workspace | |
| 18 | + └── mm-acp/spec.yaml kind: sandbox — `sbx create` once, the editor runs `sbx exec -i … mm -acp` | |
| 19 | +``` | |
| 20 | + | |
| 21 | +## 0. Use the published kits (no build) | |
| 22 | + | |
| 23 | +Every release publishes the image and the two kits to Docker Hub at mm's version: | |
| 24 | + | |
| 25 | +| On Docker Hub | What it is | | |
| 26 | +|---|---| | |
| 27 | +| `k33g/mm-sandbox:0.0.2` | The template image. You never name it yourself: each kit pulls it. | | |
| 28 | +| `k33g/mm-tui:0.0.2` | The window-mode kit, for `sbx run`. | | |
| 29 | +| `k33g/mm-acp:0.0.2` | The editor kit, for `sbx create` + `sbx exec -i … mm -acp`. | | |
| 30 | + | |
| 31 | +Nothing to build, no `--pull never`, and no setting to change: Docker Hub is an allowed kit source out of the box (`sbx settings get kit.allowedSources` shows `docker.io/`), and a reference with a `/` but no registry host is read as `docker.io/…`. Only Docker Desktop with the `sbx` command, and a model server on your machine, are needed. | |
| 32 | + | |
| 33 | +**Look before you run.** `sbx kit inspect` reads the kit from the Hub without creating anything: its image, its entry point, its arguments and their defaults, the ports it allows. | |
| 34 | + | |
| 35 | +```bash | |
| 36 | +sbx kit inspect k33g/mm-tui:0.0.2 | |
| 37 | +``` | |
| 38 | + | |
| 39 | +**Window mode.** From the project you want mm to work on: | |
| 40 | + | |
| 41 | +```bash | |
| 42 | +cd /path/to/your/project | |
| 43 | +sbx run k33g/mm-tui:0.0.2 . -- agent.dmr.yaml | |
| 44 | +``` | |
| 45 | + | |
| 46 | +The first run pulls the kit and the image, creates the sandbox `mm-tui-<directory>`, mounts the current directory as its workspace and opens `mm -tui agent.dmr.yaml` in it. What follows `--` is mm's own command line, so `-- -config other.yaml` works too. Without anything after `--`, mm reads the kit's `config` argument, then `./agent.yaml` in the workspace: | |
| 47 | + | |
| 48 | +```bash | |
| 49 | +sbx run k33g/mm-tui:0.0.2 . --kit-arg config=agent.dmr.yaml | |
| 50 | +``` | |
| 51 | + | |
| 52 | +Later, `sbx run --name mm-tui-<directory>` re-attaches to that sandbox; `sbx ls` lists them. | |
| 53 | + | |
| 54 | +**From an editor.** Create the sandbox once, from the project the editor will open, with the configuration file baked in: | |
| 55 | + | |
| 56 | +```bash | |
| 57 | +cd /path/to/your/project | |
| 58 | +sbx create --name mm-acp k33g/mm-acp:0.0.2 . --kit-arg config=agent.dmr.yaml | |
| 59 | +``` | |
| 60 | + | |
| 61 | +then register it in the editor — in Zed's `settings.json`: | |
| 62 | + | |
| 63 | +```json | |
| 64 | +{ | |
| 65 | + "agent_servers": { | |
| 66 | + "mini-me (sandbox)": { | |
| 67 | + "type": "custom", | |
| 68 | + "command": "sbx", | |
| 69 | + "args": ["exec", "-i", "mm-acp", "mm", "-acp"] | |
| 70 | + } | |
| 71 | + } | |
| 72 | +} | |
| 73 | +``` | |
| 74 | + | |
| 75 | +The editor starts `mm -acp` inside the sandbox for every conversation; `sbx exec` starts the sandbox first when it is stopped, and runs mm in the workspace with the baked-in `AGENT_CONFIG`. One sandbox per project, each with its own `--name` and its own editor entry. Section 3 has the details (another config for one session, where the logs are, why `sbx exec -i` and not `sbx run`). | |
| 76 | + | |
| 77 | +**Kit arguments.** Both kits take the same two, passed with `--kit-arg name=value` (repeatable) to `sbx run` or `sbx create`: | |
| 78 | + | |
| 79 | +| `--kit-arg` | Default | Effect | | |
| 80 | +|---|---|---| | |
| 81 | +| `config=<path>` | empty | The file mm reads (its `AGENT_CONFIG`): relative to the workspace, or absolute inside the sandbox. Empty: `./agent.yaml`, then the built-in defaults. | | |
| 82 | +| `tag=<version>` | the kit's own version (`0.0.2`) | Which `k33g/mm-sandbox:<version>` image to use. Normally left alone: a kit and its image share a version. | | |
| 83 | + | |
| 84 | +**Other versions.** Replace `0.0.2` with the release you want, in the kit reference only — the kit brings the matching image. A kit can also be pinned by digest, `k33g/mm-tui@sha256:…`, the digest being what `sbx kit inspect` or the Hub page shows. | |
| 85 | + | |
| 86 | +**If sbx refuses the source.** A message like `its source is not in your allowlist` means `kit.allowedSources` was narrowed on this machine; `sbx settings set kit.allowedSources '["docker.io/"]'` restores the default, or add `docker.io/k33g/` to your list. | |
| 87 | + | |
| 88 | +Sections 1–3 are the same flow with a template and kits built locally, for a work-in-progress mm. | |
| 89 | + | |
| 90 | +## 1. Build the template | |
| 91 | + | |
| 92 | +From the repository root: | |
| 93 | + | |
| 94 | +```bash | |
| 95 | +./docker-sbx/build.sh # TAG from release.env: v0.0.2 → k33g/mm-sandbox:0.0.2 | |
| 96 | +./docker-sbx/build.sh v0.0.3 # → k33g/mm-sandbox:0.0.3 | |
| 97 | +``` | |
| 98 | + | |
| 99 | +The image tag is mm's tag without the `v`, the same rule as the release assets, and the script rewrites the kits' default `tag` argument to the version it built, so the kits always name the image just built. The image lands in the local Docker image store: every `sbx run` / `sbx create` below carries `--pull never` until you `docker push k33g/mm-sandbox:0.0.2`. If `sbx` runs on another machine, `EXPORT=1 ./docker-sbx/build.sh` also writes `tmp/mm-sandbox-<version>.tar`; load it there with `sbx template load`. | |
| 100 | + | |
| 101 | +## 2. Window mode in the terminal — kit `mm-tui` | |
| 102 | + | |
| 103 | +From the project you want `mm` to work on: | |
| 104 | + | |
| 105 | +```bash | |
| 106 | +sbx run --pull never ./docker-sbx/kits/mm-tui . -- agent.dmr.yaml | |
| 107 | +``` | |
| 108 | + | |
| 109 | +What follows `--` reaches `mm` as its own arguments, so this runs `mm -tui agent.dmr.yaml` in the workspace. Any `mm` flag passes through the same way (`-- -config other.yaml`). Two kit arguments exist as well: | |
| 110 | + | |
| 111 | +| `--kit-arg` | Default | Effect | | |
| 112 | +|---|---|---| | |
| 113 | +| `config=<path>` | empty | The file `mm` reads when nothing follows `--` (its `AGENT_CONFIG`). Relative to the workspace, or absolute. Empty: `./agent.yaml`, then the built-in defaults. | | |
| 114 | +| `tag=<version>` | the version last built (`0.0.2`) | Which `k33g/mm-sandbox:<version>` image to use. | | |
| 115 | + | |
| 116 | +`sbx run --name <name>` re-attaches to a sandbox that already exists. Sessions, when enabled in the YAML, are written under the workspace's `.mm/sessions`, which is your host directory. | |
| 117 | + | |
| 118 | +## 3. From an editor (ACP) — kit `mm-acp` | |
| 119 | + | |
| 120 | +Create the sandbox once, without attaching, **from the project the editor will open**. `.` is the workspace; sbx makes it absolute. A path that does not exist on your machine makes sbx ask "The selected workspace does not exist. Would you like to create it?" — answer `N` and fix the path. The kit path must be explicit: `./`, `../` or absolute. | |
| 121 | + | |
| 122 | +```bash | |
| 123 | +cd /path/to/your/project | |
| 124 | +sbx create --pull never --name mm-acp ./docker-sbx/kits/mm-acp . \ | |
| 125 | + --kit-arg config=agent.dmr.yaml | |
| 126 | + | |
| 127 | +# once the image is pushed (docker push k33g/mm-sandbox:0.0.2), --pull never is not needed: | |
| 128 | +sbx create --name mm-acp ./docker-sbx/kits/mm-acp . \ | |
| 129 | + --kit-arg config=agent.dmr.yaml | |
| 130 | +``` | |
| 131 | + | |
| 132 | +Then register it in the editor. In Zed's `settings.json`: | |
| 133 | + | |
| 134 | +```json | |
| 135 | +{ | |
| 136 | + "agent_servers": { | |
| 137 | + "📦 mini-me (dmr sbx)": { | |
| 138 | + "type": "custom", | |
| 139 | + "command": "sbx", | |
| 140 | + "args": ["exec", "-i", "mm-acp", "mm", "-acp"], | |
| 141 | + }, | |
| 142 | + } | |
| 143 | +} | |
| 144 | +``` | |
| 145 | + | |
| 146 | +Or: | |
| 147 | + | |
| 148 | +```bash | |
| 149 | +sbx create --pull never --name mm-acp-2 ./docker-sbx/kits/mm-acp . | |
| 150 | +``` | |
| 151 | + | |
| 152 | +And: | |
| 153 | + | |
| 154 | +```json | |
| 155 | +{ | |
| 156 | + "agent_servers": { | |
| 157 | + "😄 mini-me (dmr sbx)": { | |
| 158 | + "type": "custom", | |
| 159 | + "command": "sbx", | |
| 160 | + "args": ["exec", "-i", "mm-acp-2", "mm", "-acp"], | |
| 161 | + "env": { "AGENT_CONFIG": "/Users/k33g/kDrive/Rickub/bots-garden/mini-me/demo/agent.dmr.yaml" } | |
| 162 | + }, | |
| 163 | + } | |
| 164 | +} | |
| 165 | +``` | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | +The editor starts `mm -acp` inside the sandbox for every conversation; `sbx exec` starts the sandbox first when it is stopped, and runs `mm` in the workspace with the `AGENT_CONFIG` baked in by `--kit-arg config`. Everything the model does — commands, file edits — happens inside the sandbox, on the mounted project. | |
| 170 | + | |
| 171 | +Why `sbx exec -i` and not `sbx run`: an ACP client speaks JSON-RPC on the agent's raw stdin/stdout, and `sbx run` always allocates a terminal. Attaching to this sandbox with `sbx run --name mm-acp` is still useful: its entry point prints the wiring above with the sandbox's real name, then opens a shell. | |
| 172 | + | |
| 173 | +- One sandbox per project: the workspace is fixed at creation. Give each one its own `--name` and its own entry in the editor. | |
| 174 | +- Another configuration file for one session: `"args": ["exec", "-i", "-e", "AGENT_CONFIG=agent.lsp.yaml", "mm-acp", "mm", "-acp"]`. | |
| 175 | +- `sbx` not found by the editor: use the absolute path printed by `which sbx` as `"command"`. | |
| 176 | +- Logs: in Zed, `dev: open acp logs` shows `mm`'s banner, warnings and `[acp] …` trail. | |
| 177 | + | |
| 178 | +## 4. Publishing a version | |
| 179 | + | |
| 180 | +The Release workflow (`.github/workflows/release.yml`, job `docker-sandbox`) does this on every `v*` tag, after the binaries: it reads two repository secrets, `DOCKER_HANDLE` (the Docker Hub account, also the image owner) and `DOCKER_PAT` (an access token), logs in, then runs the same two scripts you can run from a laptop: | |
| 181 | + | |
| 182 | +```bash | |
| 183 | +docker login -u k33g # with an access token | |
| 184 | +PUSH=1 ./docker-sbx/build.sh v0.0.2 # buildx, linux/amd64 + linux/arm64, pushed as k33g/mm-sandbox:0.0.2 | |
| 185 | +./docker-sbx/push-kits.sh v0.0.2 # sbx kit validate + push → k33g/mm-tui:0.0.2, k33g/mm-acp:0.0.2 | |
| 186 | +``` | |
| 187 | + | |
| 188 | +`push-kits.sh` needs the `sbx` CLI (the workflow downloads the Linux tarball from `docker/sbx-releases`); `sbx kit push` talks to the registry directly and authenticates from the Docker credential store, no sandbox daemon involved. Both scripts call `sync-kits.sh` first, so the published kits always name the image just pushed. `IMAGE_OWNER=<handle>` publishes under another account. | |
| 189 | + | |
| 190 | +## The model server stays on the host | |
| 191 | + | |
| 192 | +Inside a sandbox `localhost` is the sandbox itself. Docker Model Runner and llama-server keep running on your machine and are reached through `host.docker.internal`, which the sandbox proxy maps to your host's `localhost`: | |
| 193 | + | |
| 194 | +- `agent.dmr.yaml` already has `fallback: http://host.docker.internal:12434/engines/v1` — nothing to change. | |
| 195 | +- `agent.llamacpp.yaml` has no fallback: pass `--env AGENT_BASE_URL=http://host.docker.internal:8080/v1` to `sbx run` / `sbx create`, or edit its `baseUrl`. | |
| 196 | + | |
| 197 | +Both kits allow ports 12434 (Docker Model Runner) and 8080 (llama-server) on the host. Another port: `sbx policy allow network host.docker.internal:<port>`. | |
| new file mode 100644 | |||
| @@ -0,0 +1,197 @@ | |||
| 1 | +# mm in a Docker Sandbox | ||
| 2 | + | ||
| 3 | +Everything needed to run `mm` inside a [Docker Sandbox](https://docs.docker.com/ai/sandboxes/): a **template** (the `shell` sandbox image with `mm` installed) and two **kits** that start it — one for the window mode in your terminal, one for an editor that speaks the Agent Client Protocol. | ||
| 4 | + | ||
| 5 | +The full guide, with the reasoning, is in [`docs/en/how-to/run-in-a-docker-sandbox.md`](../docs/en/how-to/run-in-a-docker-sandbox.md) ([français](../docs/fr/how-to/run-in-a-docker-sandbox.md)). This page is the short version. | ||
| 6 | + | ||
| 7 | +``` | ||
| 8 | +docker-sbx/ | ||
| 9 | +├── build.sh builds the template image, k33g/mm-sandbox:<version> (PUSH=1: multi-arch, pushed) | ||
| 10 | +├── sync-kits.sh points the kits at one image: owner + version | ||
| 11 | +├── push-kits.sh publishes the kits to Docker Hub as OCI artifacts (sbx kit push) | ||
| 12 | +├── template/ | ||
| 13 | +│ ├── Dockerfile docker/sandbox-templates:shell + mm (built from this checkout) | ||
| 14 | +│ ├── mm-tui entry point of the mm-tui kit: `mm -tui "$@"` | ||
| 15 | +│ └── mm-acp-info entry point of the mm-acp kit: prints the editor wiring, opens a shell | ||
| 16 | +└── kits/ | ||
| 17 | + ├── mm-tui/spec.yaml kind: sandbox — `sbx run` opens mm's window on the workspace | ||
| 18 | + └── mm-acp/spec.yaml kind: sandbox — `sbx create` once, the editor runs `sbx exec -i … mm -acp` | ||
| 19 | +``` | ||
| 20 | + | ||
| 21 | +## 0. Use the published kits (no build) | ||
| 22 | + | ||
| 23 | +Every release publishes the image and the two kits to Docker Hub at mm's version: | ||
| 24 | + | ||
| 25 | +| On Docker Hub | What it is | | ||
| 26 | +|---|---| | ||
| 27 | +| `k33g/mm-sandbox:0.0.2` | The template image. You never name it yourself: each kit pulls it. | | ||
| 28 | +| `k33g/mm-tui:0.0.2` | The window-mode kit, for `sbx run`. | | ||
| 29 | +| `k33g/mm-acp:0.0.2` | The editor kit, for `sbx create` + `sbx exec -i … mm -acp`. | | ||
| 30 | + | ||
| 31 | +Nothing to build, no `--pull never`, and no setting to change: Docker Hub is an allowed kit source out of the box (`sbx settings get kit.allowedSources` shows `docker.io/`), and a reference with a `/` but no registry host is read as `docker.io/…`. Only Docker Desktop with the `sbx` command, and a model server on your machine, are needed. | ||
| 32 | + | ||
| 33 | +**Look before you run.** `sbx kit inspect` reads the kit from the Hub without creating anything: its image, its entry point, its arguments and their defaults, the ports it allows. | ||
| 34 | + | ||
| 35 | +```bash | ||
| 36 | +sbx kit inspect k33g/mm-tui:0.0.2 | ||
| 37 | +``` | ||
| 38 | + | ||
| 39 | +**Window mode.** From the project you want mm to work on: | ||
| 40 | + | ||
| 41 | +```bash | ||
| 42 | +cd /path/to/your/project | ||
| 43 | +sbx run k33g/mm-tui:0.0.2 . -- agent.dmr.yaml | ||
| 44 | +``` | ||
| 45 | + | ||
| 46 | +The first run pulls the kit and the image, creates the sandbox `mm-tui-<directory>`, mounts the current directory as its workspace and opens `mm -tui agent.dmr.yaml` in it. What follows `--` is mm's own command line, so `-- -config other.yaml` works too. Without anything after `--`, mm reads the kit's `config` argument, then `./agent.yaml` in the workspace: | ||
| 47 | + | ||
| 48 | +```bash | ||
| 49 | +sbx run k33g/mm-tui:0.0.2 . --kit-arg config=agent.dmr.yaml | ||
| 50 | +``` | ||
| 51 | + | ||
| 52 | +Later, `sbx run --name mm-tui-<directory>` re-attaches to that sandbox; `sbx ls` lists them. | ||
| 53 | + | ||
| 54 | +**From an editor.** Create the sandbox once, from the project the editor will open, with the configuration file baked in: | ||
| 55 | + | ||
| 56 | +```bash | ||
| 57 | +cd /path/to/your/project | ||
| 58 | +sbx create --name mm-acp k33g/mm-acp:0.0.2 . --kit-arg config=agent.dmr.yaml | ||
| 59 | +``` | ||
| 60 | + | ||
| 61 | +then register it in the editor — in Zed's `settings.json`: | ||
| 62 | + | ||
| 63 | +```json | ||
| 64 | +{ | ||
| 65 | + "agent_servers": { | ||
| 66 | + "mini-me (sandbox)": { | ||
| 67 | + "type": "custom", | ||
| 68 | + "command": "sbx", | ||
| 69 | + "args": ["exec", "-i", "mm-acp", "mm", "-acp"] | ||
| 70 | + } | ||
| 71 | + } | ||
| 72 | +} | ||
| 73 | +``` | ||
| 74 | + | ||
| 75 | +The editor starts `mm -acp` inside the sandbox for every conversation; `sbx exec` starts the sandbox first when it is stopped, and runs mm in the workspace with the baked-in `AGENT_CONFIG`. One sandbox per project, each with its own `--name` and its own editor entry. Section 3 has the details (another config for one session, where the logs are, why `sbx exec -i` and not `sbx run`). | ||
| 76 | + | ||
| 77 | +**Kit arguments.** Both kits take the same two, passed with `--kit-arg name=value` (repeatable) to `sbx run` or `sbx create`: | ||
| 78 | + | ||
| 79 | +| `--kit-arg` | Default | Effect | | ||
| 80 | +|---|---|---| | ||
| 81 | +| `config=<path>` | empty | The file mm reads (its `AGENT_CONFIG`): relative to the workspace, or absolute inside the sandbox. Empty: `./agent.yaml`, then the built-in defaults. | | ||
| 82 | +| `tag=<version>` | the kit's own version (`0.0.2`) | Which `k33g/mm-sandbox:<version>` image to use. Normally left alone: a kit and its image share a version. | | ||
| 83 | + | ||
| 84 | +**Other versions.** Replace `0.0.2` with the release you want, in the kit reference only — the kit brings the matching image. A kit can also be pinned by digest, `k33g/mm-tui@sha256:…`, the digest being what `sbx kit inspect` or the Hub page shows. | ||
| 85 | + | ||
| 86 | +**If sbx refuses the source.** A message like `its source is not in your allowlist` means `kit.allowedSources` was narrowed on this machine; `sbx settings set kit.allowedSources '["docker.io/"]'` restores the default, or add `docker.io/k33g/` to your list. | ||
| 87 | + | ||
| 88 | +Sections 1–3 are the same flow with a template and kits built locally, for a work-in-progress mm. | ||
| 89 | + | ||
| 90 | +## 1. Build the template | ||
| 91 | + | ||
| 92 | +From the repository root: | ||
| 93 | + | ||
| 94 | +```bash | ||
| 95 | +./docker-sbx/build.sh # TAG from release.env: v0.0.2 → k33g/mm-sandbox:0.0.2 | ||
| 96 | +./docker-sbx/build.sh v0.0.3 # → k33g/mm-sandbox:0.0.3 | ||
| 97 | +``` | ||
| 98 | + | ||
| 99 | +The image tag is mm's tag without the `v`, the same rule as the release assets, and the script rewrites the kits' default `tag` argument to the version it built, so the kits always name the image just built. The image lands in the local Docker image store: every `sbx run` / `sbx create` below carries `--pull never` until you `docker push k33g/mm-sandbox:0.0.2`. If `sbx` runs on another machine, `EXPORT=1 ./docker-sbx/build.sh` also writes `tmp/mm-sandbox-<version>.tar`; load it there with `sbx template load`. | ||
| 100 | + | ||
| 101 | +## 2. Window mode in the terminal — kit `mm-tui` | ||
| 102 | + | ||
| 103 | +From the project you want `mm` to work on: | ||
| 104 | + | ||
| 105 | +```bash | ||
| 106 | +sbx run --pull never ./docker-sbx/kits/mm-tui . -- agent.dmr.yaml | ||
| 107 | +``` | ||
| 108 | + | ||
| 109 | +What follows `--` reaches `mm` as its own arguments, so this runs `mm -tui agent.dmr.yaml` in the workspace. Any `mm` flag passes through the same way (`-- -config other.yaml`). Two kit arguments exist as well: | ||
| 110 | + | ||
| 111 | +| `--kit-arg` | Default | Effect | | ||
| 112 | +|---|---|---| | ||
| 113 | +| `config=<path>` | empty | The file `mm` reads when nothing follows `--` (its `AGENT_CONFIG`). Relative to the workspace, or absolute. Empty: `./agent.yaml`, then the built-in defaults. | | ||
| 114 | +| `tag=<version>` | the version last built (`0.0.2`) | Which `k33g/mm-sandbox:<version>` image to use. | | ||
| 115 | + | ||
| 116 | +`sbx run --name <name>` re-attaches to a sandbox that already exists. Sessions, when enabled in the YAML, are written under the workspace's `.mm/sessions`, which is your host directory. | ||
| 117 | + | ||
| 118 | +## 3. From an editor (ACP) — kit `mm-acp` | ||
| 119 | + | ||
| 120 | +Create the sandbox once, without attaching, **from the project the editor will open**. `.` is the workspace; sbx makes it absolute. A path that does not exist on your machine makes sbx ask "The selected workspace does not exist. Would you like to create it?" — answer `N` and fix the path. The kit path must be explicit: `./`, `../` or absolute. | ||
| 121 | + | ||
| 122 | +```bash | ||
| 123 | +cd /path/to/your/project | ||
| 124 | +sbx create --pull never --name mm-acp ./docker-sbx/kits/mm-acp . \ | ||
| 125 | + --kit-arg config=agent.dmr.yaml | ||
| 126 | + | ||
| 127 | +# once the image is pushed (docker push k33g/mm-sandbox:0.0.2), --pull never is not needed: | ||
| 128 | +sbx create --name mm-acp ./docker-sbx/kits/mm-acp . \ | ||
| 129 | + --kit-arg config=agent.dmr.yaml | ||
| 130 | +``` | ||
| 131 | + | ||
| 132 | +Then register it in the editor. In Zed's `settings.json`: | ||
| 133 | + | ||
| 134 | +```json | ||
| 135 | +{ | ||
| 136 | + "agent_servers": { | ||
| 137 | + "📦 mini-me (dmr sbx)": { | ||
| 138 | + "type": "custom", | ||
| 139 | + "command": "sbx", | ||
| 140 | + "args": ["exec", "-i", "mm-acp", "mm", "-acp"], | ||
| 141 | + }, | ||
| 142 | + } | ||
| 143 | +} | ||
| 144 | +``` | ||
| 145 | + | ||
| 146 | +Or: | ||
| 147 | + | ||
| 148 | +```bash | ||
| 149 | +sbx create --pull never --name mm-acp-2 ./docker-sbx/kits/mm-acp . | ||
| 150 | +``` | ||
| 151 | + | ||
| 152 | +And: | ||
| 153 | + | ||
| 154 | +```json | ||
| 155 | +{ | ||
| 156 | + "agent_servers": { | ||
| 157 | + "😄 mini-me (dmr sbx)": { | ||
| 158 | + "type": "custom", | ||
| 159 | + "command": "sbx", | ||
| 160 | + "args": ["exec", "-i", "mm-acp-2", "mm", "-acp"], | ||
| 161 | + "env": { "AGENT_CONFIG": "/Users/k33g/kDrive/Rickub/bots-garden/mini-me/demo/agent.dmr.yaml" } | ||
| 162 | + }, | ||
| 163 | + } | ||
| 164 | +} | ||
| 165 | +``` | ||
| 166 | + | ||
| 167 | + | ||
| 168 | + | ||
| 169 | +The editor starts `mm -acp` inside the sandbox for every conversation; `sbx exec` starts the sandbox first when it is stopped, and runs `mm` in the workspace with the `AGENT_CONFIG` baked in by `--kit-arg config`. Everything the model does — commands, file edits — happens inside the sandbox, on the mounted project. | ||
| 170 | + | ||
| 171 | +Why `sbx exec -i` and not `sbx run`: an ACP client speaks JSON-RPC on the agent's raw stdin/stdout, and `sbx run` always allocates a terminal. Attaching to this sandbox with `sbx run --name mm-acp` is still useful: its entry point prints the wiring above with the sandbox's real name, then opens a shell. | ||
| 172 | + | ||
| 173 | +- One sandbox per project: the workspace is fixed at creation. Give each one its own `--name` and its own entry in the editor. | ||
| 174 | +- Another configuration file for one session: `"args": ["exec", "-i", "-e", "AGENT_CONFIG=agent.lsp.yaml", "mm-acp", "mm", "-acp"]`. | ||
| 175 | +- `sbx` not found by the editor: use the absolute path printed by `which sbx` as `"command"`. | ||
| 176 | +- Logs: in Zed, `dev: open acp logs` shows `mm`'s banner, warnings and `[acp] …` trail. | ||
| 177 | + | ||
| 178 | +## 4. Publishing a version | ||
| 179 | + | ||
| 180 | +The Release workflow (`.github/workflows/release.yml`, job `docker-sandbox`) does this on every `v*` tag, after the binaries: it reads two repository secrets, `DOCKER_HANDLE` (the Docker Hub account, also the image owner) and `DOCKER_PAT` (an access token), logs in, then runs the same two scripts you can run from a laptop: | ||
| 181 | + | ||
| 182 | +```bash | ||
| 183 | +docker login -u k33g # with an access token | ||
| 184 | +PUSH=1 ./docker-sbx/build.sh v0.0.2 # buildx, linux/amd64 + linux/arm64, pushed as k33g/mm-sandbox:0.0.2 | ||
| 185 | +./docker-sbx/push-kits.sh v0.0.2 # sbx kit validate + push → k33g/mm-tui:0.0.2, k33g/mm-acp:0.0.2 | ||
| 186 | +``` | ||
| 187 | + | ||
| 188 | +`push-kits.sh` needs the `sbx` CLI (the workflow downloads the Linux tarball from `docker/sbx-releases`); `sbx kit push` talks to the registry directly and authenticates from the Docker credential store, no sandbox daemon involved. Both scripts call `sync-kits.sh` first, so the published kits always name the image just pushed. `IMAGE_OWNER=<handle>` publishes under another account. | ||
| 189 | + | ||
| 190 | +## The model server stays on the host | ||
| 191 | + | ||
| 192 | +Inside a sandbox `localhost` is the sandbox itself. Docker Model Runner and llama-server keep running on your machine and are reached through `host.docker.internal`, which the sandbox proxy maps to your host's `localhost`: | ||
| 193 | + | ||
| 194 | +- `agent.dmr.yaml` already has `fallback: http://host.docker.internal:12434/engines/v1` — nothing to change. | ||
| 195 | +- `agent.llamacpp.yaml` has no fallback: pass `--env AGENT_BASE_URL=http://host.docker.internal:8080/v1` to `sbx run` / `sbx create`, or edit its `baseUrl`. | ||
| 196 | + | ||
| 197 | +Both kits allow ports 12434 (Docker Model Runner) and 8080 (llama-server) on the host. Another port: `sbx policy allow network host.docker.internal:<port>`. | ||
added
docker-sbx/build.sh +105 -0 | new file mode 100755 | ||
| @@ -0,0 +1,105 @@ | ||
| 1 | +#!/usr/bin/env bash | |
| 2 | +# Build the mm sandbox template image, <owner>/mm-sandbox:<version>, from this | |
| 3 | +# checkout. The image tag follows mm's own tag, minus the "v": release.env | |
| 4 | +# says TAG="v0.0.2", the image is k33g/mm-sandbox:0.0.2 and `mm --version` | |
| 5 | +# inside it reports v0.0.2 — the same rule as the release assets. | |
| 6 | +# | |
| 7 | +# Usage: | |
| 8 | +# ./docker-sbx/build.sh # TAG from release.env, image for this machine | |
| 9 | +# ./docker-sbx/build.sh v0.0.3 # override the tag for this run (what CI does) | |
| 10 | +# PUSH=1 ./docker-sbx/build.sh # multi-arch (linux/amd64 + linux/arm64) built | |
| 11 | +# # with buildx and PUSHED to the registry — | |
| 12 | +# # needs `docker login`; this is the CI path | |
| 13 | +# IMAGE_OWNER=someone ./docker-sbx/build.sh # another image owner (default k33g) | |
| 14 | +# PLATFORMS=linux/amd64 PUSH=1 … # narrower multi-arch set | |
| 15 | +# SHELL_TEMPLATE=docker/sandbox-templates:shell-docker ./docker-sbx/build.sh | |
| 16 | +# # base it on the Docker-in-Docker shell | |
| 17 | +# EXPORT=1 ./docker-sbx/build.sh # also `docker save` to tmp/, for | |
| 18 | +# # `sbx template load` on another host | |
| 19 | +# | |
| 20 | +# Whatever the mode, the two kits under docker-sbx/kits/ are then pointed at | |
| 21 | +# the image just built (docker-sbx/sync-kits.sh), so kits and image move | |
| 22 | +# together. Publishing the kits themselves is docker-sbx/push-kits.sh. | |
| 23 | +set -euo pipefail | |
| 24 | + | |
| 25 | +cd "$(dirname "$0")/.." | |
| 26 | + | |
| 27 | +# release.env carries TAG ("v0.0.2"). It is git-ignored, so a checkout without | |
| 28 | +# it names the tag on the command line. A tag given on the command line wins. | |
| 29 | +if [ -f release.env ]; then | |
| 30 | + # shellcheck source=/dev/null | |
| 31 | + source release.env | |
| 32 | +fi | |
| 33 | +TAG="${1:-${TAG:-}}" | |
| 34 | +if ! [[ "${TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then | |
| 35 | + echo "❌ TAG must look like v1.2.3 or v1.2.3-rc.1, got '${TAG}' (check release.env or pass it: ./docker-sbx/build.sh v1.2.3)" | |
| 36 | + exit 1 | |
| 37 | +fi | |
| 38 | + | |
| 39 | +# Not OWNER: release.env uses that name for the git owner (bots-garden), and | |
| 40 | +# the image owner is a different thing — the Docker Hub account, k33g. | |
| 41 | +IMAGE_OWNER="${IMAGE_OWNER:-k33g}" | |
| 42 | +VERSION="${TAG#v}" | |
| 43 | +IMAGE="${IMAGE_OWNER}/mm-sandbox:${VERSION}" | |
| 44 | +PLATFORMS="${PLATFORMS:-linux/amd64,linux/arm64}" | |
| 45 | + | |
| 46 | +build_args=(--build-arg "MM_VERSION=${TAG}") | |
| 47 | +if [ -n "${SHELL_TEMPLATE:-}" ]; then | |
| 48 | + build_args+=(--build-arg "SHELL_TEMPLATE=${SHELL_TEMPLATE}") | |
| 49 | +fi | |
| 50 | + | |
| 51 | +if [ -n "${PUSH:-}" ]; then | |
| 52 | + # Multi-arch needs buildx's container driver: the classic builder can | |
| 53 | + # neither assemble a manifest list nor build for a foreign platform. The | |
| 54 | + # Go stage cross-compiles on the build platform; only the final stage's | |
| 55 | + # `RUN mm --version` runs under emulation (QEMU, set up by the caller). | |
| 56 | + echo "🐳 Building and pushing ${IMAGE} for ${PLATFORMS} (mm ${TAG}, base ${SHELL_TEMPLATE:-docker/sandbox-templates:shell})" | |
| 57 | + if ! docker buildx inspect mm-sandbox-builder >/dev/null 2>&1; then | |
| 58 | + docker buildx create --name mm-sandbox-builder --driver docker-container --bootstrap >/dev/null | |
| 59 | + fi | |
| 60 | + docker buildx build --builder mm-sandbox-builder \ | |
| 61 | + --platform "${PLATFORMS}" \ | |
| 62 | + -f docker-sbx/template/Dockerfile "${build_args[@]}" \ | |
| 63 | + -t "${IMAGE}" --push . | |
| 64 | + # The proof that ships: the registry holds one manifest per platform. | |
| 65 | + docker buildx imagetools inspect "${IMAGE}" | grep -E '^Name:|Platform:' | sed 's/^/ /' | |
| 66 | +else | |
| 67 | + echo "🐳 Building ${IMAGE} (mm ${TAG}, base ${SHELL_TEMPLATE:-docker/sandbox-templates:shell})" | |
| 68 | + docker build -f docker-sbx/template/Dockerfile "${build_args[@]}" -t "${IMAGE}" . | |
| 69 | + | |
| 70 | + # The one proof that what was built starts: --version needs no config and | |
| 71 | + # no model server. It also confirms the stamp reached the binary. | |
| 72 | + reported="$(docker run --rm "${IMAGE}" mm --version)" | |
| 73 | + if ! grep -q -- "${TAG}" <<<"${reported}"; then | |
| 74 | + echo "❌ the image does not report ${TAG}: ${reported}" | |
| 75 | + exit 1 | |
| 76 | + fi | |
| 77 | + echo "✅ ${reported}" | |
| 78 | +fi | |
| 79 | + | |
| 80 | +./docker-sbx/sync-kits.sh "${IMAGE_OWNER}" "${VERSION}" | |
| 81 | + | |
| 82 | +if [ -n "${EXPORT:-}" ]; then | |
| 83 | + mkdir -p tmp | |
| 84 | + docker save -o "tmp/mm-sandbox-${VERSION}.tar" "${IMAGE}" | |
| 85 | + echo "📦 tmp/mm-sandbox-${VERSION}.tar — load it where sbx runs with: sbx template load tmp/mm-sandbox-${VERSION}.tar" | |
| 86 | +fi | |
| 87 | + | |
| 88 | +if [ -n "${PUSH:-}" ]; then | |
| 89 | + cat <<NEXT | |
| 90 | + | |
| 91 | +Image on the registry. Publish the kits next: ./docker-sbx/push-kits.sh ${TAG} | |
| 92 | +NEXT | |
| 93 | +else | |
| 94 | + cat <<NEXT | |
| 95 | + | |
| 96 | +Next, from the project you want mm to work on: | |
| 97 | + | |
| 98 | + window mode sbx run --pull never ./docker-sbx/kits/mm-tui . -- agent.dmr.yaml | |
| 99 | + editor (ACP) sbx create --pull never --name mm-acp ./docker-sbx/kits/mm-acp . --kit-arg config=agent.dmr.yaml | |
| 100 | + then register sbx exec -i mm-acp mm -acp in the editor (docker-sbx/README.md) | |
| 101 | + | |
| 102 | + --pull never because ${IMAGE} exists only in the local image store; | |
| 103 | + after \`docker push ${IMAGE}\` (or PUSH=1) it is no longer needed. | |
| 104 | +NEXT | |
| 105 | +fi | |
| new file mode 100755 | |||
| @@ -0,0 +1,105 @@ | |||
| 1 | +#!/usr/bin/env bash | ||
| 2 | +# Build the mm sandbox template image, <owner>/mm-sandbox:<version>, from this | ||
| 3 | +# checkout. The image tag follows mm's own tag, minus the "v": release.env | ||
| 4 | +# says TAG="v0.0.2", the image is k33g/mm-sandbox:0.0.2 and `mm --version` | ||
| 5 | +# inside it reports v0.0.2 — the same rule as the release assets. | ||
| 6 | +# | ||
| 7 | +# Usage: | ||
| 8 | +# ./docker-sbx/build.sh # TAG from release.env, image for this machine | ||
| 9 | +# ./docker-sbx/build.sh v0.0.3 # override the tag for this run (what CI does) | ||
| 10 | +# PUSH=1 ./docker-sbx/build.sh # multi-arch (linux/amd64 + linux/arm64) built | ||
| 11 | +# # with buildx and PUSHED to the registry — | ||
| 12 | +# # needs `docker login`; this is the CI path | ||
| 13 | +# IMAGE_OWNER=someone ./docker-sbx/build.sh # another image owner (default k33g) | ||
| 14 | +# PLATFORMS=linux/amd64 PUSH=1 … # narrower multi-arch set | ||
| 15 | +# SHELL_TEMPLATE=docker/sandbox-templates:shell-docker ./docker-sbx/build.sh | ||
| 16 | +# # base it on the Docker-in-Docker shell | ||
| 17 | +# EXPORT=1 ./docker-sbx/build.sh # also `docker save` to tmp/, for | ||
| 18 | +# # `sbx template load` on another host | ||
| 19 | +# | ||
| 20 | +# Whatever the mode, the two kits under docker-sbx/kits/ are then pointed at | ||
| 21 | +# the image just built (docker-sbx/sync-kits.sh), so kits and image move | ||
| 22 | +# together. Publishing the kits themselves is docker-sbx/push-kits.sh. | ||
| 23 | +set -euo pipefail | ||
| 24 | + | ||
| 25 | +cd "$(dirname "$0")/.." | ||
| 26 | + | ||
| 27 | +# release.env carries TAG ("v0.0.2"). It is git-ignored, so a checkout without | ||
| 28 | +# it names the tag on the command line. A tag given on the command line wins. | ||
| 29 | +if [ -f release.env ]; then | ||
| 30 | + # shellcheck source=/dev/null | ||
| 31 | + source release.env | ||
| 32 | +fi | ||
| 33 | +TAG="${1:-${TAG:-}}" | ||
| 34 | +if ! [[ "${TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then | ||
| 35 | + echo "❌ TAG must look like v1.2.3 or v1.2.3-rc.1, got '${TAG}' (check release.env or pass it: ./docker-sbx/build.sh v1.2.3)" | ||
| 36 | + exit 1 | ||
| 37 | +fi | ||
| 38 | + | ||
| 39 | +# Not OWNER: release.env uses that name for the git owner (bots-garden), and | ||
| 40 | +# the image owner is a different thing — the Docker Hub account, k33g. | ||
| 41 | +IMAGE_OWNER="${IMAGE_OWNER:-k33g}" | ||
| 42 | +VERSION="${TAG#v}" | ||
| 43 | +IMAGE="${IMAGE_OWNER}/mm-sandbox:${VERSION}" | ||
| 44 | +PLATFORMS="${PLATFORMS:-linux/amd64,linux/arm64}" | ||
| 45 | + | ||
| 46 | +build_args=(--build-arg "MM_VERSION=${TAG}") | ||
| 47 | +if [ -n "${SHELL_TEMPLATE:-}" ]; then | ||
| 48 | + build_args+=(--build-arg "SHELL_TEMPLATE=${SHELL_TEMPLATE}") | ||
| 49 | +fi | ||
| 50 | + | ||
| 51 | +if [ -n "${PUSH:-}" ]; then | ||
| 52 | + # Multi-arch needs buildx's container driver: the classic builder can | ||
| 53 | + # neither assemble a manifest list nor build for a foreign platform. The | ||
| 54 | + # Go stage cross-compiles on the build platform; only the final stage's | ||
| 55 | + # `RUN mm --version` runs under emulation (QEMU, set up by the caller). | ||
| 56 | + echo "🐳 Building and pushing ${IMAGE} for ${PLATFORMS} (mm ${TAG}, base ${SHELL_TEMPLATE:-docker/sandbox-templates:shell})" | ||
| 57 | + if ! docker buildx inspect mm-sandbox-builder >/dev/null 2>&1; then | ||
| 58 | + docker buildx create --name mm-sandbox-builder --driver docker-container --bootstrap >/dev/null | ||
| 59 | + fi | ||
| 60 | + docker buildx build --builder mm-sandbox-builder \ | ||
| 61 | + --platform "${PLATFORMS}" \ | ||
| 62 | + -f docker-sbx/template/Dockerfile "${build_args[@]}" \ | ||
| 63 | + -t "${IMAGE}" --push . | ||
| 64 | + # The proof that ships: the registry holds one manifest per platform. | ||
| 65 | + docker buildx imagetools inspect "${IMAGE}" | grep -E '^Name:|Platform:' | sed 's/^/ /' | ||
| 66 | +else | ||
| 67 | + echo "🐳 Building ${IMAGE} (mm ${TAG}, base ${SHELL_TEMPLATE:-docker/sandbox-templates:shell})" | ||
| 68 | + docker build -f docker-sbx/template/Dockerfile "${build_args[@]}" -t "${IMAGE}" . | ||
| 69 | + | ||
| 70 | + # The one proof that what was built starts: --version needs no config and | ||
| 71 | + # no model server. It also confirms the stamp reached the binary. | ||
| 72 | + reported="$(docker run --rm "${IMAGE}" mm --version)" | ||
| 73 | + if ! grep -q -- "${TAG}" <<<"${reported}"; then | ||
| 74 | + echo "❌ the image does not report ${TAG}: ${reported}" | ||
| 75 | + exit 1 | ||
| 76 | + fi | ||
| 77 | + echo "✅ ${reported}" | ||
| 78 | +fi | ||
| 79 | + | ||
| 80 | +./docker-sbx/sync-kits.sh "${IMAGE_OWNER}" "${VERSION}" | ||
| 81 | + | ||
| 82 | +if [ -n "${EXPORT:-}" ]; then | ||
| 83 | + mkdir -p tmp | ||
| 84 | + docker save -o "tmp/mm-sandbox-${VERSION}.tar" "${IMAGE}" | ||
| 85 | + echo "📦 tmp/mm-sandbox-${VERSION}.tar — load it where sbx runs with: sbx template load tmp/mm-sandbox-${VERSION}.tar" | ||
| 86 | +fi | ||
| 87 | + | ||
| 88 | +if [ -n "${PUSH:-}" ]; then | ||
| 89 | + cat <<NEXT | ||
| 90 | + | ||
| 91 | +Image on the registry. Publish the kits next: ./docker-sbx/push-kits.sh ${TAG} | ||
| 92 | +NEXT | ||
| 93 | +else | ||
| 94 | + cat <<NEXT | ||
| 95 | + | ||
| 96 | +Next, from the project you want mm to work on: | ||
| 97 | + | ||
| 98 | + window mode sbx run --pull never ./docker-sbx/kits/mm-tui . -- agent.dmr.yaml | ||
| 99 | + editor (ACP) sbx create --pull never --name mm-acp ./docker-sbx/kits/mm-acp . --kit-arg config=agent.dmr.yaml | ||
| 100 | + then register sbx exec -i mm-acp mm -acp in the editor (docker-sbx/README.md) | ||
| 101 | + | ||
| 102 | + --pull never because ${IMAGE} exists only in the local image store; | ||
| 103 | + after \`docker push ${IMAGE}\` (or PUSH=1) it is no longer needed. | ||
| 104 | +NEXT | ||
| 105 | +fi | ||
added
docker-sbx/push-kits.sh +59 -0 | new file mode 100755 | ||
| @@ -0,0 +1,59 @@ | ||
| 1 | +#!/usr/bin/env bash | |
| 2 | +# Publish the two kits to Docker Hub as OCI kit artifacts: | |
| 3 | +# <owner>/mm-tui:<version> and <owner>/mm-acp:<version> | |
| 4 | +# | |
| 5 | +# Usage: | |
| 6 | +# ./docker-sbx/push-kits.sh # TAG from release.env | |
| 7 | +# ./docker-sbx/push-kits.sh v0.0.3 # override the tag (what CI does) | |
| 8 | +# IMAGE_OWNER=someone … # another owner (default k33g); the kits | |
| 9 | +# # are re-pointed at someone/mm-sandbox too | |
| 10 | +# REGISTRY=localhost:5000 … # another registry (default docker.io) | |
| 11 | +# SBX=/path/to/sbx … # the sbx binary, if not on the PATH | |
| 12 | +# | |
| 13 | +# Needs a login: `docker login -u <handle>` with a Docker Hub access token. | |
| 14 | +# `sbx kit push` talks to the registry directly — no sandbox daemon involved — | |
| 15 | +# and falls back to the Docker credential store for authentication. | |
| 16 | +# | |
| 17 | +# Push the image first (PUSH=1 ./docker-sbx/build.sh): a kit names | |
| 18 | +# <owner>/mm-sandbox:<version>, and sbx pulls that image when the kit is used. | |
| 19 | +set -euo pipefail | |
| 20 | + | |
| 21 | +cd "$(dirname "$0")/.." | |
| 22 | + | |
| 23 | +if [ -f release.env ]; then | |
| 24 | + # shellcheck source=/dev/null | |
| 25 | + source release.env | |
| 26 | +fi | |
| 27 | +TAG="${1:-${TAG:-}}" | |
| 28 | +if ! [[ "${TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then | |
| 29 | + echo "❌ TAG must look like v1.2.3 or v1.2.3-rc.1, got '${TAG}' (check release.env or pass it: ./docker-sbx/push-kits.sh v1.2.3)" | |
| 30 | + exit 1 | |
| 31 | +fi | |
| 32 | + | |
| 33 | +IMAGE_OWNER="${IMAGE_OWNER:-k33g}" | |
| 34 | +REGISTRY="${REGISTRY:-docker.io}" | |
| 35 | +VERSION="${TAG#v}" | |
| 36 | +SBX="${SBX:-sbx}" | |
| 37 | + | |
| 38 | +if ! command -v "${SBX}" >/dev/null 2>&1; then | |
| 39 | + echo "❌ sbx not found (${SBX}). Install Docker Sandboxes, or SBX=/path/to/sbx" | |
| 40 | + exit 1 | |
| 41 | +fi | |
| 42 | + | |
| 43 | +./docker-sbx/sync-kits.sh "${IMAGE_OWNER}" "${VERSION}" | |
| 44 | + | |
| 45 | +for dir in docker-sbx/kits/*/; do | |
| 46 | + kit="$(basename "${dir}")" | |
| 47 | + ref="${REGISTRY}/${IMAGE_OWNER}/${kit}:${VERSION}" | |
| 48 | + "${SBX}" kit validate "${dir}" | |
| 49 | + "${SBX}" kit push "${dir}" "${ref}" | |
| 50 | +done | |
| 51 | + | |
| 52 | +cat <<NEXT | |
| 53 | + | |
| 54 | +Published. From any machine with Docker Sandboxes (docker.io is an allowed kit source by default): | |
| 55 | + | |
| 56 | + window mode sbx run ${IMAGE_OWNER}/mm-tui:${VERSION} . -- agent.dmr.yaml | |
| 57 | + editor (ACP) sbx create --name mm-acp ${IMAGE_OWNER}/mm-acp:${VERSION} . --kit-arg config=agent.dmr.yaml | |
| 58 | + then register sbx exec -i mm-acp mm -acp in the editor | |
| 59 | +NEXT | |
| new file mode 100755 | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | +#!/usr/bin/env bash | ||
| 2 | +# Publish the two kits to Docker Hub as OCI kit artifacts: | ||
| 3 | +# <owner>/mm-tui:<version> and <owner>/mm-acp:<version> | ||
| 4 | +# | ||
| 5 | +# Usage: | ||
| 6 | +# ./docker-sbx/push-kits.sh # TAG from release.env | ||
| 7 | +# ./docker-sbx/push-kits.sh v0.0.3 # override the tag (what CI does) | ||
| 8 | +# IMAGE_OWNER=someone … # another owner (default k33g); the kits | ||
| 9 | +# # are re-pointed at someone/mm-sandbox too | ||
| 10 | +# REGISTRY=localhost:5000 … # another registry (default docker.io) | ||
| 11 | +# SBX=/path/to/sbx … # the sbx binary, if not on the PATH | ||
| 12 | +# | ||
| 13 | +# Needs a login: `docker login -u <handle>` with a Docker Hub access token. | ||
| 14 | +# `sbx kit push` talks to the registry directly — no sandbox daemon involved — | ||
| 15 | +# and falls back to the Docker credential store for authentication. | ||
| 16 | +# | ||
| 17 | +# Push the image first (PUSH=1 ./docker-sbx/build.sh): a kit names | ||
| 18 | +# <owner>/mm-sandbox:<version>, and sbx pulls that image when the kit is used. | ||
| 19 | +set -euo pipefail | ||
| 20 | + | ||
| 21 | +cd "$(dirname "$0")/.." | ||
| 22 | + | ||
| 23 | +if [ -f release.env ]; then | ||
| 24 | + # shellcheck source=/dev/null | ||
| 25 | + source release.env | ||
| 26 | +fi | ||
| 27 | +TAG="${1:-${TAG:-}}" | ||
| 28 | +if ! [[ "${TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then | ||
| 29 | + echo "❌ TAG must look like v1.2.3 or v1.2.3-rc.1, got '${TAG}' (check release.env or pass it: ./docker-sbx/push-kits.sh v1.2.3)" | ||
| 30 | + exit 1 | ||
| 31 | +fi | ||
| 32 | + | ||
| 33 | +IMAGE_OWNER="${IMAGE_OWNER:-k33g}" | ||
| 34 | +REGISTRY="${REGISTRY:-docker.io}" | ||
| 35 | +VERSION="${TAG#v}" | ||
| 36 | +SBX="${SBX:-sbx}" | ||
| 37 | + | ||
| 38 | +if ! command -v "${SBX}" >/dev/null 2>&1; then | ||
| 39 | + echo "❌ sbx not found (${SBX}). Install Docker Sandboxes, or SBX=/path/to/sbx" | ||
| 40 | + exit 1 | ||
| 41 | +fi | ||
| 42 | + | ||
| 43 | +./docker-sbx/sync-kits.sh "${IMAGE_OWNER}" "${VERSION}" | ||
| 44 | + | ||
| 45 | +for dir in docker-sbx/kits/*/; do | ||
| 46 | + kit="$(basename "${dir}")" | ||
| 47 | + ref="${REGISTRY}/${IMAGE_OWNER}/${kit}:${VERSION}" | ||
| 48 | + "${SBX}" kit validate "${dir}" | ||
| 49 | + "${SBX}" kit push "${dir}" "${ref}" | ||
| 50 | +done | ||
| 51 | + | ||
| 52 | +cat <<NEXT | ||
| 53 | + | ||
| 54 | +Published. From any machine with Docker Sandboxes (docker.io is an allowed kit source by default): | ||
| 55 | + | ||
| 56 | + window mode sbx run ${IMAGE_OWNER}/mm-tui:${VERSION} . -- agent.dmr.yaml | ||
| 57 | + editor (ACP) sbx create --name mm-acp ${IMAGE_OWNER}/mm-acp:${VERSION} . --kit-arg config=agent.dmr.yaml | ||
| 58 | + then register sbx exec -i mm-acp mm -acp in the editor | ||
| 59 | +NEXT | ||
added
docker-sbx/sync-kits.sh +37 -0 | new file mode 100755 | ||
| @@ -0,0 +1,37 @@ | ||
| 1 | +#!/usr/bin/env bash | |
| 2 | +# Point the two kits at one image: <owner>/mm-sandbox:<version>. | |
| 3 | +# | |
| 4 | +# Usage: ./docker-sbx/sync-kits.sh OWNER VERSION (e.g. k33g 0.0.2) | |
| 5 | +# | |
| 6 | +# Rewrites, in docker-sbx/kits/*/spec.yaml, only these three values: | |
| 7 | +# version: "<version>" the kit's own version | |
| 8 | +# args.tag.default: "<version>" which image tag the kit uses | |
| 9 | +# sandbox.image: "<owner>/mm-sandbox:…" which owner the image lives under | |
| 10 | +# Everything else — comments included — is left byte-for-byte alone, so a | |
| 11 | +# kit that already matches is not changed at all. build.sh and push-kits.sh | |
| 12 | +# call this; CI relies on it so the published kits name the image it pushed. | |
| 13 | +set -euo pipefail | |
| 14 | + | |
| 15 | +if [ $# -ne 2 ]; then | |
| 16 | + echo "usage: $0 OWNER VERSION" >&2 | |
| 17 | + exit 2 | |
| 18 | +fi | |
| 19 | +OWNER="$1" | |
| 20 | +VERSION="$2" | |
| 21 | + | |
| 22 | +cd "$(dirname "$0")/.." | |
| 23 | + | |
| 24 | +for kit in docker-sbx/kits/*/spec.yaml; do | |
| 25 | + if ! grep -q '^ tag:$' "${kit}" || ! grep -q '^version: ' "${kit}" || ! grep -q '^ image: "' "${kit}"; then | |
| 26 | + echo "❌ ${kit}: expected a top-level 'version:', an 'args.tag' and a quoted 'sandbox.image' — not updated" >&2 | |
| 27 | + exit 1 | |
| 28 | + fi | |
| 29 | + # sed -i.bak works on both GNU and BSD sed; the backup is removed at once. | |
| 30 | + sed -i.bak -E \ | |
| 31 | + -e "s|^(version: )\"[^\"]*\"|\1\"${VERSION}\"|" \ | |
| 32 | + -e "/^ tag:\$/,/^ default:/ s|^( default: )\"[^\"]*\"|\1\"${VERSION}\"|" \ | |
| 33 | + -e "s|^( image: \")[^/\"]+(/mm-sandbox:.*)|\1${OWNER}\2|" \ | |
| 34 | + "${kit}" | |
| 35 | + rm -f "${kit}.bak" | |
| 36 | + echo "✅ ${kit} → version \"${VERSION}\", tag default \"${VERSION}\", image ${OWNER}/mm-sandbox:\${{ kit.args.tag }}" | |
| 37 | +done | |
| new file mode 100755 | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | +#!/usr/bin/env bash | ||
| 2 | +# Point the two kits at one image: <owner>/mm-sandbox:<version>. | ||
| 3 | +# | ||
| 4 | +# Usage: ./docker-sbx/sync-kits.sh OWNER VERSION (e.g. k33g 0.0.2) | ||
| 5 | +# | ||
| 6 | +# Rewrites, in docker-sbx/kits/*/spec.yaml, only these three values: | ||
| 7 | +# version: "<version>" the kit's own version | ||
| 8 | +# args.tag.default: "<version>" which image tag the kit uses | ||
| 9 | +# sandbox.image: "<owner>/mm-sandbox:…" which owner the image lives under | ||
| 10 | +# Everything else — comments included — is left byte-for-byte alone, so a | ||
| 11 | +# kit that already matches is not changed at all. build.sh and push-kits.sh | ||
| 12 | +# call this; CI relies on it so the published kits name the image it pushed. | ||
| 13 | +set -euo pipefail | ||
| 14 | + | ||
| 15 | +if [ $# -ne 2 ]; then | ||
| 16 | + echo "usage: $0 OWNER VERSION" >&2 | ||
| 17 | + exit 2 | ||
| 18 | +fi | ||
| 19 | +OWNER="$1" | ||
| 20 | +VERSION="$2" | ||
| 21 | + | ||
| 22 | +cd "$(dirname "$0")/.." | ||
| 23 | + | ||
| 24 | +for kit in docker-sbx/kits/*/spec.yaml; do | ||
| 25 | + if ! grep -q '^ tag:$' "${kit}" || ! grep -q '^version: ' "${kit}" || ! grep -q '^ image: "' "${kit}"; then | ||
| 26 | + echo "❌ ${kit}: expected a top-level 'version:', an 'args.tag' and a quoted 'sandbox.image' — not updated" >&2 | ||
| 27 | + exit 1 | ||
| 28 | + fi | ||
| 29 | + # sed -i.bak works on both GNU and BSD sed; the backup is removed at once. | ||
| 30 | + sed -i.bak -E \ | ||
| 31 | + -e "s|^(version: )\"[^\"]*\"|\1\"${VERSION}\"|" \ | ||
| 32 | + -e "/^ tag:\$/,/^ default:/ s|^( default: )\"[^\"]*\"|\1\"${VERSION}\"|" \ | ||
| 33 | + -e "s|^( image: \")[^/\"]+(/mm-sandbox:.*)|\1${OWNER}\2|" \ | ||
| 34 | + "${kit}" | ||
| 35 | + rm -f "${kit}.bak" | ||
| 36 | + echo "✅ ${kit} → version \"${VERSION}\", tag default \"${VERSION}\", image ${OWNER}/mm-sandbox:\${{ kit.args.tag }}" | ||
| 37 | +done | ||
added
docker-sbx/template/Dockerfile +53 -0 | new file mode 100644 | ||
| @@ -0,0 +1,53 @@ | ||
| 1 | +# syntax=docker/dockerfile:1 | |
| 2 | +# | |
| 3 | +# The mm sandbox template: Docker Sandboxes' `shell` image with mm installed. | |
| 4 | +# | |
| 5 | +# Build it from the REPOSITORY ROOT (the context is the mm sources), or run | |
| 6 | +# docker-sbx/build.sh, which does exactly that and stamps mm's tag — the | |
| 7 | +# image tag is that tag without the "v", k33g/mm-sandbox:0.0.2 for v0.0.2: | |
| 8 | +# | |
| 9 | +# docker build -f docker-sbx/template/Dockerfile \ | |
| 10 | +# --build-arg MM_VERSION=v0.0.2 -t k33g/mm-sandbox:0.0.2 . | |
| 11 | +# | |
| 12 | +# The two kits under docker-sbx/kits/ name this image. | |
| 13 | +# ENTRYPOINT and CMD are deliberately not set: the sandbox runtime keeps the | |
| 14 | +# shell template's tini entrypoint and replaces CMD with its own, and the kit's | |
| 15 | +# `sandbox.entrypoint` is what starts mm when you attach. | |
| 16 | + | |
| 17 | +ARG GO_VERSION=1.26 | |
| 18 | +ARG SHELL_TEMPLATE=docker/sandbox-templates:shell | |
| 19 | + | |
| 20 | +# ---- build stage: cross-compile mm for the target platform ----------------- | |
| 21 | +FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-bookworm AS build | |
| 22 | +ARG TARGETOS | |
| 23 | +ARG TARGETARCH | |
| 24 | +# What `mm --version` reports: the tag, as 02-build-releases.sh stamps it. | |
| 25 | +ARG MM_VERSION=dev | |
| 26 | + | |
| 27 | +WORKDIR /src | |
| 28 | +COPY go.mod go.sum ./ | |
| 29 | +RUN --mount=type=cache,target=/go/pkg/mod go mod download | |
| 30 | +COPY . . | |
| 31 | +# Same flags as 02-build-releases.sh: no C, no local paths, no symbols, the | |
| 32 | +# version stamped into internal/version. | |
| 33 | +RUN --mount=type=cache,target=/go/pkg/mod \ | |
| 34 | + --mount=type=cache,target=/root/.cache/go-build \ | |
| 35 | + CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ | |
| 36 | + go build -trimpath -ldflags "-s -w -X mm/internal/version.stamped=${MM_VERSION}" -o /out/mm . | |
| 37 | + | |
| 38 | +# ---- template: the shell sandbox plus mm ----------------------------------- | |
| 39 | +FROM ${SHELL_TEMPLATE} | |
| 40 | + | |
| 41 | +COPY --from=build --chmod=0755 /out/mm /usr/local/bin/mm | |
| 42 | +# The two kit entry points. mm-tui runs `mm -tui "$@"`; mm-acp-info prints how | |
| 43 | +# an editor reaches this sandbox and opens a shell (see docker-sbx/README.md). | |
| 44 | +COPY --chmod=0755 docker-sbx/template/mm-tui docker-sbx/template/mm-acp-info /usr/local/bin/ | |
| 45 | + | |
| 46 | +# tcell (the window mode) needs a terminal type and a UTF-8 locale; the kits | |
| 47 | +# set them too, this is the fallback for a plain `docker run`. | |
| 48 | +ENV TERM=xterm-256color \ | |
| 49 | + COLORTERM=truecolor \ | |
| 50 | + LANG=C.UTF-8 | |
| 51 | + | |
| 52 | +# Prove the binary runs on this platform before the image is tagged. | |
| 53 | +RUN mm --version && mm-tui --version | |
| new file mode 100644 | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | +# syntax=docker/dockerfile:1 | ||
| 2 | +# | ||
| 3 | +# The mm sandbox template: Docker Sandboxes' `shell` image with mm installed. | ||
| 4 | +# | ||
| 5 | +# Build it from the REPOSITORY ROOT (the context is the mm sources), or run | ||
| 6 | +# docker-sbx/build.sh, which does exactly that and stamps mm's tag — the | ||
| 7 | +# image tag is that tag without the "v", k33g/mm-sandbox:0.0.2 for v0.0.2: | ||
| 8 | +# | ||
| 9 | +# docker build -f docker-sbx/template/Dockerfile \ | ||
| 10 | +# --build-arg MM_VERSION=v0.0.2 -t k33g/mm-sandbox:0.0.2 . | ||
| 11 | +# | ||
| 12 | +# The two kits under docker-sbx/kits/ name this image. | ||
| 13 | +# ENTRYPOINT and CMD are deliberately not set: the sandbox runtime keeps the | ||
| 14 | +# shell template's tini entrypoint and replaces CMD with its own, and the kit's | ||
| 15 | +# `sandbox.entrypoint` is what starts mm when you attach. | ||
| 16 | + | ||
| 17 | +ARG GO_VERSION=1.26 | ||
| 18 | +ARG SHELL_TEMPLATE=docker/sandbox-templates:shell | ||
| 19 | + | ||
| 20 | +# ---- build stage: cross-compile mm for the target platform ----------------- | ||
| 21 | +FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-bookworm AS build | ||
| 22 | +ARG TARGETOS | ||
| 23 | +ARG TARGETARCH | ||
| 24 | +# What `mm --version` reports: the tag, as 02-build-releases.sh stamps it. | ||
| 25 | +ARG MM_VERSION=dev | ||
| 26 | + | ||
| 27 | +WORKDIR /src | ||
| 28 | +COPY go.mod go.sum ./ | ||
| 29 | +RUN --mount=type=cache,target=/go/pkg/mod go mod download | ||
| 30 | +COPY . . | ||
| 31 | +# Same flags as 02-build-releases.sh: no C, no local paths, no symbols, the | ||
| 32 | +# version stamped into internal/version. | ||
| 33 | +RUN --mount=type=cache,target=/go/pkg/mod \ | ||
| 34 | + --mount=type=cache,target=/root/.cache/go-build \ | ||
| 35 | + CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ | ||
| 36 | + go build -trimpath -ldflags "-s -w -X mm/internal/version.stamped=${MM_VERSION}" -o /out/mm . | ||
| 37 | + | ||
| 38 | +# ---- template: the shell sandbox plus mm ----------------------------------- | ||
| 39 | +FROM ${SHELL_TEMPLATE} | ||
| 40 | + | ||
| 41 | +COPY --from=build --chmod=0755 /out/mm /usr/local/bin/mm | ||
| 42 | +# The two kit entry points. mm-tui runs `mm -tui "$@"`; mm-acp-info prints how | ||
| 43 | +# an editor reaches this sandbox and opens a shell (see docker-sbx/README.md). | ||
| 44 | +COPY --chmod=0755 docker-sbx/template/mm-tui docker-sbx/template/mm-acp-info /usr/local/bin/ | ||
| 45 | + | ||
| 46 | +# tcell (the window mode) needs a terminal type and a UTF-8 locale; the kits | ||
| 47 | +# set them too, this is the fallback for a plain `docker run`. | ||
| 48 | +ENV TERM=xterm-256color \ | ||
| 49 | + COLORTERM=truecolor \ | ||
| 50 | + LANG=C.UTF-8 | ||
| 51 | + | ||
| 52 | +# Prove the binary runs on this platform before the image is tagged. | ||
| 53 | +RUN mm --version && mm-tui --version | ||
added
docker-sbx/template/mm-acp-info +52 -0 | new file mode 100755 | ||
| @@ -0,0 +1,52 @@ | ||
| 1 | +#!/bin/bash | |
| 2 | +# Entry point of the mm-acp sandbox kit (docker-sbx/kits/mm-acp). | |
| 3 | +# | |
| 4 | +# An editor never runs this script. It reaches mm with | |
| 5 | +# | |
| 6 | +# sbx exec -i <sandbox> mm -acp | |
| 7 | +# | |
| 8 | +# because an ACP client needs raw stdin/stdout and `sbx run` always allocates | |
| 9 | +# a terminal. So attaching to this sandbox with `sbx run` prints the wiring | |
| 10 | +# for the editor, with this sandbox's real name and paths, then opens a shell. | |
| 11 | +set -u | |
| 12 | + | |
| 13 | +name="${SANDBOX_NAME:-$(hostname)}" | |
| 14 | +workspace="${WORKSPACE_DIR:-$PWD}" | |
| 15 | +config="${AGENT_CONFIG:-}" | |
| 16 | +if [ -z "$config" ]; then | |
| 17 | + config_note="none set — mm reads ./agent.yaml in the workspace, then its built-in defaults" | |
| 18 | +elif [ "${config#/}" = "$config" ]; then | |
| 19 | + config_note="$config (relative to the workspace: ${workspace%/}/$config)" | |
| 20 | +else | |
| 21 | + config_note="$config" | |
| 22 | +fi | |
| 23 | + | |
| 24 | +cat <<INFO | |
| 25 | + | |
| 26 | + mm $(mm --version 2>/dev/null | awk '{print $2}') in sandbox "$name" | |
| 27 | + workspace : $workspace | |
| 28 | + config : $config_note | |
| 29 | + | |
| 30 | + This sandbox is meant for an editor (Agent Client Protocol). Register it in | |
| 31 | + Zed's settings.json — the editor starts mm inside the sandbox on every | |
| 32 | + conversation, and sbx starts the sandbox first when it is stopped: | |
| 33 | + | |
| 34 | + { | |
| 35 | + "agent_servers": { | |
| 36 | + "mini-me (sandbox)": { | |
| 37 | + "type": "custom", | |
| 38 | + "command": "sbx", | |
| 39 | + "args": ["exec", "-i", "$name", "mm", "-acp"] | |
| 40 | + } | |
| 41 | + } | |
| 42 | + } | |
| 43 | + | |
| 44 | + Use the absolute path of sbx as "command" if the editor cannot find it | |
| 45 | + (\`which sbx\` on the host). To change the configuration file for one | |
| 46 | + session, add "-e", "AGENT_CONFIG=/path/in/workspace/agent.yaml" before | |
| 47 | + "$name" in "args". Full guide: docs/en/how-to/run-in-a-docker-sandbox.md | |
| 48 | + | |
| 49 | + You are now in a shell inside the sandbox. \`mm agent.yaml\` works here too. | |
| 50 | + | |
| 51 | +INFO | |
| 52 | +exec bash -l | |
| new file mode 100755 | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | +#!/bin/bash | ||
| 2 | +# Entry point of the mm-acp sandbox kit (docker-sbx/kits/mm-acp). | ||
| 3 | +# | ||
| 4 | +# An editor never runs this script. It reaches mm with | ||
| 5 | +# | ||
| 6 | +# sbx exec -i <sandbox> mm -acp | ||
| 7 | +# | ||
| 8 | +# because an ACP client needs raw stdin/stdout and `sbx run` always allocates | ||
| 9 | +# a terminal. So attaching to this sandbox with `sbx run` prints the wiring | ||
| 10 | +# for the editor, with this sandbox's real name and paths, then opens a shell. | ||
| 11 | +set -u | ||
| 12 | + | ||
| 13 | +name="${SANDBOX_NAME:-$(hostname)}" | ||
| 14 | +workspace="${WORKSPACE_DIR:-$PWD}" | ||
| 15 | +config="${AGENT_CONFIG:-}" | ||
| 16 | +if [ -z "$config" ]; then | ||
| 17 | + config_note="none set — mm reads ./agent.yaml in the workspace, then its built-in defaults" | ||
| 18 | +elif [ "${config#/}" = "$config" ]; then | ||
| 19 | + config_note="$config (relative to the workspace: ${workspace%/}/$config)" | ||
| 20 | +else | ||
| 21 | + config_note="$config" | ||
| 22 | +fi | ||
| 23 | + | ||
| 24 | +cat <<INFO | ||
| 25 | + | ||
| 26 | + mm $(mm --version 2>/dev/null | awk '{print $2}') in sandbox "$name" | ||
| 27 | + workspace : $workspace | ||
| 28 | + config : $config_note | ||
| 29 | + | ||
| 30 | + This sandbox is meant for an editor (Agent Client Protocol). Register it in | ||
| 31 | + Zed's settings.json — the editor starts mm inside the sandbox on every | ||
| 32 | + conversation, and sbx starts the sandbox first when it is stopped: | ||
| 33 | + | ||
| 34 | + { | ||
| 35 | + "agent_servers": { | ||
| 36 | + "mini-me (sandbox)": { | ||
| 37 | + "type": "custom", | ||
| 38 | + "command": "sbx", | ||
| 39 | + "args": ["exec", "-i", "$name", "mm", "-acp"] | ||
| 40 | + } | ||
| 41 | + } | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + Use the absolute path of sbx as "command" if the editor cannot find it | ||
| 45 | + (\`which sbx\` on the host). To change the configuration file for one | ||
| 46 | + session, add "-e", "AGENT_CONFIG=/path/in/workspace/agent.yaml" before | ||
| 47 | + "$name" in "args". Full guide: docs/en/how-to/run-in-a-docker-sandbox.md | ||
| 48 | + | ||
| 49 | + You are now in a shell inside the sandbox. \`mm agent.yaml\` works here too. | ||
| 50 | + | ||
| 51 | +INFO | ||
| 52 | +exec bash -l | ||
added
docker-sbx/template/mm-tui +11 -0 | new file mode 100755 | ||
| @@ -0,0 +1,11 @@ | ||
| 1 | +#!/bin/bash | |
| 2 | +# Entry point of the mm-tui sandbox kit (docker-sbx/kits/mm-tui). | |
| 3 | +# | |
| 4 | +# `sbx run` forwards whatever follows "--" as this script's arguments, so | |
| 5 | +# | |
| 6 | +# sbx run --pull never ./docker-sbx/kits/mm-tui . -- agent.dmr.yaml | |
| 7 | +# | |
| 8 | +# runs `mm -tui agent.dmr.yaml` in the workspace. Without an argument mm reads | |
| 9 | +# $AGENT_CONFIG (the kit's `config` argument), then ./agent.yaml, then its | |
| 10 | +# built-in defaults. Flags pass through too: `-- -config other.yaml`. | |
| 11 | +exec mm -tui "$@" | |
| new file mode 100755 | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | +#!/bin/bash | ||
| 2 | +# Entry point of the mm-tui sandbox kit (docker-sbx/kits/mm-tui). | ||
| 3 | +# | ||
| 4 | +# `sbx run` forwards whatever follows "--" as this script's arguments, so | ||
| 5 | +# | ||
| 6 | +# sbx run --pull never ./docker-sbx/kits/mm-tui . -- agent.dmr.yaml | ||
| 7 | +# | ||
| 8 | +# runs `mm -tui agent.dmr.yaml` in the workspace. Without an argument mm reads | ||
| 9 | +# $AGENT_CONFIG (the kit's `config` argument), then ./agent.yaml, then its | ||
| 10 | +# built-in defaults. Flags pass through too: `-- -config other.yaml`. | ||
| 11 | +exec mm -tui "$@" | ||
modified
docs/en/README.md +1 -0 | @@ -12,6 +12,7 @@ This documentation is organised along the four [Diátaxis](https://diataxis.fr) | ||
| 12 | 12 | |
| 13 | 13 | - [Run mini-me on llama.cpp instead of Docker Model Runner](how-to/run-with-llama-cpp.md) |
| 14 | 14 | - [Use mini-me from an editor (ACP)](how-to/use-from-an-editor.md) |
| 15 | +- [Run mini-me in a Docker Sandbox](how-to/run-in-a-docker-sandbox.md) — the template and the two kits under `docker-sbx/`, window mode and editor | |
| 15 | 16 | - [Add a skill](how-to/add-a-skill.md) |
| 16 | 17 | - [Measure whether the model picks the right skill](how-to/measure-skill-routing.md) — the routing eval: run it, read its table, use it as a before-and-after |
| 17 | 18 | - [Keep a long session inside the context window](how-to/manage-the-context-window.md) |
| @@ -12,6 +12,7 @@ This documentation is organised along the four [Diátaxis](https://diataxis.fr) | |||
| 12 | 12 | ||
| 13 | - [Run mini-me on llama.cpp instead of Docker Model Runner](how-to/run-with-llama-cpp.md) | 13 | - [Run mini-me on llama.cpp instead of Docker Model Runner](how-to/run-with-llama-cpp.md) |
| 14 | - [Use mini-me from an editor (ACP)](how-to/use-from-an-editor.md) | 14 | - [Use mini-me from an editor (ACP)](how-to/use-from-an-editor.md) |
| 15 | +- [Run mini-me in a Docker Sandbox](how-to/run-in-a-docker-sandbox.md) — the template and the two kits under `docker-sbx/`, window mode and editor | ||
| 15 | - [Add a skill](how-to/add-a-skill.md) | 16 | - [Add a skill](how-to/add-a-skill.md) |
| 16 | - [Measure whether the model picks the right skill](how-to/measure-skill-routing.md) — the routing eval: run it, read its table, use it as a before-and-after | 17 | - [Measure whether the model picks the right skill](how-to/measure-skill-routing.md) — the routing eval: run it, read its table, use it as a before-and-after |
| 17 | - [Keep a long session inside the context window](how-to/manage-the-context-window.md) | 18 | - [Keep a long session inside the context window](how-to/manage-the-context-window.md) |
added
docs/en/how-to/run-in-a-docker-sandbox.md +138 -0 | new file mode 100644 | ||
| @@ -0,0 +1,138 @@ | ||
| 1 | +# How to run mini-me in a Docker Sandbox | |
| 2 | + | |
| 3 | +This guide shows how to run `mm` inside a [Docker Sandbox](https://docs.docker.com/ai/sandboxes/), so that every command the model runs and every file it edits happens in a container, on a mounted copy of your project. It covers the two ways of using it: the window mode in your terminal, and from an editor through the Agent Client Protocol. It assumes Docker Desktop with the `sbx` command, and a model server (Docker Model Runner or llama-server) running on your machine. | |
| 4 | + | |
| 5 | +Everything lives under [`docker-sbx/`](../../../docker-sbx/README.md) in the repository: | |
| 6 | + | |
| 7 | +| Piece | What it is | | |
| 8 | +|---|---| | |
| 9 | +| `template/Dockerfile` | The **template**: Docker Sandboxes' `shell` image with `mm` built from this checkout, plus the two entry points below. | | |
| 10 | +| `kits/mm-tui/spec.yaml` | A **sandbox kit**: `sbx run` on it opens `mm`'s window mode on the workspace. | | |
| 11 | +| `kits/mm-acp/spec.yaml` | A **sandbox kit** for an editor: created once with `sbx create`, the editor then runs `mm -acp` inside it. | | |
| 12 | +| `build.sh` | Builds the template image, `k33g/mm-sandbox:<version>`; `PUSH=1` builds it for linux/amd64 and linux/arm64 and pushes it. | | |
| 13 | +| `sync-kits.sh`, `push-kits.sh` | Point the kits at one image, and publish them to Docker Hub as OCI kit artifacts. | | |
| 14 | + | |
| 15 | +## Use the published kits | |
| 16 | + | |
| 17 | +Every release publishes the image and both kits to Docker Hub at mm's version — `k33g/mm-sandbox:0.0.2`, `k33g/mm-tui:0.0.2`, `k33g/mm-acp:0.0.2` — so the shortest path needs no build at all. Docker Hub is an allowed kit source by default (`sbx settings get kit.allowedSources` shows `docker.io/`), and a reference with a `/` but no registry host is read as `docker.io/…`: | |
| 18 | + | |
| 19 | +```bash | |
| 20 | +cd /path/to/your/project | |
| 21 | +sbx run k33g/mm-tui:0.0.2 . -- agent.dmr.yaml | |
| 22 | +``` | |
| 23 | + | |
| 24 | +for the window mode, and for an editor: | |
| 25 | + | |
| 26 | +```bash | |
| 27 | +sbx create --name mm-acp k33g/mm-acp:0.0.2 . --kit-arg config=agent.dmr.yaml | |
| 28 | +``` | |
| 29 | + | |
| 30 | +then the editor registration of step 3 below. No `--pull never`: the kit names `k33g/mm-sandbox:0.0.2` and sbx pulls it. `sbx kit inspect k33g/mm-tui:0.0.2` shows what a kit declares before you run it. The steps that follow build the same thing locally, for a work-in-progress mm. | |
| 31 | + | |
| 32 | +## Steps | |
| 33 | + | |
| 34 | +### 1. Build the template | |
| 35 | + | |
| 36 | +From the repository root: | |
| 37 | + | |
| 38 | +```bash | |
| 39 | +./docker-sbx/build.sh | |
| 40 | +``` | |
| 41 | + | |
| 42 | +This reads mm's tag from `release.env` (`v0.0.2`) and builds `k33g/mm-sandbox:0.0.2`: the image tag is the version without the `v`, like the release assets, and `mm` inside is stamped with the tag — `mm --version` is run in the image as proof. The script then rewrites the kits' default `tag` argument to `0.0.2`, so the kits and the image move together. `./docker-sbx/build.sh v0.0.3` builds another version; an earlier image stays reachable with `--kit-arg tag=0.0.1`. | |
| 43 | + | |
| 44 | +The image is in your local Docker image store: every `sbx run` or `sbx create` on these kits takes `--pull never` until the image is pushed (`docker push k33g/mm-sandbox:0.0.2`), or `sbx` tries to pull it and fails. If `sbx` runs on another machine, `EXPORT=1 ./docker-sbx/build.sh` also writes `tmp/mm-sandbox-0.0.2.tar`, to load there with `sbx template load`. | |
| 45 | + | |
| 46 | +### 2. Window mode in the terminal (kit `mm-tui`) | |
| 47 | + | |
| 48 | +From the project you want `mm` to work on: | |
| 49 | + | |
| 50 | +```bash | |
| 51 | +sbx run --pull never ./docker-sbx/kits/mm-tui . -- agent.dmr.yaml | |
| 52 | +``` | |
| 53 | + | |
| 54 | +The first time, `sbx` creates the sandbox, mounts the current directory as its workspace and attaches; then it runs the kit's entry point with what follows `--` as arguments — here `mm -tui agent.dmr.yaml`, in the workspace. Any `mm` flag passes the same way: `-- -config other.yaml`. | |
| 55 | + | |
| 56 | +Without anything after `--`, `mm` reads the kit's `config` argument, then `./agent.yaml` in the workspace, then its built-in defaults: | |
| 57 | + | |
| 58 | +```bash | |
| 59 | +sbx run --pull never ./docker-sbx/kits/mm-tui . --kit-arg config=agent.dmr.yaml | |
| 60 | +``` | |
| 61 | + | |
| 62 | +| `--kit-arg` | Default | Effect | | |
| 63 | +|---|---|---| | |
| 64 | +| `config=<path>` | empty | The configuration file, as `mm`'s `AGENT_CONFIG`: relative to the workspace, or absolute inside the sandbox. | | |
| 65 | +| `tag=<version>` | the version last built (`0.0.2`) | Which `k33g/mm-sandbox:<version>` image the sandbox is made from. | | |
| 66 | + | |
| 67 | +The sandbox keeps its name (`mm-tui-<directory>` unless you pass `--name`); `sbx run --name <name>` re-attaches to it later. Inside, the window is the one described in [Use the window mode](use-the-window-mode.md). Sessions, when `sessions.enabled` is on, are written under the workspace's `.mm/sessions` — that is your host directory, so they survive the sandbox. | |
| 68 | + | |
| 69 | +### 3. From an editor (kit `mm-acp`) | |
| 70 | + | |
| 71 | +An editor talks to `mm -acp` over the agent's raw stdin and stdout. `sbx run` cannot carry that: it always allocates a terminal. So the sandbox is created once without attaching, and the editor starts `mm` inside it with `sbx exec -i`, which pipes stdin and stdout untouched. | |
| 72 | + | |
| 73 | +1. From the project the editor will open, create the sandbox with the configuration file baked in (`.` is the workspace, sbx makes it absolute; the kit path must be an explicit `./`, `../` or absolute path): | |
| 74 | + | |
| 75 | + ```bash | |
| 76 | + cd /path/to/your/project | |
| 77 | + sbx create --pull never --name mm-acp /path/to/mini-me/docker-sbx/kits/mm-acp . \ | |
| 78 | + --kit-arg config=agent.dmr.yaml | |
| 79 | + ``` | |
| 80 | + | |
| 81 | + A workspace path that does not exist on your machine makes sbx ask "The selected workspace does not exist. Would you like to create it?" — answer `N` and fix the path. | |
| 82 | + | |
| 83 | + `--kit-arg config` becomes the container's `AGENT_CONFIG`, relative to the workspace because that is where `sbx exec` starts `mm`. The workspace is fixed at creation: one sandbox per project, each with its own `--name`. | |
| 84 | + | |
| 85 | +2. Register it in the editor. In Zed's `settings.json`: | |
| 86 | + | |
| 87 | + ```json | |
| 88 | + { | |
| 89 | + "agent_servers": { | |
| 90 | + "mini-me (sandbox)": { | |
| 91 | + "type": "custom", | |
| 92 | + "command": "sbx", | |
| 93 | + "args": ["exec", "-i", "mm-acp", "mm", "-acp"] | |
| 94 | + } | |
| 95 | + } | |
| 96 | + } | |
| 97 | + ``` | |
| 98 | + | |
| 99 | + Compared with [the host-side setup](use-from-an-editor.md), the command is `sbx` and no `env` is needed: `AGENT_CONFIG` is already in the container. If the editor cannot find `sbx`, put the absolute path from `which sbx` in `"command"`. | |
| 100 | + | |
| 101 | +3. Open the agent panel and pick **mini-me (sandbox)**. The editor starts `mm -acp` in the sandbox for every conversation; `sbx exec` starts the sandbox first when it is stopped. Tool calls, permission dialogs, `@file` mentions and the slash-command menu work as they do on the host — the difference is where the commands run. | |
| 102 | + | |
| 103 | +Attaching to this sandbox with `sbx run --name mm-acp` does not start `mm`: its entry point prints the wiring above with the sandbox's real name and paths, then opens a shell inside the sandbox. `sbx exec -it mm-acp bash` is the shorter way to that shell. | |
| 104 | + | |
| 105 | +## The model server stays on the host | |
| 106 | + | |
| 107 | +Inside a sandbox `localhost` is the sandbox itself. Your model server keeps running on your machine and is reached through `host.docker.internal`, which the sandbox proxy maps to your host's `localhost`. | |
| 108 | + | |
| 109 | +- **Docker Model Runner** — nothing to change: `agent.dmr.yaml` already has `fallback: http://host.docker.internal:12434/engines/v1`, and `mm` switches to it when `localhost:12434` does not answer (the banner says which URL won). | |
| 110 | +- **llama-server** — `agent.llamacpp.yaml` has no fallback. Either edit its `baseUrl` to `http://host.docker.internal:8080/v1`, or pass `--env AGENT_BASE_URL=http://host.docker.internal:8080/v1` to `sbx run` / `sbx create` (see [Run with llama.cpp](run-with-llama-cpp.md)). | |
| 111 | + | |
| 112 | +Both kits allow ports 12434 and 8080 on the host in the sandbox's network policy. For another port: `sbx policy allow network host.docker.internal:<port>`. | |
| 113 | + | |
| 114 | +## Publishing a version | |
| 115 | + | |
| 116 | +The Release workflow (`.github/workflows/release.yml`, job `docker-sandbox`) publishes the image and the kits on every `v*` tag, after the binaries job. It needs two repository secrets, exposed to the job as environment variables: `DOCKER_HANDLE`, the Docker Hub account (also the image owner), and `DOCKER_PAT`, an access token with read/write scope. It then runs the scripts a laptop would run: | |
| 117 | + | |
| 118 | +```bash | |
| 119 | +docker login -u k33g # paste an access token | |
| 120 | +PUSH=1 ./docker-sbx/build.sh v0.0.2 # buildx, linux/amd64 + linux/arm64, pushed as k33g/mm-sandbox:0.0.2 | |
| 121 | +./docker-sbx/push-kits.sh v0.0.2 # sbx kit validate + sbx kit push → k33g/mm-tui:0.0.2, k33g/mm-acp:0.0.2 | |
| 122 | +``` | |
| 123 | + | |
| 124 | +`push-kits.sh` needs the `sbx` CLI on the PATH (or `SBX=/path/to/sbx`; the workflow downloads the Linux tarball of `docker/sbx-releases`). `sbx kit push` talks to the registry directly and authenticates from the Docker credential store — no sandbox daemon is involved, which is what makes it usable in CI. Both scripts call `sync-kits.sh` first, which rewrites the kits' `version`, default `tag` and image owner, so a published kit always names the image pushed with it. `IMAGE_OWNER=<handle>` publishes under another account. | |
| 125 | + | |
| 126 | +## Variants | |
| 127 | + | |
| 128 | +- **Another configuration for one editor session.** Add `"-e", "AGENT_CONFIG=agent.lsp.yaml"` before the sandbox name in `"args"`; `sbx exec -e` overrides the baked-in value for that run only. | |
| 129 | +- **Skills.** `skillsDir` is relative to the directory `mm` runs in, which is the workspace in both kits — the same rule as on the host. | |
| 130 | +- **Docker inside the sandbox.** Build the template on the Docker-in-Docker shell instead: `SHELL_TEMPLATE=docker/sandbox-templates:shell-docker ./docker-sbx/build.sh`. | |
| 131 | +- **Read the logs.** In the window mode they are on screen; over ACP they go to stderr, which Zed shows under `dev: open acp logs`. | |
| 132 | +- **Remove a sandbox.** `sbx rm mm-acp`. The project directory is a bind mount and is not touched. | |
| 133 | + | |
| 134 | +## See also | |
| 135 | + | |
| 136 | +- The kit format these two files follow: the Docker Sandboxes kit specification (`schemaVersion: "2"`), bundled with the `sbx` sources. | |
| 137 | +- What `mm -acp` exposes to the editor: [ACP reference](../reference/acp.md). | |
| 138 | +- The flags and environment variables `mm` reads: [command line reference](../reference/cli.md). | |
| new file mode 100644 | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | +# How to run mini-me in a Docker Sandbox | ||
| 2 | + | ||
| 3 | +This guide shows how to run `mm` inside a [Docker Sandbox](https://docs.docker.com/ai/sandboxes/), so that every command the model runs and every file it edits happens in a container, on a mounted copy of your project. It covers the two ways of using it: the window mode in your terminal, and from an editor through the Agent Client Protocol. It assumes Docker Desktop with the `sbx` command, and a model server (Docker Model Runner or llama-server) running on your machine. | ||
| 4 | + | ||
| 5 | +Everything lives under [`docker-sbx/`](../../../docker-sbx/README.md) in the repository: | ||
| 6 | + | ||
| 7 | +| Piece | What it is | | ||
| 8 | +|---|---| | ||
| 9 | +| `template/Dockerfile` | The **template**: Docker Sandboxes' `shell` image with `mm` built from this checkout, plus the two entry points below. | | ||
| 10 | +| `kits/mm-tui/spec.yaml` | A **sandbox kit**: `sbx run` on it opens `mm`'s window mode on the workspace. | | ||
| 11 | +| `kits/mm-acp/spec.yaml` | A **sandbox kit** for an editor: created once with `sbx create`, the editor then runs `mm -acp` inside it. | | ||
| 12 | +| `build.sh` | Builds the template image, `k33g/mm-sandbox:<version>`; `PUSH=1` builds it for linux/amd64 and linux/arm64 and pushes it. | | ||
| 13 | +| `sync-kits.sh`, `push-kits.sh` | Point the kits at one image, and publish them to Docker Hub as OCI kit artifacts. | | ||
| 14 | + | ||
| 15 | +## Use the published kits | ||
| 16 | + | ||
| 17 | +Every release publishes the image and both kits to Docker Hub at mm's version — `k33g/mm-sandbox:0.0.2`, `k33g/mm-tui:0.0.2`, `k33g/mm-acp:0.0.2` — so the shortest path needs no build at all. Docker Hub is an allowed kit source by default (`sbx settings get kit.allowedSources` shows `docker.io/`), and a reference with a `/` but no registry host is read as `docker.io/…`: | ||
| 18 | + | ||
| 19 | +```bash | ||
| 20 | +cd /path/to/your/project | ||
| 21 | +sbx run k33g/mm-tui:0.0.2 . -- agent.dmr.yaml | ||
| 22 | +``` | ||
| 23 | + | ||
| 24 | +for the window mode, and for an editor: | ||
| 25 | + | ||
| 26 | +```bash | ||
| 27 | +sbx create --name mm-acp k33g/mm-acp:0.0.2 . --kit-arg config=agent.dmr.yaml | ||
| 28 | +``` | ||
| 29 | + | ||
| 30 | +then the editor registration of step 3 below. No `--pull never`: the kit names `k33g/mm-sandbox:0.0.2` and sbx pulls it. `sbx kit inspect k33g/mm-tui:0.0.2` shows what a kit declares before you run it. The steps that follow build the same thing locally, for a work-in-progress mm. | ||
| 31 | + | ||
| 32 | +## Steps | ||
| 33 | + | ||
| 34 | +### 1. Build the template | ||
| 35 | + | ||
| 36 | +From the repository root: | ||
| 37 | + | ||
| 38 | +```bash | ||
| 39 | +./docker-sbx/build.sh | ||
| 40 | +``` | ||
| 41 | + | ||
| 42 | +This reads mm's tag from `release.env` (`v0.0.2`) and builds `k33g/mm-sandbox:0.0.2`: the image tag is the version without the `v`, like the release assets, and `mm` inside is stamped with the tag — `mm --version` is run in the image as proof. The script then rewrites the kits' default `tag` argument to `0.0.2`, so the kits and the image move together. `./docker-sbx/build.sh v0.0.3` builds another version; an earlier image stays reachable with `--kit-arg tag=0.0.1`. | ||
| 43 | + | ||
| 44 | +The image is in your local Docker image store: every `sbx run` or `sbx create` on these kits takes `--pull never` until the image is pushed (`docker push k33g/mm-sandbox:0.0.2`), or `sbx` tries to pull it and fails. If `sbx` runs on another machine, `EXPORT=1 ./docker-sbx/build.sh` also writes `tmp/mm-sandbox-0.0.2.tar`, to load there with `sbx template load`. | ||
| 45 | + | ||
| 46 | +### 2. Window mode in the terminal (kit `mm-tui`) | ||
| 47 | + | ||
| 48 | +From the project you want `mm` to work on: | ||
| 49 | + | ||
| 50 | +```bash | ||
| 51 | +sbx run --pull never ./docker-sbx/kits/mm-tui . -- agent.dmr.yaml | ||
| 52 | +``` | ||
| 53 | + | ||
| 54 | +The first time, `sbx` creates the sandbox, mounts the current directory as its workspace and attaches; then it runs the kit's entry point with what follows `--` as arguments — here `mm -tui agent.dmr.yaml`, in the workspace. Any `mm` flag passes the same way: `-- -config other.yaml`. | ||
| 55 | + | ||
| 56 | +Without anything after `--`, `mm` reads the kit's `config` argument, then `./agent.yaml` in the workspace, then its built-in defaults: | ||
| 57 | + | ||
| 58 | +```bash | ||
| 59 | +sbx run --pull never ./docker-sbx/kits/mm-tui . --kit-arg config=agent.dmr.yaml | ||
| 60 | +``` | ||
| 61 | + | ||
| 62 | +| `--kit-arg` | Default | Effect | | ||
| 63 | +|---|---|---| | ||
| 64 | +| `config=<path>` | empty | The configuration file, as `mm`'s `AGENT_CONFIG`: relative to the workspace, or absolute inside the sandbox. | | ||
| 65 | +| `tag=<version>` | the version last built (`0.0.2`) | Which `k33g/mm-sandbox:<version>` image the sandbox is made from. | | ||
| 66 | + | ||
| 67 | +The sandbox keeps its name (`mm-tui-<directory>` unless you pass `--name`); `sbx run --name <name>` re-attaches to it later. Inside, the window is the one described in [Use the window mode](use-the-window-mode.md). Sessions, when `sessions.enabled` is on, are written under the workspace's `.mm/sessions` — that is your host directory, so they survive the sandbox. | ||
| 68 | + | ||
| 69 | +### 3. From an editor (kit `mm-acp`) | ||
| 70 | + | ||
| 71 | +An editor talks to `mm -acp` over the agent's raw stdin and stdout. `sbx run` cannot carry that: it always allocates a terminal. So the sandbox is created once without attaching, and the editor starts `mm` inside it with `sbx exec -i`, which pipes stdin and stdout untouched. | ||
| 72 | + | ||
| 73 | +1. From the project the editor will open, create the sandbox with the configuration file baked in (`.` is the workspace, sbx makes it absolute; the kit path must be an explicit `./`, `../` or absolute path): | ||
| 74 | + | ||
| 75 | + ```bash | ||
| 76 | + cd /path/to/your/project | ||
| 77 | + sbx create --pull never --name mm-acp /path/to/mini-me/docker-sbx/kits/mm-acp . \ | ||
| 78 | + --kit-arg config=agent.dmr.yaml | ||
| 79 | + ``` | ||
| 80 | + | ||
| 81 | + A workspace path that does not exist on your machine makes sbx ask "The selected workspace does not exist. Would you like to create it?" — answer `N` and fix the path. | ||
| 82 | + | ||
| 83 | + `--kit-arg config` becomes the container's `AGENT_CONFIG`, relative to the workspace because that is where `sbx exec` starts `mm`. The workspace is fixed at creation: one sandbox per project, each with its own `--name`. | ||
| 84 | + | ||
| 85 | +2. Register it in the editor. In Zed's `settings.json`: | ||
| 86 | + | ||
| 87 | + ```json | ||
| 88 | + { | ||
| 89 | + "agent_servers": { | ||
| 90 | + "mini-me (sandbox)": { | ||
| 91 | + "type": "custom", | ||
| 92 | + "command": "sbx", | ||
| 93 | + "args": ["exec", "-i", "mm-acp", "mm", "-acp"] | ||
| 94 | + } | ||
| 95 | + } | ||
| 96 | + } | ||
| 97 | + ``` | ||
| 98 | + | ||
| 99 | + Compared with [the host-side setup](use-from-an-editor.md), the command is `sbx` and no `env` is needed: `AGENT_CONFIG` is already in the container. If the editor cannot find `sbx`, put the absolute path from `which sbx` in `"command"`. | ||
| 100 | + | ||
| 101 | +3. Open the agent panel and pick **mini-me (sandbox)**. The editor starts `mm -acp` in the sandbox for every conversation; `sbx exec` starts the sandbox first when it is stopped. Tool calls, permission dialogs, `@file` mentions and the slash-command menu work as they do on the host — the difference is where the commands run. | ||
| 102 | + | ||
| 103 | +Attaching to this sandbox with `sbx run --name mm-acp` does not start `mm`: its entry point prints the wiring above with the sandbox's real name and paths, then opens a shell inside the sandbox. `sbx exec -it mm-acp bash` is the shorter way to that shell. | ||
| 104 | + | ||
| 105 | +## The model server stays on the host | ||
| 106 | + | ||
| 107 | +Inside a sandbox `localhost` is the sandbox itself. Your model server keeps running on your machine and is reached through `host.docker.internal`, which the sandbox proxy maps to your host's `localhost`. | ||
| 108 | + | ||
| 109 | +- **Docker Model Runner** — nothing to change: `agent.dmr.yaml` already has `fallback: http://host.docker.internal:12434/engines/v1`, and `mm` switches to it when `localhost:12434` does not answer (the banner says which URL won). | ||
| 110 | +- **llama-server** — `agent.llamacpp.yaml` has no fallback. Either edit its `baseUrl` to `http://host.docker.internal:8080/v1`, or pass `--env AGENT_BASE_URL=http://host.docker.internal:8080/v1` to `sbx run` / `sbx create` (see [Run with llama.cpp](run-with-llama-cpp.md)). | ||
| 111 | + | ||
| 112 | +Both kits allow ports 12434 and 8080 on the host in the sandbox's network policy. For another port: `sbx policy allow network host.docker.internal:<port>`. | ||
| 113 | + | ||
| 114 | +## Publishing a version | ||
| 115 | + | ||
| 116 | +The Release workflow (`.github/workflows/release.yml`, job `docker-sandbox`) publishes the image and the kits on every `v*` tag, after the binaries job. It needs two repository secrets, exposed to the job as environment variables: `DOCKER_HANDLE`, the Docker Hub account (also the image owner), and `DOCKER_PAT`, an access token with read/write scope. It then runs the scripts a laptop would run: | ||
| 117 | + | ||
| 118 | +```bash | ||
| 119 | +docker login -u k33g # paste an access token | ||
| 120 | +PUSH=1 ./docker-sbx/build.sh v0.0.2 # buildx, linux/amd64 + linux/arm64, pushed as k33g/mm-sandbox:0.0.2 | ||
| 121 | +./docker-sbx/push-kits.sh v0.0.2 # sbx kit validate + sbx kit push → k33g/mm-tui:0.0.2, k33g/mm-acp:0.0.2 | ||
| 122 | +``` | ||
| 123 | + | ||
| 124 | +`push-kits.sh` needs the `sbx` CLI on the PATH (or `SBX=/path/to/sbx`; the workflow downloads the Linux tarball of `docker/sbx-releases`). `sbx kit push` talks to the registry directly and authenticates from the Docker credential store — no sandbox daemon is involved, which is what makes it usable in CI. Both scripts call `sync-kits.sh` first, which rewrites the kits' `version`, default `tag` and image owner, so a published kit always names the image pushed with it. `IMAGE_OWNER=<handle>` publishes under another account. | ||
| 125 | + | ||
| 126 | +## Variants | ||
| 127 | + | ||
| 128 | +- **Another configuration for one editor session.** Add `"-e", "AGENT_CONFIG=agent.lsp.yaml"` before the sandbox name in `"args"`; `sbx exec -e` overrides the baked-in value for that run only. | ||
| 129 | +- **Skills.** `skillsDir` is relative to the directory `mm` runs in, which is the workspace in both kits — the same rule as on the host. | ||
| 130 | +- **Docker inside the sandbox.** Build the template on the Docker-in-Docker shell instead: `SHELL_TEMPLATE=docker/sandbox-templates:shell-docker ./docker-sbx/build.sh`. | ||
| 131 | +- **Read the logs.** In the window mode they are on screen; over ACP they go to stderr, which Zed shows under `dev: open acp logs`. | ||
| 132 | +- **Remove a sandbox.** `sbx rm mm-acp`. The project directory is a bind mount and is not touched. | ||
| 133 | + | ||
| 134 | +## See also | ||
| 135 | + | ||
| 136 | +- The kit format these two files follow: the Docker Sandboxes kit specification (`schemaVersion: "2"`), bundled with the `sbx` sources. | ||
| 137 | +- What `mm -acp` exposes to the editor: [ACP reference](../reference/acp.md). | ||
| 138 | +- The flags and environment variables `mm` reads: [command line reference](../reference/cli.md). | ||
modified
docs/fr/README.md +1 -0 | @@ -12,6 +12,7 @@ Cette documentation est organisée selon les quatre quadrants [Diátaxis](https: | ||
| 12 | 12 | |
| 13 | 13 | - [Faire tourner mini-me sur llama.cpp plutôt que Docker Model Runner](how-to/run-with-llama-cpp.md) |
| 14 | 14 | - [Utiliser mini-me depuis un éditeur (ACP)](how-to/use-from-an-editor.md) |
| 15 | +- [Faire tourner mini-me dans un Docker Sandbox](how-to/run-in-a-docker-sandbox.md) — le template et les deux kits sous `docker-sbx/`, mode fenêtre et éditeur | |
| 15 | 16 | - [Ajouter un skill](how-to/add-a-skill.md) |
| 16 | 17 | - [Mesurer si le modèle choisit le bon skill](how-to/measure-skill-routing.md) — l'eval de routage : le lancer, lire son tableau, s'en servir en avant/après |
| 17 | 18 | - [Garder une longue session dans la fenêtre de contexte](how-to/manage-the-context-window.md) |
| @@ -12,6 +12,7 @@ Cette documentation est organisée selon les quatre quadrants [Diátaxis](https: | |||
| 12 | 12 | ||
| 13 | - [Faire tourner mini-me sur llama.cpp plutôt que Docker Model Runner](how-to/run-with-llama-cpp.md) | 13 | - [Faire tourner mini-me sur llama.cpp plutôt que Docker Model Runner](how-to/run-with-llama-cpp.md) |
| 14 | - [Utiliser mini-me depuis un éditeur (ACP)](how-to/use-from-an-editor.md) | 14 | - [Utiliser mini-me depuis un éditeur (ACP)](how-to/use-from-an-editor.md) |
| 15 | +- [Faire tourner mini-me dans un Docker Sandbox](how-to/run-in-a-docker-sandbox.md) — le template et les deux kits sous `docker-sbx/`, mode fenêtre et éditeur | ||
| 15 | - [Ajouter un skill](how-to/add-a-skill.md) | 16 | - [Ajouter un skill](how-to/add-a-skill.md) |
| 16 | - [Mesurer si le modèle choisit le bon skill](how-to/measure-skill-routing.md) — l'eval de routage : le lancer, lire son tableau, s'en servir en avant/après | 17 | - [Mesurer si le modèle choisit le bon skill](how-to/measure-skill-routing.md) — l'eval de routage : le lancer, lire son tableau, s'en servir en avant/après |
| 17 | - [Garder une longue session dans la fenêtre de contexte](how-to/manage-the-context-window.md) | 18 | - [Garder une longue session dans la fenêtre de contexte](how-to/manage-the-context-window.md) |
added
docs/fr/how-to/run-in-a-docker-sandbox.md +138 -0 | new file mode 100644 | ||
| @@ -0,0 +1,138 @@ | ||
| 1 | +# Faire tourner mini-me dans un Docker Sandbox | |
| 2 | + | |
| 3 | +Ce guide montre comment exécuter `mm` dans un [Docker Sandbox](https://docs.docker.com/ai/sandboxes/), pour que chaque commande lancée par le modèle et chaque fichier qu'il modifie le soient dans un conteneur, sur votre projet monté. Il couvre les deux usages : le mode fenêtre dans votre terminal, et depuis un éditeur via l'Agent Client Protocol. Il suppose Docker Desktop avec la commande `sbx`, et un serveur de modèles (Docker Model Runner ou llama-server) qui tourne sur votre machine. | |
| 4 | + | |
| 5 | +Tout est sous [`docker-sbx/`](../../../docker-sbx/README.md) dans le dépôt : | |
| 6 | + | |
| 7 | +| Élément | Ce que c'est | | |
| 8 | +|---|---| | |
| 9 | +| `template/Dockerfile` | Le **template** : l'image `shell` de Docker Sandboxes avec `mm` compilé depuis ce dépôt, plus les deux points d'entrée ci-dessous. | | |
| 10 | +| `kits/mm-tui/spec.yaml` | Un **kit sandbox** : `sbx run` dessus ouvre le mode fenêtre de `mm` sur l'espace de travail. | | |
| 11 | +| `kits/mm-acp/spec.yaml` | Un **kit sandbox** pour un éditeur : créé une fois avec `sbx create`, l'éditeur y lance ensuite `mm -acp`. | | |
| 12 | +| `build.sh` | Construit l'image du template, `k33g/mm-sandbox:<version>` ; `PUSH=1` la construit pour linux/amd64 et linux/arm64 et la pousse. | | |
| 13 | +| `sync-kits.sh`, `push-kits.sh` | Alignent les kits sur une image, et les publient sur Docker Hub comme artefacts OCI de kit. | | |
| 14 | + | |
| 15 | +## Utiliser les kits publiés | |
| 16 | + | |
| 17 | +Chaque release publie l'image et les deux kits sur Docker Hub à la version de mm — `k33g/mm-sandbox:0.0.2`, `k33g/mm-tui:0.0.2`, `k33g/mm-acp:0.0.2` — le chemin le plus court ne demande donc aucune construction. Docker Hub est une source de kits autorisée par défaut (`sbx settings get kit.allowedSources` montre `docker.io/`), et une référence avec un `/` mais sans hôte de registre est lue comme `docker.io/…` : | |
| 18 | + | |
| 19 | +```bash | |
| 20 | +cd /chemin/de/votre/projet | |
| 21 | +sbx run k33g/mm-tui:0.0.2 . -- agent.dmr.yaml | |
| 22 | +``` | |
| 23 | + | |
| 24 | +pour le mode fenêtre, et pour un éditeur : | |
| 25 | + | |
| 26 | +```bash | |
| 27 | +sbx create --name mm-acp k33g/mm-acp:0.0.2 . --kit-arg config=agent.dmr.yaml | |
| 28 | +``` | |
| 29 | + | |
| 30 | +puis la déclaration dans l'éditeur de l'étape 3 ci-dessous. Pas de `--pull never` : le kit nomme `k33g/mm-sandbox:0.0.2` et sbx la télécharge. `sbx kit inspect k33g/mm-tui:0.0.2` montre ce qu'un kit déclare avant de le lancer. Les étapes qui suivent construisent la même chose en local, pour un mm en cours de travail. | |
| 31 | + | |
| 32 | +## Étapes | |
| 33 | + | |
| 34 | +### 1. Construire le template | |
| 35 | + | |
| 36 | +Depuis la racine du dépôt : | |
| 37 | + | |
| 38 | +```bash | |
| 39 | +./docker-sbx/build.sh | |
| 40 | +``` | |
| 41 | + | |
| 42 | +Cela lit le tag de mm dans `release.env` (`v0.0.2`) et construit `k33g/mm-sandbox:0.0.2` : le tag de l'image est la version sans le `v`, comme les binaires de release, et le `mm` à l'intérieur est estampillé avec le tag — `mm --version` est exécuté dans l'image comme preuve. Le script réécrit ensuite la valeur par défaut de l'argument `tag` des kits à `0.0.2`, pour que kits et image avancent ensemble. `./docker-sbx/build.sh v0.0.3` construit une autre version ; une image antérieure reste joignable avec `--kit-arg tag=0.0.1`. | |
| 43 | + | |
| 44 | +L'image est dans votre store d'images Docker local : chaque `sbx run` ou `sbx create` sur ces kits prend `--pull never` tant qu'elle n'est pas poussée (`docker push k33g/mm-sandbox:0.0.2`), sinon `sbx` tente de la télécharger et échoue. Si `sbx` tourne sur une autre machine, `EXPORT=1 ./docker-sbx/build.sh` écrit aussi `tmp/mm-sandbox-0.0.2.tar`, à charger là-bas avec `sbx template load`. | |
| 45 | + | |
| 46 | +### 2. Mode fenêtre dans le terminal (kit `mm-tui`) | |
| 47 | + | |
| 48 | +Depuis le projet sur lequel `mm` doit travailler : | |
| 49 | + | |
| 50 | +```bash | |
| 51 | +sbx run --pull never ./docker-sbx/kits/mm-tui . -- agent.dmr.yaml | |
| 52 | +``` | |
| 53 | + | |
| 54 | +La première fois, `sbx` crée le sandbox, monte le répertoire courant comme espace de travail et s'y attache ; puis il lance le point d'entrée du kit avec ce qui suit `--` en arguments — ici `mm -tui agent.dmr.yaml`, dans l'espace de travail. N'importe quel drapeau de `mm` passe de la même façon : `-- -config autre.yaml`. | |
| 55 | + | |
| 56 | +Sans rien après `--`, `mm` lit l'argument `config` du kit, puis `./agent.yaml` dans l'espace de travail, puis ses valeurs par défaut : | |
| 57 | + | |
| 58 | +```bash | |
| 59 | +sbx run --pull never ./docker-sbx/kits/mm-tui . --kit-arg config=agent.dmr.yaml | |
| 60 | +``` | |
| 61 | + | |
| 62 | +| `--kit-arg` | Défaut | Effet | | |
| 63 | +|---|---|---| | |
| 64 | +| `config=<chemin>` | vide | Le fichier de configuration, comme `AGENT_CONFIG` de `mm` : relatif à l'espace de travail, ou absolu dans le sandbox. | | |
| 65 | +| `tag=<version>` | la dernière version construite (`0.0.2`) | L'image `k33g/mm-sandbox:<version>` dont le sandbox est fait. | | |
| 66 | + | |
| 67 | +Le sandbox garde son nom (`mm-tui-<répertoire>` sauf `--name`) ; `sbx run --name <nom>` s'y rattache plus tard. À l'intérieur, la fenêtre est celle décrite dans [Utiliser le mode fenêtre](use-the-window-mode.md). Les sessions, quand `sessions.enabled` est actif, sont écrites sous `.mm/sessions` dans l'espace de travail — c'est votre répertoire hôte, elles survivent donc au sandbox. | |
| 68 | + | |
| 69 | +### 3. Depuis un éditeur (kit `mm-acp`) | |
| 70 | + | |
| 71 | +Un éditeur parle à `mm -acp` sur l'entrée et la sortie standard brutes de l'agent. `sbx run` ne peut pas porter cela : il alloue toujours un terminal. Le sandbox est donc créé une fois sans s'y attacher, et l'éditeur y lance `mm` avec `sbx exec -i`, qui transmet stdin et stdout tels quels. | |
| 72 | + | |
| 73 | +1. Depuis le projet que l'éditeur ouvrira, créez le sandbox avec le fichier de configuration intégré (`.` est l'espace de travail, sbx le rend absolu ; le chemin du kit doit être explicite : `./`, `../` ou absolu) : | |
| 74 | + | |
| 75 | + ```bash | |
| 76 | + cd /chemin/de/votre/projet | |
| 77 | + sbx create --pull never --name mm-acp /chemin/de/mini-me/docker-sbx/kits/mm-acp . \ | |
| 78 | + --kit-arg config=agent.dmr.yaml | |
| 79 | + ``` | |
| 80 | + | |
| 81 | + Un chemin d'espace de travail qui n'existe pas sur votre machine fait demander à sbx « The selected workspace does not exist. Would you like to create it? » — répondez `N` et corrigez le chemin. | |
| 82 | + | |
| 83 | + `--kit-arg config` devient l'`AGENT_CONFIG` du conteneur, relatif à l'espace de travail parce que c'est là que `sbx exec` démarre `mm`. L'espace de travail est fixé à la création : un sandbox par projet, chacun avec son `--name`. | |
| 84 | + | |
| 85 | +2. Déclarez-le dans l'éditeur. Dans le `settings.json` de Zed : | |
| 86 | + | |
| 87 | + ```json | |
| 88 | + { | |
| 89 | + "agent_servers": { | |
| 90 | + "mini-me (sandbox)": { | |
| 91 | + "type": "custom", | |
| 92 | + "command": "sbx", | |
| 93 | + "args": ["exec", "-i", "mm-acp", "mm", "-acp"] | |
| 94 | + } | |
| 95 | + } | |
| 96 | + } | |
| 97 | + ``` | |
| 98 | + | |
| 99 | + Par rapport à [la configuration côté hôte](use-from-an-editor.md), la commande est `sbx` et aucun `env` n'est nécessaire : `AGENT_CONFIG` est déjà dans le conteneur. Si l'éditeur ne trouve pas `sbx`, mettez le chemin absolu donné par `which sbx` dans `"command"`. | |
| 100 | + | |
| 101 | +3. Ouvrez le panneau agent et choisissez **mini-me (sandbox)**. L'éditeur lance `mm -acp` dans le sandbox à chaque conversation ; `sbx exec` démarre d'abord le sandbox s'il est arrêté. Appels d'outils, dialogues de permission, mentions `@fichier` et menu des commandes fonctionnent comme sur l'hôte — la différence est l'endroit où les commandes s'exécutent. | |
| 102 | + | |
| 103 | +S'attacher à ce sandbox avec `sbx run --name mm-acp` ne lance pas `mm` : son point d'entrée affiche le câblage ci-dessus avec le vrai nom et les vrais chemins du sandbox, puis ouvre un shell à l'intérieur. `sbx exec -it mm-acp bash` est le chemin le plus court vers ce shell. | |
| 104 | + | |
| 105 | +## Le serveur de modèles reste sur l'hôte | |
| 106 | + | |
| 107 | +Dans un sandbox, `localhost` est le sandbox lui-même. Votre serveur de modèles continue de tourner sur votre machine et se joint par `host.docker.internal`, que le proxy du sandbox fait pointer vers le `localhost` de l'hôte. | |
| 108 | + | |
| 109 | +- **Docker Model Runner** — rien à changer : `agent.dmr.yaml` a déjà `fallback: http://host.docker.internal:12434/engines/v1`, et `mm` bascule dessus quand `localhost:12434` ne répond pas (la bannière dit quelle URL a gagné). | |
| 110 | +- **llama-server** — `agent.llamacpp.yaml` n'a pas de fallback. Soit vous changez son `baseUrl` en `http://host.docker.internal:8080/v1`, soit vous passez `--env AGENT_BASE_URL=http://host.docker.internal:8080/v1` à `sbx run` / `sbx create` (voir [Faire tourner sur llama.cpp](run-with-llama-cpp.md)). | |
| 111 | + | |
| 112 | +Les deux kits autorisent les ports 12434 et 8080 de l'hôte dans la politique réseau du sandbox. Pour un autre port : `sbx policy allow network host.docker.internal:<port>`. | |
| 113 | + | |
| 114 | +## Publier une version | |
| 115 | + | |
| 116 | +Le workflow Release (`.github/workflows/release.yml`, job `docker-sandbox`) publie l'image et les kits à chaque tag `v*`, après le job des binaires. Il a besoin de deux secrets du dépôt, exposés au job comme variables d'environnement : `DOCKER_HANDLE`, le compte Docker Hub (aussi le propriétaire de l'image), et `DOCKER_PAT`, un jeton d'accès en lecture/écriture. Il lance ensuite les scripts qu'un portable lancerait : | |
| 117 | + | |
| 118 | +```bash | |
| 119 | +docker login -u k33g # coller un jeton d'accès | |
| 120 | +PUSH=1 ./docker-sbx/build.sh v0.0.2 # buildx, linux/amd64 + linux/arm64, poussée comme k33g/mm-sandbox:0.0.2 | |
| 121 | +./docker-sbx/push-kits.sh v0.0.2 # sbx kit validate + sbx kit push → k33g/mm-tui:0.0.2, k33g/mm-acp:0.0.2 | |
| 122 | +``` | |
| 123 | + | |
| 124 | +`push-kits.sh` a besoin de la CLI `sbx` sur le PATH (ou `SBX=/chemin/vers/sbx` ; le workflow télécharge l'archive Linux de `docker/sbx-releases`). `sbx kit push` parle directement au registre et s'authentifie depuis le magasin d'identifiants Docker — aucun démon sandbox n'intervient, c'est ce qui le rend utilisable en CI. Les deux scripts appellent d'abord `sync-kits.sh`, qui réécrit `version`, le `tag` par défaut et le propriétaire de l'image des kits, de sorte qu'un kit publié nomme toujours l'image poussée avec lui. `IMAGE_OWNER=<compte>` publie sous un autre compte. | |
| 125 | + | |
| 126 | +## Variantes | |
| 127 | + | |
| 128 | +- **Une autre configuration pour une session d'éditeur.** Ajoutez `"-e", "AGENT_CONFIG=agent.lsp.yaml"` avant le nom du sandbox dans `"args"` ; `sbx exec -e` remplace la valeur intégrée pour cette exécution seulement. | |
| 129 | +- **Skills.** `skillsDir` est relatif au répertoire où `mm` tourne, c'est-à-dire l'espace de travail dans les deux kits — la même règle que sur l'hôte. | |
| 130 | +- **Docker dans le sandbox.** Construisez le template sur le shell Docker-in-Docker : `SHELL_TEMPLATE=docker/sandbox-templates:shell-docker ./docker-sbx/build.sh`. | |
| 131 | +- **Lire les logs.** En mode fenêtre ils sont à l'écran ; en ACP ils vont sur stderr, que Zed montre sous `dev: open acp logs`. | |
| 132 | +- **Supprimer un sandbox.** `sbx rm mm-acp`. Le répertoire du projet est un montage et n'est pas touché. | |
| 133 | + | |
| 134 | +## Voir aussi | |
| 135 | + | |
| 136 | +- Le format suivi par ces deux fichiers : la spécification des kits de Docker Sandboxes (`schemaVersion: "2"`), livrée avec les sources de `sbx`. | |
| 137 | +- Ce que `mm -acp` expose à l'éditeur : [référence ACP](../reference/acp.md). | |
| 138 | +- Les drapeaux et variables d'environnement que `mm` lit : [référence de la ligne de commande](../reference/cli.md). | |
| new file mode 100644 | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | +# Faire tourner mini-me dans un Docker Sandbox | ||
| 2 | + | ||
| 3 | +Ce guide montre comment exécuter `mm` dans un [Docker Sandbox](https://docs.docker.com/ai/sandboxes/), pour que chaque commande lancée par le modèle et chaque fichier qu'il modifie le soient dans un conteneur, sur votre projet monté. Il couvre les deux usages : le mode fenêtre dans votre terminal, et depuis un éditeur via l'Agent Client Protocol. Il suppose Docker Desktop avec la commande `sbx`, et un serveur de modèles (Docker Model Runner ou llama-server) qui tourne sur votre machine. | ||
| 4 | + | ||
| 5 | +Tout est sous [`docker-sbx/`](../../../docker-sbx/README.md) dans le dépôt : | ||
| 6 | + | ||
| 7 | +| Élément | Ce que c'est | | ||
| 8 | +|---|---| | ||
| 9 | +| `template/Dockerfile` | Le **template** : l'image `shell` de Docker Sandboxes avec `mm` compilé depuis ce dépôt, plus les deux points d'entrée ci-dessous. | | ||
| 10 | +| `kits/mm-tui/spec.yaml` | Un **kit sandbox** : `sbx run` dessus ouvre le mode fenêtre de `mm` sur l'espace de travail. | | ||
| 11 | +| `kits/mm-acp/spec.yaml` | Un **kit sandbox** pour un éditeur : créé une fois avec `sbx create`, l'éditeur y lance ensuite `mm -acp`. | | ||
| 12 | +| `build.sh` | Construit l'image du template, `k33g/mm-sandbox:<version>` ; `PUSH=1` la construit pour linux/amd64 et linux/arm64 et la pousse. | | ||
| 13 | +| `sync-kits.sh`, `push-kits.sh` | Alignent les kits sur une image, et les publient sur Docker Hub comme artefacts OCI de kit. | | ||
| 14 | + | ||
| 15 | +## Utiliser les kits publiés | ||
| 16 | + | ||
| 17 | +Chaque release publie l'image et les deux kits sur Docker Hub à la version de mm — `k33g/mm-sandbox:0.0.2`, `k33g/mm-tui:0.0.2`, `k33g/mm-acp:0.0.2` — le chemin le plus court ne demande donc aucune construction. Docker Hub est une source de kits autorisée par défaut (`sbx settings get kit.allowedSources` montre `docker.io/`), et une référence avec un `/` mais sans hôte de registre est lue comme `docker.io/…` : | ||
| 18 | + | ||
| 19 | +```bash | ||
| 20 | +cd /chemin/de/votre/projet | ||
| 21 | +sbx run k33g/mm-tui:0.0.2 . -- agent.dmr.yaml | ||
| 22 | +``` | ||
| 23 | + | ||
| 24 | +pour le mode fenêtre, et pour un éditeur : | ||
| 25 | + | ||
| 26 | +```bash | ||
| 27 | +sbx create --name mm-acp k33g/mm-acp:0.0.2 . --kit-arg config=agent.dmr.yaml | ||
| 28 | +``` | ||
| 29 | + | ||
| 30 | +puis la déclaration dans l'éditeur de l'étape 3 ci-dessous. Pas de `--pull never` : le kit nomme `k33g/mm-sandbox:0.0.2` et sbx la télécharge. `sbx kit inspect k33g/mm-tui:0.0.2` montre ce qu'un kit déclare avant de le lancer. Les étapes qui suivent construisent la même chose en local, pour un mm en cours de travail. | ||
| 31 | + | ||
| 32 | +## Étapes | ||
| 33 | + | ||
| 34 | +### 1. Construire le template | ||
| 35 | + | ||
| 36 | +Depuis la racine du dépôt : | ||
| 37 | + | ||
| 38 | +```bash | ||
| 39 | +./docker-sbx/build.sh | ||
| 40 | +``` | ||
| 41 | + | ||
| 42 | +Cela lit le tag de mm dans `release.env` (`v0.0.2`) et construit `k33g/mm-sandbox:0.0.2` : le tag de l'image est la version sans le `v`, comme les binaires de release, et le `mm` à l'intérieur est estampillé avec le tag — `mm --version` est exécuté dans l'image comme preuve. Le script réécrit ensuite la valeur par défaut de l'argument `tag` des kits à `0.0.2`, pour que kits et image avancent ensemble. `./docker-sbx/build.sh v0.0.3` construit une autre version ; une image antérieure reste joignable avec `--kit-arg tag=0.0.1`. | ||
| 43 | + | ||
| 44 | +L'image est dans votre store d'images Docker local : chaque `sbx run` ou `sbx create` sur ces kits prend `--pull never` tant qu'elle n'est pas poussée (`docker push k33g/mm-sandbox:0.0.2`), sinon `sbx` tente de la télécharger et échoue. Si `sbx` tourne sur une autre machine, `EXPORT=1 ./docker-sbx/build.sh` écrit aussi `tmp/mm-sandbox-0.0.2.tar`, à charger là-bas avec `sbx template load`. | ||
| 45 | + | ||
| 46 | +### 2. Mode fenêtre dans le terminal (kit `mm-tui`) | ||
| 47 | + | ||
| 48 | +Depuis le projet sur lequel `mm` doit travailler : | ||
| 49 | + | ||
| 50 | +```bash | ||
| 51 | +sbx run --pull never ./docker-sbx/kits/mm-tui . -- agent.dmr.yaml | ||
| 52 | +``` | ||
| 53 | + | ||
| 54 | +La première fois, `sbx` crée le sandbox, monte le répertoire courant comme espace de travail et s'y attache ; puis il lance le point d'entrée du kit avec ce qui suit `--` en arguments — ici `mm -tui agent.dmr.yaml`, dans l'espace de travail. N'importe quel drapeau de `mm` passe de la même façon : `-- -config autre.yaml`. | ||
| 55 | + | ||
| 56 | +Sans rien après `--`, `mm` lit l'argument `config` du kit, puis `./agent.yaml` dans l'espace de travail, puis ses valeurs par défaut : | ||
| 57 | + | ||
| 58 | +```bash | ||
| 59 | +sbx run --pull never ./docker-sbx/kits/mm-tui . --kit-arg config=agent.dmr.yaml | ||
| 60 | +``` | ||
| 61 | + | ||
| 62 | +| `--kit-arg` | Défaut | Effet | | ||
| 63 | +|---|---|---| | ||
| 64 | +| `config=<chemin>` | vide | Le fichier de configuration, comme `AGENT_CONFIG` de `mm` : relatif à l'espace de travail, ou absolu dans le sandbox. | | ||
| 65 | +| `tag=<version>` | la dernière version construite (`0.0.2`) | L'image `k33g/mm-sandbox:<version>` dont le sandbox est fait. | | ||
| 66 | + | ||
| 67 | +Le sandbox garde son nom (`mm-tui-<répertoire>` sauf `--name`) ; `sbx run --name <nom>` s'y rattache plus tard. À l'intérieur, la fenêtre est celle décrite dans [Utiliser le mode fenêtre](use-the-window-mode.md). Les sessions, quand `sessions.enabled` est actif, sont écrites sous `.mm/sessions` dans l'espace de travail — c'est votre répertoire hôte, elles survivent donc au sandbox. | ||
| 68 | + | ||
| 69 | +### 3. Depuis un éditeur (kit `mm-acp`) | ||
| 70 | + | ||
| 71 | +Un éditeur parle à `mm -acp` sur l'entrée et la sortie standard brutes de l'agent. `sbx run` ne peut pas porter cela : il alloue toujours un terminal. Le sandbox est donc créé une fois sans s'y attacher, et l'éditeur y lance `mm` avec `sbx exec -i`, qui transmet stdin et stdout tels quels. | ||
| 72 | + | ||
| 73 | +1. Depuis le projet que l'éditeur ouvrira, créez le sandbox avec le fichier de configuration intégré (`.` est l'espace de travail, sbx le rend absolu ; le chemin du kit doit être explicite : `./`, `../` ou absolu) : | ||
| 74 | + | ||
| 75 | + ```bash | ||
| 76 | + cd /chemin/de/votre/projet | ||
| 77 | + sbx create --pull never --name mm-acp /chemin/de/mini-me/docker-sbx/kits/mm-acp . \ | ||
| 78 | + --kit-arg config=agent.dmr.yaml | ||
| 79 | + ``` | ||
| 80 | + | ||
| 81 | + Un chemin d'espace de travail qui n'existe pas sur votre machine fait demander à sbx « The selected workspace does not exist. Would you like to create it? » — répondez `N` et corrigez le chemin. | ||
| 82 | + | ||
| 83 | + `--kit-arg config` devient l'`AGENT_CONFIG` du conteneur, relatif à l'espace de travail parce que c'est là que `sbx exec` démarre `mm`. L'espace de travail est fixé à la création : un sandbox par projet, chacun avec son `--name`. | ||
| 84 | + | ||
| 85 | +2. Déclarez-le dans l'éditeur. Dans le `settings.json` de Zed : | ||
| 86 | + | ||
| 87 | + ```json | ||
| 88 | + { | ||
| 89 | + "agent_servers": { | ||
| 90 | + "mini-me (sandbox)": { | ||
| 91 | + "type": "custom", | ||
| 92 | + "command": "sbx", | ||
| 93 | + "args": ["exec", "-i", "mm-acp", "mm", "-acp"] | ||
| 94 | + } | ||
| 95 | + } | ||
| 96 | + } | ||
| 97 | + ``` | ||
| 98 | + | ||
| 99 | + Par rapport à [la configuration côté hôte](use-from-an-editor.md), la commande est `sbx` et aucun `env` n'est nécessaire : `AGENT_CONFIG` est déjà dans le conteneur. Si l'éditeur ne trouve pas `sbx`, mettez le chemin absolu donné par `which sbx` dans `"command"`. | ||
| 100 | + | ||
| 101 | +3. Ouvrez le panneau agent et choisissez **mini-me (sandbox)**. L'éditeur lance `mm -acp` dans le sandbox à chaque conversation ; `sbx exec` démarre d'abord le sandbox s'il est arrêté. Appels d'outils, dialogues de permission, mentions `@fichier` et menu des commandes fonctionnent comme sur l'hôte — la différence est l'endroit où les commandes s'exécutent. | ||
| 102 | + | ||
| 103 | +S'attacher à ce sandbox avec `sbx run --name mm-acp` ne lance pas `mm` : son point d'entrée affiche le câblage ci-dessus avec le vrai nom et les vrais chemins du sandbox, puis ouvre un shell à l'intérieur. `sbx exec -it mm-acp bash` est le chemin le plus court vers ce shell. | ||
| 104 | + | ||
| 105 | +## Le serveur de modèles reste sur l'hôte | ||
| 106 | + | ||
| 107 | +Dans un sandbox, `localhost` est le sandbox lui-même. Votre serveur de modèles continue de tourner sur votre machine et se joint par `host.docker.internal`, que le proxy du sandbox fait pointer vers le `localhost` de l'hôte. | ||
| 108 | + | ||
| 109 | +- **Docker Model Runner** — rien à changer : `agent.dmr.yaml` a déjà `fallback: http://host.docker.internal:12434/engines/v1`, et `mm` bascule dessus quand `localhost:12434` ne répond pas (la bannière dit quelle URL a gagné). | ||
| 110 | +- **llama-server** — `agent.llamacpp.yaml` n'a pas de fallback. Soit vous changez son `baseUrl` en `http://host.docker.internal:8080/v1`, soit vous passez `--env AGENT_BASE_URL=http://host.docker.internal:8080/v1` à `sbx run` / `sbx create` (voir [Faire tourner sur llama.cpp](run-with-llama-cpp.md)). | ||
| 111 | + | ||
| 112 | +Les deux kits autorisent les ports 12434 et 8080 de l'hôte dans la politique réseau du sandbox. Pour un autre port : `sbx policy allow network host.docker.internal:<port>`. | ||
| 113 | + | ||
| 114 | +## Publier une version | ||
| 115 | + | ||
| 116 | +Le workflow Release (`.github/workflows/release.yml`, job `docker-sandbox`) publie l'image et les kits à chaque tag `v*`, après le job des binaires. Il a besoin de deux secrets du dépôt, exposés au job comme variables d'environnement : `DOCKER_HANDLE`, le compte Docker Hub (aussi le propriétaire de l'image), et `DOCKER_PAT`, un jeton d'accès en lecture/écriture. Il lance ensuite les scripts qu'un portable lancerait : | ||
| 117 | + | ||
| 118 | +```bash | ||
| 119 | +docker login -u k33g # coller un jeton d'accès | ||
| 120 | +PUSH=1 ./docker-sbx/build.sh v0.0.2 # buildx, linux/amd64 + linux/arm64, poussée comme k33g/mm-sandbox:0.0.2 | ||
| 121 | +./docker-sbx/push-kits.sh v0.0.2 # sbx kit validate + sbx kit push → k33g/mm-tui:0.0.2, k33g/mm-acp:0.0.2 | ||
| 122 | +``` | ||
| 123 | + | ||
| 124 | +`push-kits.sh` a besoin de la CLI `sbx` sur le PATH (ou `SBX=/chemin/vers/sbx` ; le workflow télécharge l'archive Linux de `docker/sbx-releases`). `sbx kit push` parle directement au registre et s'authentifie depuis le magasin d'identifiants Docker — aucun démon sandbox n'intervient, c'est ce qui le rend utilisable en CI. Les deux scripts appellent d'abord `sync-kits.sh`, qui réécrit `version`, le `tag` par défaut et le propriétaire de l'image des kits, de sorte qu'un kit publié nomme toujours l'image poussée avec lui. `IMAGE_OWNER=<compte>` publie sous un autre compte. | ||
| 125 | + | ||
| 126 | +## Variantes | ||
| 127 | + | ||
| 128 | +- **Une autre configuration pour une session d'éditeur.** Ajoutez `"-e", "AGENT_CONFIG=agent.lsp.yaml"` avant le nom du sandbox dans `"args"` ; `sbx exec -e` remplace la valeur intégrée pour cette exécution seulement. | ||
| 129 | +- **Skills.** `skillsDir` est relatif au répertoire où `mm` tourne, c'est-à-dire l'espace de travail dans les deux kits — la même règle que sur l'hôte. | ||
| 130 | +- **Docker dans le sandbox.** Construisez le template sur le shell Docker-in-Docker : `SHELL_TEMPLATE=docker/sandbox-templates:shell-docker ./docker-sbx/build.sh`. | ||
| 131 | +- **Lire les logs.** En mode fenêtre ils sont à l'écran ; en ACP ils vont sur stderr, que Zed montre sous `dev: open acp logs`. | ||
| 132 | +- **Supprimer un sandbox.** `sbx rm mm-acp`. Le répertoire du projet est un montage et n'est pas touché. | ||
| 133 | + | ||
| 134 | +## Voir aussi | ||
| 135 | + | ||
| 136 | +- Le format suivi par ces deux fichiers : la spécification des kits de Docker Sandboxes (`schemaVersion: "2"`), livrée avec les sources de `sbx`. | ||
| 137 | +- Ce que `mm -acp` expose à l'éditeur : [référence ACP](../reference/acp.md). | ||
| 138 | +- Les drapeaux et variables d'environnement que `mm` lit : [référence de la ligne de commande](../reference/cli.md). | ||