chore: initial commit

This commit is contained in:
Björn Fromme committed 2026-09-16 12:37:50 +02:00
commit 63f3199076
9 files changed
+567

No files matched your search

+185
View File
@@ -0,0 +1,185 @@
# ddev-codex-claude-cli
`ddev-codex-claude-cli` is a DDEV add-on that runs Codex CLI and Claude Code CLI inside the DDEV `web` container while keeping runtime state under the project's `.ddev` directory on the host (bind-mounted into the container).
## What it does
- exposes `ddev codex`
- exposes `ddev claude`
- optionally exposes `ddev claude-swap` for juggling multiple Claude accounts (opt-in; see [Multiple Claude accounts](#multiple-claude-accounts))
- installs Codex CLI lazily the first time you run `ddev codex` (or whenever the persisted binary is missing) by downloading the latest release from GitHub: `curl -fsSL https://github.com/openai/codex/releases/latest/download/install.sh | sh`
- installs Claude Code CLI during `ddev start` when the persisted binary is missing or broken by downloading the stable native build: `curl -fsSL https://claude.ai/install.sh | bash -s stable`
- keeps normal `ddev start` fast by skipping Claude Code installation when the persisted binary is already runnable
- persists each CLI's binary, HOME, and XDG paths under **project-local** storage so each DDEV project has its own profiles
- seeds the project-local HOME from the container home on first bootstrap for `.gitignore_global` when present
- always runs Codex with `--sandbox danger-full-access --ask-for-approval untrusted`
## Runtime storage
Inside the web container, Codex CLI state lives under:
${DDEV_APPROOT}/.ddev/codex-cli
Claude Code CLI state lives under:
${DDEV_APPROOT}/.ddev/claude-code
On the host, those are your project's **`.ddev/codex-cli/`** and **`.ddev/claude-code/`** directories. They survive `ddev restart`, `ddev stop`, and `ddev poweroff` like any other project file. The same directories hold the add-on's **install scripts** (managed files) alongside runtime data created after the first start.
Codex layout:
| Path | Role |
|------|------|
| `.ddev/codex-cli/install.sh` | Codex install script |
| `.ddev/codex-cli/bin/codex` | Codex CLI binary |
| `.ddev/codex-cli/home` | `HOME` |
| `.ddev/codex-cli/config` | `XDG_CONFIG_HOME` |
| `.ddev/codex-cli/data` | `XDG_DATA_HOME` |
| `.ddev/codex-cli/state` | `XDG_STATE_HOME` |
Claude Code layout:
| Path | Role |
|------|------|
| `.ddev/claude-code/install.sh` | Claude Code install script |
| `.ddev/claude-code/home/.local/bin/claude` | Claude Code CLI binary |
| `.ddev/claude-code/home` | `HOME` |
| `.ddev/claude-code/config` | `XDG_CONFIG_HOME` |
| `.ddev/claude-code/data` | `XDG_DATA_HOME` |
| `.ddev/claude-code/state` | `XDG_STATE_HOME` |
Authentication and settings are **per project**: sign in once per project; they persist across container restarts.
## Install
From a DDEV project root:
ddev add-on get /path/to/ddev-codex-claude-cli
Then restart:
ddev restart
## Usage
ddev codex
ddev codex --version
ddev claude
ddev claude --version
ddev claude update
ddev claude-swap list # opt-in; see "Multiple Claude accounts"
## Updates
Codex CLI is installed lazily when `ddev codex` runs and the persisted binary is missing or broken.
Claude Code CLI is installed automatically on `ddev start` only when the persisted binary is missing or broken. Later starts run a fast health check and skip installation when Claude is already runnable.
To check for and install a newer Claude Code stable release, run:
ddev claude update
## Multiple Claude accounts
The add-on can integrate [`claude-swap`](https://github.com/realiti4/claude-swap)
(`cswap`) so one project can hold several Claude accounts and swap between them —
mainly to keep working past a 5-hour or weekly rate limit. It is **opt-in**: nothing
is installed during `ddev start`; the first `ddev claude-swap` call (or first
`ddev claude --account …`) installs `uv` and `claude-swap` into the project-local
Claude profile under `.ddev/claude-code/`.
Accounts are stored **per project**, next to the rest of the Claude state:
.ddev/claude-code/data/claude-swap/
They persist across restarts and are deleted when the add-on is removed. Back them up
first with `ddev claude-swap export backup.cswap` if you need to keep them.
### Swapping accounts without losing your session
A running `ddev claude` conversation is not tied to the auth token, and Claude Code
re-reads its credentials file whenever it changes. So:
1. Terminal 1: you are in `ddev claude`, working, and hit (or approach) the limit.
2. Terminal 2 (on the host): `ddev claude-swap switch <n|email|alias>`
(or `ddev claude-swap switch --strategy best`).
3. Back in terminal 1: send the next message — it goes out on the new account, in the
**same session** with all its context. If a request just failed with the limit
error, resend it.
To automate step 2, run `ddev claude-swap auto` in a spare terminal (or an
`ddev ssh` shell); it watches usage and switches around 90%.
### One-time setup
ddev claude # sign in as your first account
ddev claude-swap add --alias a # capture it
ddev claude # /login inside Claude Code as the next account, then exit
ddev claude-swap add --alias b # capture it
ddev claude-swap list # all accounts with usage and reset times
ddev claude-swap status # currently active account
`ddev claude-swap` forwards every argument straight to `cswap`, so any `cswap`
subcommand works (`switch`, `run`, `map`, `disable`, `export`, `import`, `tui`, …).
`ddev claude-swap upgrade` updates `claude-swap` to the latest release.
### Starting a session pinned to an account
Separately from the hot-swap flow, you can launch a session bound to one account
(with its own per-account chat history):
ddev claude --account b
ddev claude --account b --resume # remaining args pass straight to Claude Code
Set a project default so plain `ddev claude` always uses it:
echo b > .ddev/claude-code/claude-swap-account
# or export DDEV_CLAUDE_SWAP_ACCOUNT=b
Remove the file (or unset the variable) to go back to the normal single-account
launch.
### Requirements
The first install needs outbound network access to `astral.sh`, `pypi.org`,
`files.pythonhosted.org`, and `github.com` (for the `uv`-managed Python 3.12).
`claude-swap` captures whichever account is signed in at `cswap add` time, so the
initial multi-account setup is a few manual `/login` round-trips.
## Installed managed files
.ddev/config.codex-cli.yaml
.ddev/config.claude-code.yaml
.ddev/commands/web/codex
.ddev/commands/web/claude
.ddev/commands/web/claude-swap
.ddev/codex-cli/install.sh
.ddev/claude-code/install.sh
.ddev/claude-code/install-swap.sh
## Notes
- the first `ddev codex` in a project can take some time because it downloads the Codex CLI binary; later runs reuse `.ddev/codex-cli/bin/codex`
- the first `ddev start` after installing the add-on can take some time because it downloads Claude Code; later starts reuse `.ddev/claude-code/home/.local/bin/claude`
- if the container home has `.gitignore_global` (typically from host `~/.ddev/homeadditions/.gitignore_global`), it is copied into each project-local CLI `HOME` on first bootstrap; git's `core.excludesfile` is pointed at that project-local copy
- if you do not want add-on or CLI runtime files in your repository, ignore them in git, for example:
/.ddev/addon-metadata/ddev-codex-claude-cli
/.ddev/config.codex-cli.yaml
/.ddev/config.claude-code.yaml
/.ddev/commands/web/codex
/.ddev/commands/web/claude
/.ddev/commands/web/claude-swap
/.ddev/codex-cli/
/.ddev/claude-code/
Removing the add-on deletes `.ddev/codex-cli/` and `.ddev/claude-code/` as well (local auth, config, and CLI binaries). Back up those directories first if you need to keep them.
## macOS / Linux / WSL2
This add-on runs Codex CLI and Claude Code CLI in the DDEV container, so host OS differences are minimized as long as DDEV works normally.
+85
View File
@@ -0,0 +1,85 @@
#!/usr/bin/env bash
#ddev-generated
set -euo pipefail
# Lazy installer for claude-swap (cswap): a multi-account manager for Claude Code.
# It is NOT installed during "ddev start" - the first "ddev claude-swap ..." call
# (or "ddev claude --account ...") runs this script, which installs uv and then
# "claude-swap" into the same project-local, persisted profile that the Claude
# Code CLI uses. Pass --upgrade to update an existing install.
#
# All state lives under .ddev/claude-code so it survives container rebuilds
# without being committed to the project repository, exactly like install.sh.
PERSIST_BASE="${DDEV_APPROOT}/.ddev/claude-code"
PERSIST_HOME="${PERSIST_BASE}/home" # replaces $HOME inside the container
PERSIST_CONFIG_HOME="${PERSIST_BASE}/config" # XDG_CONFIG_HOME
PERSIST_DATA_HOME="${PERSIST_BASE}/data" # XDG_DATA_HOME
PERSIST_STATE_HOME="${PERSIST_BASE}/state" # XDG_STATE_HOME
PERSIST_BIN_DIR="${PERSIST_HOME}/.local/bin" # shared with the claude launcher
UV_BIN="${PERSIST_BIN_DIR}/uv"
CSWAP_BIN="${PERSIST_BIN_DIR}/cswap"
mkdir -p \
"${PERSIST_HOME}" \
"${PERSIST_CONFIG_HOME}" \
"${PERSIST_DATA_HOME}" \
"${PERSIST_STATE_HOME}" \
"${PERSIST_BIN_DIR}"
# Point HOME / XDG paths at the persisted profile before any install or health
# check so cswap's own data (Linux: ${XDG_DATA_HOME}/claude-swap) is per-project
# and survives restarts with no extra configuration.
export HOME="${PERSIST_HOME}"
export XDG_CONFIG_HOME="${PERSIST_CONFIG_HOME}"
export XDG_DATA_HOME="${PERSIST_DATA_HOME}"
export XDG_STATE_HOME="${PERSIST_STATE_HOME}"
export PATH="${PERSIST_BIN_DIR}:${PATH}"
# Keep every artifact uv creates inside the persisted tree.
export UV_INSTALL_DIR="${PERSIST_BIN_DIR}" # uv / uvx binaries
export UV_TOOL_BIN_DIR="${PERSIST_BIN_DIR}" # tool entry points (cswap)
export UV_TOOL_DIR="${XDG_DATA_HOME}/uv/tools" # tool virtualenvs
export UV_PYTHON_INSTALL_DIR="${XDG_DATA_HOME}/uv/python" # uv-managed CPython
export UV_CACHE_DIR="${XDG_DATA_HOME}/uv/cache" # download / build cache
# Remove an unrunnable persisted uv (e.g. wrong arch after a host OS change).
if [ -x "${UV_BIN}" ] && ! "${UV_BIN}" --version >/dev/null 2>&1; then
echo "Persisted uv binary is not runnable, reinstalling ..."
rm -f "${UV_BIN}" "${PERSIST_BIN_DIR}/uvx"
fi
# Install uv when missing. uv fetches its own CPython 3.12 below, so the DDEV web
# image's system Python (3.11) is never relied upon. Do not let the installer
# edit shell rc files in the persisted HOME - PATH is set by the launchers.
if [ ! -x "${UV_BIN}" ]; then
echo "Installing uv ..."
curl -fsSL https://astral.sh/uv/install.sh | env INSTALLER_NO_MODIFY_PATH=1 sh
fi
if [ ! -x "${UV_BIN}" ]; then
echo "uv installation completed but no executable was found at ${UV_BIN}." >&2
echo "If outbound access to astral.sh is blocked this step cannot succeed." >&2
exit 1
fi
if [ "${1:-}" = "--upgrade" ]; then
echo "Upgrading claude-swap ..."
"${UV_BIN}" tool upgrade claude-swap
echo "claude-swap version: $("${CSWAP_BIN}" --version)"
exit 0
fi
# Install claude-swap when missing or unrunnable.
if [ ! -x "${CSWAP_BIN}" ] || ! "${CSWAP_BIN}" --version >/dev/null 2>&1; then
echo "Installing claude-swap ..."
"${UV_BIN}" tool install --force --python 3.12 claude-swap
fi
if [ ! -x "${CSWAP_BIN}" ] || ! "${CSWAP_BIN}" --version >/dev/null 2>&1; then
echo "claude-swap installation completed but ${CSWAP_BIN} is not runnable." >&2
echo "First install needs outbound access to astral.sh, pypi.org," >&2
echo "files.pythonhosted.org and github.com (uv-managed CPython)." >&2
exit 1
fi
echo "claude-swap version: $("${CSWAP_BIN}" --version)"
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env bash
#ddev-generated
set -euo pipefail
# All Claude Code state is stored under .ddev/claude-code so it survives
# container rebuilds without being committed to the project repository.
PERSIST_BASE="${DDEV_APPROOT}/.ddev/claude-code"
PERSIST_HOME="${PERSIST_BASE}/home" # replaces $HOME inside the container
PERSIST_CONFIG_HOME="${PERSIST_BASE}/config" # XDG_CONFIG_HOME (e.g. ~/.config)
PERSIST_DATA_HOME="${PERSIST_BASE}/data" # XDG_DATA_HOME (e.g. ~/.local/share)
PERSIST_STATE_HOME="${PERSIST_BASE}/state" # XDG_STATE_HOME (e.g. ~/.local/state)
PERSIST_BIN_DIR="${PERSIST_HOME}/.local/bin" # Claude native installer target
PERSIST_BIN="${PERSIST_BIN_DIR}/claude" # full path to the claude executable
PERSIST_GITIGNORE_GLOBAL="${PERSIST_HOME}/.gitignore_global" # mirrors $HOME/.gitignore_global
# Capture the container's original HOME before we override it below, so we can
# seed the persisted gitignore from the container image on first run.
CONTAINER_HOME="${HOME:?HOME must be set}"
CONTAINER_GITIGNORE_GLOBAL="${CONTAINER_HOME}/.gitignore_global"
# Ensure all persistence directories exist.
mkdir -p \
"${PERSIST_HOME}" \
"${PERSIST_CONFIG_HOME}" \
"${PERSIST_DATA_HOME}" \
"${PERSIST_STATE_HOME}" \
"${PERSIST_BIN_DIR}"
# Seed the persisted gitignore from the container image on first run only.
if [ -f "${CONTAINER_GITIGNORE_GLOBAL}" ] && [ ! -f "${PERSIST_GITIGNORE_GLOBAL}" ]; then
cp -f "${CONTAINER_GITIGNORE_GLOBAL}" "${PERSIST_GITIGNORE_GLOBAL}"
fi
# Claude Code's native installer stores its launcher, auth, settings, downloads,
# and versioned package data relative to HOME/XDG paths.
export HOME="${PERSIST_HOME}"
export XDG_CONFIG_HOME="${PERSIST_CONFIG_HOME}"
export XDG_DATA_HOME="${PERSIST_DATA_HOME}"
export XDG_STATE_HOME="${PERSIST_STATE_HOME}"
export PATH="${PERSIST_BIN_DIR}:${PATH}"
# If a persisted binary exists but fails to run (e.g. wrong architecture after
# a host OS change), remove it so the install step below fetches a fresh copy.
if [ -x "${PERSIST_BIN}" ] && ! "${PERSIST_BIN}" --version >/dev/null 2>&1; then
echo "Persisted Claude Code binary is not runnable, reinstalling ..."
rm -f "${PERSIST_BIN}"
fi
# Download and install the current stable Claude Code native build only when
# missing. Use "ddev claude update" to check for and install newer versions.
if [ ! -x "${PERSIST_BIN}" ]; then
echo "Installing Claude Code CLI ..."
curl -fsSL https://claude.ai/install.sh | bash -s stable
fi
# Verify the binary is present and runnable after installation.
if [ ! -x "${PERSIST_BIN}" ]; then
echo "Claude Code installation completed but no executable was found at ${PERSIST_BIN}." >&2
exit 1
fi
if ! "${PERSIST_BIN}" --version >/dev/null 2>&1; then
echo "Claude Code installation completed but ${PERSIST_BIN} is not runnable." >&2
exit 1
fi
# Point git's global excludes file at the persisted copy when available,
# so git inside the container respects user-defined ignore patterns.
if command -v git >/dev/null 2>&1 && [ -f "${PERSIST_GITIGNORE_GLOBAL}" ]; then
CURRENT_EXCLUDESFILE="$(git config --global --get core.excludesfile || true)"
if [ "${CURRENT_EXCLUDESFILE}" != "${PERSIST_GITIGNORE_GLOBAL}" ]; then
git config --global core.excludesfile "${PERSIST_GITIGNORE_GLOBAL}"
fi
fi
echo "Claude Code version: $("${PERSIST_BIN}" --version)"
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
#ddev-generated
set -euo pipefail
# All Codex CLI state is stored under .ddev/codex-cli so it survives container
# rebuilds without being committed to the project repository.
PERSIST_BASE="${DDEV_APPROOT}/.ddev/codex-cli"
PERSIST_HOME="${PERSIST_BASE}/home" # replaces $HOME inside the container
PERSIST_CONFIG_HOME="${PERSIST_BASE}/config" # XDG_CONFIG_HOME (e.g. ~/.config)
PERSIST_DATA_HOME="${PERSIST_BASE}/data" # XDG_DATA_HOME (e.g. ~/.local/share)
PERSIST_STATE_HOME="${PERSIST_BASE}/state" # XDG_STATE_HOME (e.g. ~/.local/state)
PERSIST_BIN_DIR="${PERSIST_BASE}/bin" # directory that holds the codex binary
PERSIST_BIN="${PERSIST_BIN_DIR}/codex" # full path to the codex executable
PERSIST_GITIGNORE_GLOBAL="${PERSIST_HOME}/.gitignore_global" # mirrors $HOME/.gitignore_global
# Capture the container's original HOME before we override it below, so we can
# seed the persisted gitignore from the container image on first run.
CONTAINER_HOME="${HOME:?HOME must be set}"
CONTAINER_GITIGNORE_GLOBAL="${CONTAINER_HOME}/.gitignore_global"
# Ensure all persistence directories exist.
mkdir -p \
"${PERSIST_HOME}" \
"${PERSIST_CONFIG_HOME}" \
"${PERSIST_DATA_HOME}" \
"${PERSIST_STATE_HOME}" \
"${PERSIST_BIN_DIR}"
# Seed the persisted gitignore from the container image on first run only.
if [ -f "${CONTAINER_GITIGNORE_GLOBAL}" ] && [ ! -f "${PERSIST_GITIGNORE_GLOBAL}" ]; then
cp -f "${CONTAINER_GITIGNORE_GLOBAL}" "${PERSIST_GITIGNORE_GLOBAL}"
fi
# Codex stores its standalone package tree under $HOME/.codex and links
# $CODEX_INSTALL_DIR/codex into that tree. Point HOME there before any install
# or health check so package data survives container restarts.
export HOME="${PERSIST_HOME}"
export CODEX_HOME="${PERSIST_HOME}/.codex"
export XDG_CONFIG_HOME="${PERSIST_CONFIG_HOME}"
export XDG_DATA_HOME="${PERSIST_DATA_HOME}"
export XDG_STATE_HOME="${PERSIST_STATE_HOME}"
export PATH="${PERSIST_BIN_DIR}:${HOME}/.local/bin:${PATH}"
# If a persisted binary exists but fails to run (e.g. wrong architecture after
# a host OS change), remove it so the install step below fetches a fresh copy.
if [ -x "${PERSIST_BIN}" ] && ! "${PERSIST_BIN}" --version >/dev/null 2>&1; then
echo "Persisted Codex CLI binary is not runnable, reinstalling ..."
rm -f "${PERSIST_BIN}"
fi
# Download and install the Codex CLI into the persisted bin directory.
if [ ! -x "${PERSIST_BIN}" ]; then
echo "Installing Codex CLI ..."
export CODEX_INSTALL_DIR="${PERSIST_BIN_DIR}"
export CODEX_NON_INTERACTIVE=1
curl -fsSL https://github.com/openai/codex/releases/latest/download/install.sh | sh
fi
# Verify the binary is present after installation.
if [ ! -x "${PERSIST_BIN}" ]; then
echo "Codex CLI installation completed but no executable was found at ${PERSIST_BIN}." >&2
exit 1
fi
# Point git's global excludes file at the persisted copy when available,
# so git inside the container respects user-defined ignore patterns.
if command -v git >/dev/null 2>&1 && [ -f "${PERSIST_GITIGNORE_GLOBAL}" ]; then
CURRENT_EXCLUDESFILE="$(git config --global --get core.excludesfile || true)"
if [ "${CURRENT_EXCLUDESFILE}" != "${PERSIST_GITIGNORE_GLOBAL}" ]; then
git config --global core.excludesfile "${PERSIST_GITIGNORE_GLOBAL}"
fi
fi
echo "Codex CLI version: $("${PERSIST_BIN}" --version)"
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
#ddev-generated
## Description: Run Claude Code CLI inside the web container
## Usage: claude [--account <n|email|alias>]
## Example: "ddev claude --version"
set -euo pipefail
INSTALL_SCRIPT="${DDEV_APPROOT}/.ddev/claude-code/install.sh"
SWAP_INSTALL_SCRIPT="${DDEV_APPROOT}/.ddev/claude-code/install-swap.sh"
PERSIST_BASE="${DDEV_APPROOT}/.ddev/claude-code"
PERSIST_HOME="${PERSIST_BASE}/home"
PERSIST_BIN="${PERSIST_HOME}/.local/bin/claude"
CSWAP_BIN="${PERSIST_HOME}/.local/bin/cswap"
# Optional multi-account routing: "ddev claude --account <sel>" starts a session
# pinned to a claude-swap account via "cswap run". With no flag, a project
# default can come from $DDEV_CLAUDE_SWAP_ACCOUNT or the first line of
# .ddev/claude-code/claude-swap-account. When neither is set this behaves
# exactly like a plain Claude Code launch.
ACCOUNT=""
ARGS=()
while [ $# -gt 0 ]; do
case "$1" in
--account=*) ACCOUNT="${1#--account=}"; shift ;;
--account|-A)
if [ $# -lt 2 ]; then
echo "ddev claude: --account requires an argument" >&2
exit 1
fi
ACCOUNT="$2"; shift 2 ;;
-A*) ACCOUNT="${1#-A}"; shift ;;
*) ARGS+=("$1"); shift ;;
esac
done
if [ -z "${ACCOUNT}" ]; then
if [ -n "${DDEV_CLAUDE_SWAP_ACCOUNT:-}" ]; then
ACCOUNT="${DDEV_CLAUDE_SWAP_ACCOUNT}"
elif [ -f "${PERSIST_BASE}/claude-swap-account" ]; then
ACCOUNT="$(head -n1 "${PERSIST_BASE}/claude-swap-account" | tr -d '[:space:]')"
fi
fi
"${INSTALL_SCRIPT}"
export HOME="${PERSIST_HOME}"
export XDG_CONFIG_HOME="${PERSIST_BASE}/config"
export XDG_DATA_HOME="${PERSIST_BASE}/data"
export XDG_STATE_HOME="${PERSIST_BASE}/state"
export PATH="${PERSIST_HOME}/.local/bin:${PATH}"
unset SSH_AUTH_SOCK
cd "${DDEV_APPROOT}"
if [ -n "${ACCOUNT}" ]; then
"${SWAP_INSTALL_SCRIPT}"
if [ "${#ARGS[@]}" -gt 0 ]; then
exec "${CSWAP_BIN}" run "${ACCOUNT}" -- "${ARGS[@]}"
fi
exec "${CSWAP_BIN}" run "${ACCOUNT}"
fi
exec "${PERSIST_BIN}" ${ARGS[@]+"${ARGS[@]}"}
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
#ddev-generated
## Description: Manage multiple Claude accounts (claude-swap / cswap)
## Usage: claude-swap [command]
## Example: "ddev claude-swap list"
set -euo pipefail
INSTALL_SCRIPT="${DDEV_APPROOT}/.ddev/claude-code/install-swap.sh"
PERSIST_BASE="${DDEV_APPROOT}/.ddev/claude-code"
PERSIST_HOME="${PERSIST_BASE}/home"
CSWAP_BIN="${PERSIST_HOME}/.local/bin/cswap"
# Lazy install / health check (also handles "ddev claude-swap upgrade").
if [ "${1:-}" = "upgrade" ]; then
exec "${INSTALL_SCRIPT}" --upgrade
fi
"${INSTALL_SCRIPT}"
export HOME="${PERSIST_HOME}"
export XDG_CONFIG_HOME="${PERSIST_BASE}/config"
export XDG_DATA_HOME="${PERSIST_BASE}/data"
export XDG_STATE_HOME="${PERSIST_BASE}/state"
export PATH="${PERSIST_HOME}/.local/bin:${PATH}"
unset SSH_AUTH_SOCK
cd "${DDEV_APPROOT}"
exec "${CSWAP_BIN}" "$@"
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
#ddev-generated
## Description: Run Codex CLI inside the web container
## Usage: codex
## Example: "ddev codex --version"
set -euo pipefail
INSTALL_SCRIPT="${DDEV_APPROOT}/.ddev/codex-cli/install.sh"
PERSIST_BASE="${DDEV_APPROOT}/.ddev/codex-cli"
PERSIST_BIN="${PERSIST_BASE}/bin/codex"
"${INSTALL_SCRIPT}"
export HOME="${PERSIST_BASE}/home"
export CODEX_HOME="${PERSIST_BASE}/home/.codex"
export XDG_CONFIG_HOME="${PERSIST_BASE}/config"
export XDG_DATA_HOME="${PERSIST_BASE}/data"
export XDG_STATE_HOME="${PERSIST_BASE}/state"
export PATH="${PERSIST_BASE}/bin:${HOME}/.local/bin:${PATH}"
unset SSH_AUTH_SOCK
cd "${DDEV_APPROOT}"
exec "${PERSIST_BIN}" --sandbox danger-full-access --ask-for-approval on-request "$@"
+9
View File
@@ -0,0 +1,9 @@
#ddev-generated
webimage_extra_packages:
- ripgrep
- bubblewrap
- socat
hooks:
post-start:
- exec: .ddev/claude-code/install.sh
- exec: .ddev/codex-cli/install.sh
+25
View File
@@ -0,0 +1,25 @@
name: ddev-codex-claude-cli
project_files:
- config.codex-claude-cli.yaml
- commands/web/codex
- commands/web/claude
- commands/web/claude-swap
- codex-cli/install.sh
- claude-code/install.sh
- claude-code/install-swap.sh
post_install_actions:
- |
#ddev-description: Make AI CLI scripts executable
set -eu
chmod +x commands/web/codex commands/web/claude commands/web/claude-swap codex-cli/install.sh claude-code/install.sh claude-code/install-swap.sh
removal_actions:
- |
#ddev-description: Remove AI CLI add-on artifacts
rm -f config.codex-claude-cli.yaml commands/web/codex commands/web/claude commands/web/claude-swap 2>/dev/null || true
rm -rf codex-cli 2>/dev/null || true
rm -rf claude-code 2>/dev/null || true
ddev_version_constraint: ">= v1.24.3"