Development
Docker, Make and Bash are enough for everything except the authenticated end-to-end suite. make help lists every target.
Layout
| Path | What it is |
|---|---|
Dockerfile, Dockerfile.full | The minimal and full images, on psyb0t/aicodebox:v0.18.4 and :latest-full |
codexbox/ | The Python package with CodexAdapter, its unit tests, and init.d/ |
codexbox-entrypoint.sh | Copies CODEXBOX_* settings to AICODEBOX_*, then runs the base entrypoint |
codexbox-agent.sh | What the shell path runs: adds the bypass flag and session continuation around codex |
wrapper.sh, install.sh | The host command and its installer |
test.sh, tests/ | The end-to-end runner and the shell test scripts |
.agents/ | The agent skill, plugin manifests and MCP bridge |
docs/ | This site |
Building
make build # psyb0t/codexbox:latest and :v<version>, on psyb0t/aicodebox:v0.18.4
make build-full # psyb0t/codexbox:latest-full and :v<version>-full, on :latest-full
make build-all # both
make clean # remove the images these targets built
Both builds pull the published aicodebox base first. To build on a base you built yourself, tag it as the base name and skip the pull:
SKIP_BASE_PULL=1 make build
SKIP_BASE_PULL=1 BASE_IMAGE=aicodebox-base:local make build
CODEX_VERSION (default in the Makefile and both Dockerfiles) is the npm version of @openai/codex the images install. Change all three together.
Linting
make lint # all of the below
make lint-shell # shellcheck and shfmt (-i 4 -ci) over the first-party shell scripts
make lint-python # ruff check, ruff format --check and pyright over codexbox/
Every linter runs in a container. The shell linters use pinned koalaman/shellcheck and mvdan/shfmt images. The Python checks run in the aicodebox base image, which already has the aicodebox package the adapter imports, with ruff, pyright and pytest installed into a throwaway virtual environment at the versions pinned in codexbox/pyproject.toml. The checkout is mounted read-only.
Tests
| Target | Needs | What it checks |
|---|---|---|
make test-unit-ci | Docker | The adapter unit tests in codexbox/tests/, in the base image |
make test-unit | uv and ../docker-aicodebox checked out next to this repository | The same unit tests, from the locked development environment |
make test-image-select | Bash | The installer and wrapper pick, pull and bake the right image for CODEXBOX_FULL |
make test-managed-nested | Bash, Make | Managed installs, the release-pinned wrapper download and the nested-launch context |
make test-entrypoint-identity | Docker | CODEXBOX_GIT_* and AICODEBOX_GIT_* through the real base entrypoint |
make test-e2e-runner | Bash | test.sh loads only end-to-end test files, and .env.test.example names only tests that exist |
make test-agent-defaults | Bash | CODEXBOX_MODEL and CODEXBOX_REASONING_EFFORT reach Codex on the shell path, and the caller’s flags still win |
make test-mcp-bridge | Docker | The URL the OpenClaw MCP bridge connects to, for API mode and a separate MCP server |
make test-scratch-paths | Git | No tracked file points at the local planning directory |
make test-ci | Docker | Every target above except test-unit. This is what CI runs. |
make test-full-image | Docker | Builds the full image and checks its variant marker, Codex, the Go and Python versions and every advertised tool |
make test | Docker, .env.test | The end-to-end suite against a real OpenAI or Codex endpoint |
The image-select, managed-install, e2e runner and agent defaults tests replace docker, sudo, curl or codex with fakes, so they need neither a daemon nor the network.
For the end-to-end suite, copy the example and fill it in:
cp .env.test.example .env.test
$EDITOR .env.test # OPENAI_API_KEY, or a CODEXBOX_DATA_DIR with a subscription login
make test
test.sh builds codexbox:local on the published base, runs the tests and removes the image. KEEP_IMAGE=1 keeps it, SKIP_BUILD=1 reuses it and SKIP_BASE_PULL=1 skips the base pull. ./test.sh test_smoke_codex runs one test. It sources only the tests/test_*.sh files that register tests in ALL_TESTS; the other scripts there are standalone and run through their own make targets. Set CODEXBOX_MODEL in .env.test to a small model, because an empty value means the account default.
CI
.github/workflows/pipeline.yml runs on every push:
- Any branch:
make lintandmake test-cion a GitHub runner. They need no credentials and no.env.test. main: after those pass, both variants are built forlinux/amd64andlinux/arm64, scanned with Grype (findings go to the Security tab without failing the run), and pushed as:latest,:latest-fulland per-commit SHA tags. The version and license badges are refreshed.- A
v*tag: waits for the tagged commit’smainrun to succeed, promotes those exact images to:vX.Y.Zand:vX.Y.Z-fullwithout rebuilding, creates the GitHub release from the tag’s notes, refreshes the badges, then publishes the skill and the MCP bridge to ClawHub andserver.jsonto the MCP Registry.
Releasing
make version # print the current tag, v<version>
make version V=0.7.7 # set it everywhere, without committing or tagging
make version V=... updates codexbox/pyproject.toml and uv.lock, the Codex plugin manifest and the wrapper ref in install.sh. Add a CHANGELOG.md entry, commit, then push an annotated v* tag after main is green.
Documentation
docs/ is this site, built by GitHub Pages from main with Jekyll and the just-the-docs theme.
make docs # build into .testing/docs-site with GitHub's own Pages image
Every page needs front matter with a unique title and a description of 50 to 170 characters. Link between pages with the relative .md path. Link a file outside docs/ with its full https://github.com/psyb0t/docker-codexbox/blob/main/... URL, because a relative ../ link breaks on the site.