Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ff6e17b732 | |||
| c6f9d1148b | |||
| 56e6a782e3 | |||
| 49d3e113ee | |||
| f1e879ca6c | |||
| 9c31c641d6 | |||
| d9dc85d825 | |||
| 0b78ab4a94 | |||
| 440218fc4c |
@@ -386,6 +386,7 @@ jobs:
|
||||
PI_OBSMEM_REF=${{ needs.resolve-versions.outputs.obsmem_ref }}
|
||||
- env:
|
||||
EXPECTED_PI_VERSION: ${{ needs.resolve-versions.outputs.pi_version }}
|
||||
STRICT_REGISTRATION: "1"
|
||||
run: bash scripts/smoke-test.sh opencode-devbox:smoke-with-pi --variant with-pi
|
||||
|
||||
smoke-omos-with-pi:
|
||||
@@ -435,6 +436,7 @@ jobs:
|
||||
- env:
|
||||
EXPECTED_PI_VERSION: ${{ needs.resolve-versions.outputs.pi_version }}
|
||||
EXPECTED_OMOS_VERSION: ${{ needs.resolve-versions.outputs.omos_version }}
|
||||
STRICT_REGISTRATION: "1"
|
||||
run: bash scripts/smoke-test.sh opencode-devbox:smoke-omos-with-pi --variant omos-with-pi
|
||||
|
||||
smoke-pi-only:
|
||||
@@ -482,6 +484,7 @@ jobs:
|
||||
PI_OBSMEM_REF=${{ needs.resolve-versions.outputs.obsmem_ref }}
|
||||
- env:
|
||||
EXPECTED_PI_VERSION: ${{ needs.resolve-versions.outputs.pi_version }}
|
||||
STRICT_REGISTRATION: "1"
|
||||
run: bash scripts/smoke-test.sh opencode-devbox:smoke-pi-only --variant pi-only
|
||||
|
||||
# ── Phase 4: multi-arch publish per variant ────────────────────────
|
||||
|
||||
@@ -20,6 +20,14 @@ name: Validate
|
||||
# release tags are the gate that fully validates base-image changes.
|
||||
# The base-change-warning job below surfaces a runtime warning when this
|
||||
# blind-spot applies.
|
||||
#
|
||||
# Because of this, the fork/recall *registration* smoke checks (which depend on
|
||||
# the base entrypoint running `pi install /opt/<pkg>`) are warn-only here:
|
||||
# smoke-test.sh leaves STRICT_REGISTRATION unset on this path, so a base-latest
|
||||
# that lags the entrypoint in the current commit can't red the run with a false
|
||||
# negative. The release smoke jobs build the base fresh and set
|
||||
# STRICT_REGISTRATION=1 to enforce those checks. The build-time /opt +
|
||||
# node_modules checks stay hard in both paths.
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
@@ -4,6 +4,15 @@
|
||||
|
||||
Docker image packaging [opencode](https://opencode.ai) into a production-ready dev container. Image variants are published to Docker Hub via Gitea Actions CI. Not a library or application — this is infrastructure (Dockerfiles, entrypoint scripts, docker-compose, documentation).
|
||||
|
||||
> **pi is deprecated here (since v1.17.2), removed in v2.0.0.** The
|
||||
> `INSTALL_PI` build arg, the `with-pi` / `omos-with-pi` / `pi-only`
|
||||
> variants, the `base-pi-only` published tag, and all `~/.pi`-related
|
||||
> wiring are slated for removal. pi now ships from its own repo
|
||||
> (`joakimp/pi-devbox`). Do not add new pi functionality here. Full
|
||||
> removal plan + the `NPM_CONFIG_PREFIX` relocation: see
|
||||
> `docs/CLEANUP-v2.0.0.md`. The pi-related descriptions below remain
|
||||
> accurate only until the v2.0.0 removal lands.
|
||||
|
||||
## File roles
|
||||
|
||||
- `Dockerfile.base` — variant-independent layers (apt, locales, AWS CLI, Node.js, mempalace, gitea-mcp, user setup, chromadb prewarm, ENVs, entrypoints). Published as `joakimp/opencode-devbox:base-<sha12>`. Rebuilt only when its content hash changes.
|
||||
@@ -104,6 +113,7 @@ cd /tmp && npm pack @earendil-works/pi-coding-agent@0.75.5 && tar -xzf earendil-
|
||||
- **`actions/upload-artifact` and `actions/download-artifact` must stay at @v3 on Gitea.** v4+ uses a GitHub-Enterprise-specific Artifact API; runs fail with `GHESNotSupportedError`. If you need artifacts for a new reason (build logs, SBOMs, etc.), pin @v3 explicitly.
|
||||
- **Step scripts run under `/bin/sh` (dash), not bash.** Avoid bash-isms like `${VAR//a/b}` parameter-pattern substitution; use POSIX alternatives (`tr`, `sed`) or declare `shell: bash` on the step.
|
||||
- **`BUILDKIT_PROGRESS=plain`** is set at workflow level on `docker-publish-split.yml` so arm64-under-QEMU builds log each layer line-by-line. The default collapsed progress UI hides which step is stalled, which made diagnosing earlier hangs expensive.
|
||||
- **`STRICT_REGISTRATION` gates the fork/recall *registration* smoke assertions.** `smoke-test.sh`'s two pi-extension registration checks (that `pi-fork`/`pi-observational-memory` registered in `~/.pi/agent/settings.json`) depend on the *base* entrypoint running `pi install /opt/<pkg>`. `validate.yml` builds variants from the **published** `base-latest`, which lags the in-repo entrypoint until a release rebuilds the base — so those checks would false-negative there. They are therefore warn-only unless `STRICT_REGISTRATION=1`: `validate.yml` leaves it unset (warn), and `docker-publish-split.yml` (which builds the base fresh in the same run) sets `STRICT_REGISTRATION: "1"` on the three pi-bearing smoke jobs to enforce them. Build-time `/opt` + `node_modules` checks stay hard in both paths. If you touch the registration checks or the base-freshness model, keep this flag wiring in lockstep across both workflows.
|
||||
|
||||
## Testing changes
|
||||
|
||||
|
||||
+198
-1
@@ -8,7 +8,204 @@ Tags follow `v{opencode_version}[letter]` — bare tag for the first build on a
|
||||
|
||||
## Unreleased
|
||||
|
||||
_(no changes since v1.15.13d)_
|
||||
## v1.17.2 — 2026-06-10
|
||||
|
||||
First container build on **opencode-ai `1.17.2`** (from `1.16.2`). This
|
||||
release also **deprecates all pi support** ahead of its removal in v2.0.0,
|
||||
and hardens the mempalace install.
|
||||
|
||||
### Bumped: opencode-ai 1.16.2 → 1.17.2
|
||||
|
||||
`OPENCODE_VERSION` ARG in `Dockerfile.variant`. Bare `v1.17.2` tag per the
|
||||
`v{opencode_version}` scheme.
|
||||
|
||||
### Deprecated: pi support (removed in v2.0.0)
|
||||
|
||||
pi has been decoupled into its own self-contained image,
|
||||
[`joakimp/pi-devbox`](https://gitea.jordbo.se/joakimp/pi-devbox) (v1.0.0+,
|
||||
which no longer FROMs `base-pi-only`). The pi paths in opencode-devbox are
|
||||
now dead weight and are **deprecated as of v1.17.2, scheduled for removal in
|
||||
v2.0.0**:
|
||||
|
||||
- The `INSTALL_PI` build arg and all `PI_*` args.
|
||||
- The `with-pi`, `omos-with-pi`, and `pi-only` build variants.
|
||||
- The `base-pi-only[-vX.Y.Z]` tag published (to the `joakimp/pi-devbox`
|
||||
repo) from this repo's CI.
|
||||
- All `~/.pi`-related entrypoint wiring (pi-toolkit / pi-extensions deploy,
|
||||
settings.json bootstrap, mempalace pi-bridge symlink, pi-fork / pi-obsmem
|
||||
registration).
|
||||
|
||||
What this release does:
|
||||
|
||||
- Building with `INSTALL_PI=true` now prints a **build-time deprecation
|
||||
warning** to stderr.
|
||||
- README, DOCKER_HUB.md, and AGENTS.md mark the pi variants deprecated and
|
||||
point to `joakimp/pi-devbox`.
|
||||
- The full removal plan is documented in `docs/CLEANUP-v2.0.0.md`.
|
||||
|
||||
**Migration:** pull `joakimp/pi-devbox:latest` directly instead of any
|
||||
`*-with-pi` / `pi-only` opencode-devbox tag. opencode-only users are
|
||||
unaffected by this release.
|
||||
|
||||
#### ⚠ Heads-up for v2.0.0: global npm prefix relocation
|
||||
|
||||
Today `NPM_CONFIG_PREFIX` points at the pi-specific `~/.pi/npm-global`
|
||||
(backed by the `devbox-pi-config` named volume), so `npm install -g` as the
|
||||
developer user persists across recreates. **v2.0.0 will move the prefix to a
|
||||
neutral opencode path** (e.g. `~/.config/opencode/npm-global`). Consequences
|
||||
for existing opencode users at the v2.0.0 upgrade:
|
||||
|
||||
- Previously global-installed npm tools remain on disk in the old volume but
|
||||
**drop off `PATH`** until migrated.
|
||||
- The new prefix path is not currently a named volume, so persistence needs a
|
||||
compose/volume update.
|
||||
|
||||
v2.0.0 will ship a **one-time migration shim** (copies old prefix contents to
|
||||
the new path on first run) and an updated volume mapping. This notice is the
|
||||
one-release-cycle advance warning.
|
||||
|
||||
### Hardened: mempalace install pinned + diary_write schema workaround
|
||||
|
||||
- `MEMPALACE_VERSION` is now an explicit ARG (default `3.4.0`) in
|
||||
`Dockerfile.base`; the install uses `mempalace==${MEMPALACE_VERSION}`
|
||||
instead of an unpinned `uv tool install mempalace`. An unpinned install is
|
||||
what silently swept in the broken `diary_write` schema. Mirrors pi-devbox.
|
||||
- Added an idempotent, self-deactivating post-install patch that strips the
|
||||
**top-level `anyOf`** from `mempalace_diary_write`'s `input_schema`.
|
||||
Mempalace 3.3.x/3.4.0 advertise `anyOf:[{required:[entry]},{required:[content]}]`
|
||||
at the schema root, which the Anthropic tools API rejects outright
|
||||
(`input_schema does not support oneOf, allOf, or anyOf at the top level`),
|
||||
breaking pi/Claude tool registration at session start. The handler still
|
||||
accepts `content` server-side. Upstream: MemPalace/mempalace#1728, PR #1735.
|
||||
Remove once a fixed mempalace release is pinned.
|
||||
|
||||
### Fixed: smoke-test pi-extensions readiness race (test-only, no image change)
|
||||
|
||||
`scripts/smoke-test.sh`'s entrypoint-deploy wait loop gated only on
|
||||
`keybindings.json` (written by pi-toolkit, which runs *before* pi-extensions),
|
||||
so the `~/.pi/agent/extensions/*.ts ≥ 4` assertion could sample mid-deploy and
|
||||
see fewer than 4 files under parallel build load. Observed on v1.16.2 run 370:
|
||||
`smoke-with-pi` saw `<4` while `smoke-omos-with-pi` / `smoke-pi-only` (identical
|
||||
pi-extensions `357fcc6`) both saw 8, skipping `build-variant-with-pi`. The wait
|
||||
loop now blocks until the *last*-deployed artifact (the `mempalace.ts` bridge
|
||||
symlink) exists **and** the extension count has settled ≥ 4 (up to 45s).
|
||||
|
||||
## v1.16.2 — 2026-06-08
|
||||
|
||||
First container build on the `opencode-ai@1.16.x` minor release (rolls up
|
||||
`1.16.0` → `1.16.1` → `1.16.2`, all published 2026-06-05). Also picks up
|
||||
**pi `0.78.1` → `0.79.0`** (resolved fresh by CI's `resolve-versions` job) in
|
||||
the `with-pi`, `omos-with-pi`, and `pi-only` variants.
|
||||
|
||||
### Bumped: opencode-ai 1.15.13 → 1.16.2
|
||||
|
||||
`OPENCODE_VERSION` ARG in `Dockerfile.variant`. Highlights from the upstream
|
||||
release (full notes: <https://github.com/anomalyco/opencode/releases>):
|
||||
|
||||
- **1.16.0** — ~38% faster startup (@StarpTech); managed workspace cloning that
|
||||
keeps dirty/untracked files; move sessions between workspaces/directories;
|
||||
proper OpenAI-via-Bedrock support; skill discovery + file-based agent loading;
|
||||
`run --replay` for interactive session replay. Plus TUI/desktop polish and
|
||||
numerous bugfixes (shell cancellation races, Windows path normalization, ACP
|
||||
cancel/abort).
|
||||
- **1.16.1** — internal/no user-visible notes (empty release body).
|
||||
- **1.16.2** — reasoning summaries only run on supporting providers (avoids
|
||||
GPT-5 request failures); edit operations refuse loose matches that could
|
||||
overwrite the wrong code; Bedrock hang-before-first-token fix; diff-viewer
|
||||
hunk navigation; subagents can be backgrounded; Snowflake Cortex provider.
|
||||
|
||||
### Picks up: pi 0.78.1 → 0.79.0
|
||||
|
||||
Resolved at build time for the pi-bearing variants. Headlines: project-trust
|
||||
prompting for project-local settings/resources/instructions/packages (with
|
||||
`--approve`/`--no-approve` and a `project_trust` extension event), cache-hit
|
||||
rate in the interactive footer, richer SDK/RPC extension surfaces, plus a
|
||||
stack of TUI and provider fixes. Full notes ship in the npm tarball's
|
||||
`CHANGELOG.md`.
|
||||
|
||||
### Smoke size thresholds bumped +150 MB (preemptive)
|
||||
|
||||
Ahead of the combined minor opencode + pi bump, all opencode-bearing variant
|
||||
thresholds in `scripts/smoke-test.sh` were raised: `base` 2600 → 2750,
|
||||
`omos` 3300 → 3450, `with-pi` 2900 → 3050, `omos-with-pi` 3900 → 4050; and
|
||||
`pi-only` 2750 → 2850 (+100, pi-only carries only the pi bump). Both `base`
|
||||
(last 2506 MB) and `omos` (last 3206 MB) were on ~94 MB headroom, and a minor
|
||||
opencode bump has tripped these ceilings before (v1.15.0 omos, v1.15.4
|
||||
omos-with-pi), causing a partial publish + letter-suffix recovery cycle.
|
||||
Restoring ~250 MB headroom avoids that. CI's smoke size print records actual
|
||||
landed sizes — tighten later if they come in well under.
|
||||
|
||||
## v1.15.13e — 2026-06-04
|
||||
|
||||
Letter-suffix rebuild on opencode `1.15.13` (version unchanged). Picks up
|
||||
**pi `0.78.1`** (resolved fresh by CI's `resolve-versions` job) plus the LAN-jump
|
||||
key-persistence work, an entrypoint ownership fix for the new `devbox-ssh-local`
|
||||
volume, a CI smoke false-negative fix, and documentation. Touches `entrypoint.sh`
|
||||
and `setup-lan-access.sh` (both in the base hash), so `base-latest` /
|
||||
`base-pi-only` advance and the fixes propagate to `pi-devbox`.
|
||||
|
||||
### Docs: per-host `ControlPath` overrides break `pi --ssh` (read-only `~/.ssh`)
|
||||
|
||||
Documented a gotcha in the README "Reaching your LAN" section: the bind-mounted
|
||||
`~/.ssh/config` is read before the baked `Host *` default, and SSH uses the
|
||||
first `ControlPath` it sees. A per-host block that sets `ControlPath` under
|
||||
`~/.ssh/` (a common CGNAT-multiplexing pattern, e.g. `~/.ssh/cm/%r@%h:%p`) wins
|
||||
but then fails inside the container because `~/.ssh` is mounted read-only — the
|
||||
master socket can't bind. This silently breaks `pi --ssh <host>`: the SSH layer
|
||||
fails and pi falls back to running its tools locally in the container. Fix is
|
||||
host-side — drop the per-host `ControlPath` or repoint it at the writable
|
||||
`/tmp/sshcm/%r@%h:%p` (works on both host and container, preserves multiplexing).
|
||||
No image change; documentation only.
|
||||
|
||||
### Fixed: validate.yml false-negative on fork/recall registration checks
|
||||
|
||||
The push-to-main `validate.yml` builds variants FROM the published `base-latest`
|
||||
image, which lags the entrypoint in the current commit until a release tag
|
||||
rebuilds the base. The fork/recall *registration* smoke checks depend on the
|
||||
base entrypoint running `pi install /opt/<pkg>`, so a stale `base-latest` reded
|
||||
those runs with a false negative even when the variant layer was correct.
|
||||
`smoke-test.sh` now gates the two registration assertions behind
|
||||
`STRICT_REGISTRATION` (warn-only when unset). `validate.yml` leaves it unset;
|
||||
the release pipeline (`docker-publish-split.yml`), which builds the base fresh
|
||||
in the same run, sets `STRICT_REGISTRATION=1` on the pi-bearing smoke jobs to
|
||||
enforce them. The build-time `/opt` + `node_modules` checks stay hard in both
|
||||
paths.
|
||||
|
||||
### Added: persist the LAN-jump key + one-line authorize hint (authorize once per machine)
|
||||
|
||||
The jump keypair (`~/.ssh-local/devbox_jump_ed25519`) was stored on the
|
||||
container's ephemeral overlay, so `docker compose up --force-recreate` (every
|
||||
image update) regenerated it — forcing you to re-authorize the new key on the
|
||||
host each time. The compose files now persist `~/.ssh-local` via a named volume
|
||||
(`devbox-ssh-local`), matching the pattern already used for `.pi`, shell
|
||||
history, etc. The key is generated **once** and reused across updates, so you
|
||||
authorize it on the host **once per machine**.
|
||||
|
||||
`setup-lan-access.sh` now also prints a ready-to-paste authorize line whenever
|
||||
it generates a **new** key (not just when `HOST_SSH_USER` is unset), e.g.
|
||||
`echo 'ssh-ed25519 …' >> ~/.ssh/authorized_keys` — no helper file to locate, no
|
||||
workspace path to guess. It stays silent once the key is persisted.
|
||||
|
||||
### Fixed: chown the `devbox-ssh-local` volume so the jump key can be generated
|
||||
|
||||
The previous change persisted `~/.ssh-local` via a named volume, but the
|
||||
entrypoint's volume-ownership loop was never updated to include it. Docker
|
||||
creates named volumes as `root:root`, so on a fresh volume `~/.ssh-local`
|
||||
stayed root-owned while `setup-lan-access.sh` runs as `developer` — both its
|
||||
`mkdir cm` and `ssh-keygen` failed silently (`|| true` / `|| exit 0`), leaving
|
||||
**no jump key and no config**, breaking LAN access on the first recreate after
|
||||
the persistence change. `entrypoint.sh` now chowns `~/.ssh-local` to the
|
||||
developer user alongside the other named-volume mount points.
|
||||
|
||||
### Docs: document the optional `~/.config/devbox-shell` mount in the compose template
|
||||
|
||||
`docker-compose.yml` now carries a commented-out `~/.config/devbox-shell` bind
|
||||
mount with an explanatory note. It's the recommended home for host-owned shell
|
||||
config: the image's `~/.bash_aliases` sources `~/.config/devbox-shell/bash_aliases`
|
||||
if present, and `setup-lan-access.sh` reads `~/.config/devbox-shell/ssh-lan.conf`
|
||||
for named-peer `ProxyJump host` overrides. A directory mount is preferred over
|
||||
the single-file `~/.bash_aliases` mount because it survives editors' atomic-save.
|
||||
Template comment only; no behavior change.
|
||||
|
||||
## v1.15.13d — 2026-06-04
|
||||
|
||||
|
||||
+7
-6
@@ -10,15 +10,16 @@ Designed for teams who want a reproducible coding-agent setup that runs the same
|
||||
|---|---|
|
||||
| `latest` / `vX.Y.Z` | Base image — opencode, Node.js, AWS CLI, dev tools |
|
||||
| `latest-omos` / `vX.Y.Z-omos` | Base + [oh-my-opencode-slim](https://github.com/alvinunreal/oh-my-opencode-slim) multi-agent orchestration and Bun |
|
||||
| `latest-with-pi` / `vX.Y.Z-with-pi` | Base + [pi](https://github.com/earendil-works/pi) as alternative/complementary harness (shares the mempalace install with opencode) |
|
||||
| `latest-omos-with-pi` / `vX.Y.Z-omos-with-pi` | OMOS + pi together |
|
||||
| `latest-with-pi` / `vX.Y.Z-with-pi` | **DEPRECATED (removed in v2.0.0)** — Base + [pi](https://github.com/earendil-works/pi). Use [`joakimp/pi-devbox`](https://hub.docker.com/r/joakimp/pi-devbox) instead |
|
||||
| `latest-omos-with-pi` / `vX.Y.Z-omos-with-pi` | **DEPRECATED (removed in v2.0.0)** — OMOS + pi together |
|
||||
|
||||
All variants support `linux/amd64` and `linux/arm64`.
|
||||
|
||||
> A fifth, pi-without-opencode build is produced from the same `Dockerfile.variant`
|
||||
> (`INSTALL_OPENCODE=false`) but is **not** published under this repo — it ships as
|
||||
> the separate [`joakimp/pi-devbox`](https://hub.docker.com/r/joakimp/pi-devbox)
|
||||
> image so an "opencode-devbox" tag never lacks opencode.
|
||||
> **Looking for pi?** The `*-with-pi` / `pi-only` builds and the `base-pi-only`
|
||||
> tag are **deprecated since v1.17.2 and will be removed in v2.0.0**. pi now
|
||||
> ships as its own self-contained image:
|
||||
> [`joakimp/pi-devbox`](https://hub.docker.com/r/joakimp/pi-devbox). Pull that
|
||||
> directly instead of any pi-bearing opencode-devbox tag.
|
||||
|
||||
## Quick Start
|
||||
|
||||
|
||||
+37
-1
@@ -259,14 +259,50 @@ RUN ARCH=$(case "${TARGETARCH}" in amd64) echo "x86_64" ;; arm64) echo "aarch64"
|
||||
# Always installed in the base (variant-independent). Set
|
||||
# INSTALL_MEMPALACE=false at base-build time to shave ~300 MB.
|
||||
ARG INSTALL_MEMPALACE=true
|
||||
# Pin mempalace explicitly (mirrors pi-devbox). An unpinned
|
||||
# `uv tool install mempalace` is what silently swept in the broken
|
||||
# diary_write top-level-anyOf schema (3.3.x/3.4.0) that breaks the
|
||||
# Anthropic tools API; pinning makes every bump a deliberate, reviewable
|
||||
# diff. Bump this in lockstep with pi-devbox's MEMPALACE_VERSION.
|
||||
ARG MEMPALACE_VERSION=3.4.0
|
||||
ENV UV_TOOL_DIR=/opt/uv-tools
|
||||
ENV UV_TOOL_BIN_DIR=/usr/local/bin
|
||||
RUN if [ "${INSTALL_MEMPALACE}" = "true" ]; then \
|
||||
mkdir -p /opt/uv-tools && \
|
||||
uv tool install --no-cache mempalace && \
|
||||
uv tool install --no-cache "mempalace==${MEMPALACE_VERSION}" && \
|
||||
/opt/uv-tools/mempalace/bin/python -c "import mempalace; print('mempalace', mempalace.__version__ if hasattr(mempalace, '__version__') else 'installed')" ; \
|
||||
fi
|
||||
|
||||
# ── workaround: strip top-level anyOf from mempalace_diary_write schema ──
|
||||
# Mempalace 3.3.x/3.4.0 advertise diary_write's input_schema with a
|
||||
# top-level `anyOf: [{required:[entry]}, {required:[content]}]` to express
|
||||
# "either entry or content must be supplied". Anthropic's tools API rejects
|
||||
# top-level anyOf/oneOf/allOf, so pi/Claude fail at session start with
|
||||
# `tools.<n>.custom.input_schema: input_schema does not support oneOf,
|
||||
# allOf, or anyOf at the top level`.
|
||||
#
|
||||
# Patch the advertised schema to require ["agent_name", "entry"] and remove
|
||||
# the anyOf block. The handler keeps accepting `content` server-side as a
|
||||
# kwarg alias so existing callers still work.
|
||||
#
|
||||
# Idempotent and self-deactivating: once upstream releases the fix the
|
||||
# regex no longer matches and this RUN is a silent no-op.
|
||||
# Upstream tracking:
|
||||
# https://github.com/MemPalace/mempalace/issues/1728
|
||||
# https://github.com/MemPalace/mempalace/pull/1735
|
||||
# TODO: remove this RUN once a mempalace release containing PR #1735 is on
|
||||
# PyPI and installed by the line above.
|
||||
RUN if [ "${INSTALL_MEMPALACE}" = "true" ]; then \
|
||||
MP_FILE="$(find /opt/uv-tools/mempalace -path '*/mempalace/mcp_server.py' | head -n1)" && \
|
||||
if [ -z "$MP_FILE" ]; then echo "mempalace mcp_server.py not found" >&2; exit 1; fi && \
|
||||
perl -0777 -i -pe 's/(?:[ \t]*\#[^\n]*\n)*[ \t]*"required":\s*\[\s*"agent_name"\s*\]\s*,\s*\n[ \t]*"anyOf":\s*\[\s*\n[ \t]*\{\s*"required":\s*\[\s*"entry"\s*\]\s*\}\s*,\s*\n[ \t]*\{\s*"required":\s*\[\s*"content"\s*\]\s*\}\s*,?\s*\n[ \t]*\]\s*,\s*\n/ "required": ["agent_name", "entry"],\n/s' "$MP_FILE" && \
|
||||
if grep -q '"required": \["agent_name", "entry"\]' "$MP_FILE"; then \
|
||||
echo "mempalace diary_write anyOf workaround: applied (or already clean)"; \
|
||||
else \
|
||||
echo "WARN: mempalace diary_write anyOf workaround did not match expected schema — upstream may have changed shape" >&2; \
|
||||
fi ; \
|
||||
fi
|
||||
|
||||
# ── mempalace-toolkit — bash wrappers for session/docs mining ────────
|
||||
ARG INSTALL_MEMPALACE_TOOLKIT=true
|
||||
ARG MEMPALACE_TOOLKIT_REF=main
|
||||
|
||||
+20
-8
@@ -10,14 +10,19 @@
|
||||
# ───────────────── ──────────────── ──────────── ──────────
|
||||
# base true false false
|
||||
# omos true true false
|
||||
# with-pi true false true
|
||||
# omos-with-pi true true true
|
||||
# pi-only false false true
|
||||
# with-pi *DEPR* true false true
|
||||
# omos-with-pi*DEPR* true true true
|
||||
# pi-only *DEPR* false false true
|
||||
#
|
||||
# The `pi-only` variant is the single source of truth for the pi-devbox
|
||||
# image (pi + companions, no opencode). It exists so pi-devbox can FROM it
|
||||
# without inheriting opencode, while the pi install logic stays defined
|
||||
# here in one place.
|
||||
# DEPRECATION (since v1.17.2): the three pi-bearing variants (with-pi,
|
||||
# omos-with-pi, pi-only) and the INSTALL_PI build path are DEPRECATED and
|
||||
# will be REMOVED in v2.0.0. pi now ships from its own self-contained image:
|
||||
# joakimp/pi-devbox:latest (https://gitea.jordbo.se/joakimp/pi-devbox).
|
||||
# See docs/CLEANUP-v2.0.0.md for the removal plan.
|
||||
#
|
||||
# Until v2.0.0 the `pi-only` variant remains the source of truth for the
|
||||
# legacy pi build (pi + companions, no opencode); pi-devbox v1.0.0+ no
|
||||
# longer FROMs it.
|
||||
#
|
||||
# Pass `--build-arg BASE_IMAGE=<repo>:base-<hash>` to select the base.
|
||||
# The CI workflow computes the base hash from Dockerfile.base + rootfs/
|
||||
@@ -42,7 +47,7 @@ ARG USER_NAME=developer
|
||||
# edit, so the cache-hit class of bug that bit pi-devbox v0.74.0..
|
||||
# v0.75.5 cannot apply here.
|
||||
ARG INSTALL_OPENCODE=true
|
||||
ARG OPENCODE_VERSION=1.15.13
|
||||
ARG OPENCODE_VERSION=1.17.2
|
||||
RUN if [ "${INSTALL_OPENCODE}" = "true" ]; then \
|
||||
NPM_CONFIG_PREFIX=/usr npm install -g opencode-ai@${OPENCODE_VERSION} && \
|
||||
opencode --version ; \
|
||||
@@ -81,6 +86,13 @@ ARG PI_OBSMEM_REPO=https://github.com/elpapi42/pi-observational-memory.git
|
||||
ARG PI_OBSMEM_REF=master
|
||||
RUN if [ "${INSTALL_PI}" = "true" ]; then \
|
||||
set -e && \
|
||||
printf '%s\n' \
|
||||
"===========================================================" \
|
||||
"DEPRECATION WARNING: INSTALL_PI is deprecated in opencode-devbox" \
|
||||
"(since v1.17.2) and will be REMOVED in v2.0.0. Use the dedicated" \
|
||||
"image joakimp/pi-devbox:latest instead." \
|
||||
"See https://gitea.jordbo.se/joakimp/pi-devbox" \
|
||||
"===========================================================" >&2 && \
|
||||
git_clone_retry() { \
|
||||
url="$1"; ref="$2"; dest="$3"; \
|
||||
for i in 1 2 3 4 5; do \
|
||||
|
||||
@@ -155,15 +155,20 @@ The devbox works the same way whether the host is **native Linux Docker** or a *
|
||||
- **Native Linux Docker:** the host NATs container egress onto its LAN, so other devices on your LAN are reachable directly. Nothing to configure.
|
||||
- **VM-backed (macOS / Docker Desktop):** the container runs in a Linux VM behind the host's network stack. The host's *directly-attached* LAN peers are **not** bridged into the container by default — only the host itself and *routed* subnets are reachable.
|
||||
|
||||
On every start the entrypoint detects which case applies. On VM-backed hosts it generates a writable `~/.ssh-local/config` that uses the **host as an SSH jump** to reach LAN peers; on native Linux it does nothing.
|
||||
On every start the entrypoint detects which case applies. On VM-backed hosts it generates a writable `~/.ssh-local/config` that uses the **host as an SSH jump** to reach LAN peers; on native Linux it does nothing. The jump keypair lives in `~/.ssh-local`, which is persisted by the `devbox-ssh-local` named volume — so it's generated **once** and reused across container updates.
|
||||
|
||||
**To enable it on a VM-backed host:**
|
||||
**To enable it on a VM-backed host (one-time setup per machine):**
|
||||
|
||||
1. Set `HOST_SSH_USER=<your host username>` in `.env`.
|
||||
2. Start the container once. The entrypoint prints a public key — append it to your host's `~/.ssh/authorized_keys`.
|
||||
2. Start the container once. When it generates the jump key it prints a ready-to-paste line — run it **on the host** to authorize the key:
|
||||
```bash
|
||||
echo 'ssh-ed25519 AAAA…devbox-jump@…' >> ~/.ssh/authorized_keys
|
||||
```
|
||||
3. Ensure the host's SSH server is on (on macOS: System Settings → General → Sharing → Remote Login).
|
||||
4. Reach the host itself with `dssh host`. (`dssh`/`dscp` wrap `ssh -F ~/.ssh-local/config`.)
|
||||
|
||||
Because the key is persisted, you do this **once per machine** — not after every `docker compose up --force-recreate`. You'll only see the authorize line again if you reset the `devbox-ssh-local` volume.
|
||||
|
||||
That alone gets you `container → host`. To reach **named LAN peers** by name, give them a `ProxyJump host` override. Don't add it to the shared `~/.ssh/config` entries — the host itself reaches those peers *directly*, and a jump-through-`host` would break the host's own access (and that file is mounted read-only anyway). Instead, drop the overrides in a **host-owned** file that the container Includes ahead of your `~/.ssh/config`:
|
||||
|
||||
```sshconfig
|
||||
@@ -182,6 +187,22 @@ Now `dssh my-nas` routes container → host → LAN peer, pulling HostName/User/
|
||||
|
||||
> This ships the **mechanism** only — your specific target hosts are facts about *your* network (and a laptop roams between several), so they live in your own host-side config, never baked into the image. Set `DEVBOX_LAN_ACCESS=off` to disable, or `=jump` to force it (e.g. native Linux with `extra_hosts: ["host.docker.internal:host-gateway"]`).
|
||||
|
||||
#### Gotcha: per-host `ControlPath` and `pi --ssh`
|
||||
|
||||
The base image bakes a `Host *` default (`/etc/ssh/ssh_config.d/00-devbox-controlmaster.conf`) that points `ControlPath` at the writable, per-container `/tmp/sshcm/` (created mode-700 on every start by `entrypoint-user.sh`). Multiplexing therefore works out of the box. **But your bind-mounted `~/.ssh/config` is read first, and SSH uses the first value it sees** — so any per-host block that sets its own `ControlPath` under `~/.ssh/` (a common CGNAT-multiplexing pattern, e.g. `ControlPath ~/.ssh/cm/%r@%h:%p`) **wins, and then fails inside the container** because `~/.ssh` is mounted **read-only** — the master socket can't bind (`cannot bind … Read-only file system`).
|
||||
|
||||
This bites `pi --ssh <host>` especially: the SSH layer fails to establish the master and pi silently falls back to running its `read`/`write`/`edit`/`bash` tools **locally in the container** instead of on the remote (watch for the missing `SSH ⚡` in the status bar — and `hostname` returning the container ID).
|
||||
|
||||
**Fix (host-side, one line):** in your host's `~/.ssh/config`, either drop the per-host `ControlPath` (to inherit the writable baked default) or point it at a path that's writable inside the container too:
|
||||
|
||||
```sshconfig
|
||||
Host my-remote
|
||||
# was: ControlPath ~/.ssh/cm/%r@%h:%p ← read-only in the container
|
||||
ControlPath /tmp/sshcm/%r@%h:%p # writable on both host and container
|
||||
```
|
||||
|
||||
`/tmp/sshcm/` is also writable on the host (macOS/Linux), so native (non-container) `ssh`/`pi --ssh` from the host keeps working and CGNAT multiplexing is preserved (`ControlMaster`/`ControlPersist` unchanged — only the socket *directory* moves). Note SSH does not create the `ControlPath` parent dir; the container makes `/tmp/sshcm` every start, but on the host run `mkdir -p /tmp/sshcm` once if it doesn't already exist.
|
||||
|
||||
### Custom opencode config
|
||||
|
||||
Opencode configuration is persisted automatically via the named volume `devbox-opencode-config`. This volume is mounted at `/home/developer/.config/opencode` by default — no host directory setup required. All changes to `opencode.jsonc`, skills, and (on the OMOS variant) `oh-my-opencode-slim.json` survive container recreation.
|
||||
@@ -399,7 +420,8 @@ docker compose build --build-arg NVIM_VERSION=0.12.1 # pin to a specific versi
|
||||
| `INSTALL_MEMPALACE` | `true` | [MemPalace](https://github.com/MemPalace/mempalace) local AI memory system (~300 MB — disable to shrink image if you don't need MCP memory) |
|
||||
| `INSTALL_MEMPALACE_TOOLKIT` | `true` | [mempalace-toolkit](https://gitea.jordbo.se/joakimp/mempalace-toolkit) bash wrappers (`mempalace-session`, `mempalace-docs`). Cloned at build time from `MEMPALACE_TOOLKIT_REF` (default `main`). Requires `INSTALL_MEMPALACE=true`. |
|
||||
| `INSTALL_OMOS` | `false` | [oh-my-opencode-slim](https://github.com/alvinunreal/oh-my-opencode-slim) multi-agent orchestration (installs Bun and plugin) |
|
||||
| `INSTALL_OPENCODE` | `true` | Install opencode. Set `false` to build a pi-only image (still includes Bun if `INSTALL_OMOS=true`; for a fully stripped pi-only image see the `pi-devbox` repo). |
|
||||
| `INSTALL_PI` | `false` | **DEPRECATED (removed in v2.0.0)** — install pi alongside opencode. Use [`joakimp/pi-devbox`](https://gitea.jordbo.se/joakimp/pi-devbox) instead. |
|
||||
| `INSTALL_OPENCODE` | `true` | Install opencode. Set `false` to build a pi-only image (still includes Bun if `INSTALL_OMOS=true`; for a fully stripped pi-only image see the `pi-devbox` repo). **Note: the pi-only path is deprecated and removed in v2.0.0.** |
|
||||
| `INSTALL_PI` | `false` | Install [pi](https://github.com/earendil-works/pi) as alternative/complementary harness. Both clones [pi-toolkit](https://gitea.jordbo.se/joakimp/pi-toolkit) (~5 MB) and [pi-extensions](https://gitea.jordbo.se/joakimp/pi-extensions) (~1 MB) into `/opt/`; entrypoint deploys them on container start. ~150 MB total image growth. |
|
||||
| `PI_VERSION` | `latest` | npm version of `@earendil-works/pi-coding-agent`. Floats by default (image rebuild = pi update). |
|
||||
| `PI_TOOLKIT_REF`, `PI_EXTENSIONS_REF` | `main` | Git refs for the toolkit/extensions clones. Pin to a tag/commit for reproducibility. |
|
||||
@@ -466,6 +488,19 @@ All six agents should respond if your provider authentication is working.
|
||||
|
||||
## pi (alternative/complementary harness)
|
||||
|
||||
> **⚠ DEPRECATED since v1.17.2 — removed in v2.0.0.** pi support in
|
||||
> opencode-devbox (the `INSTALL_PI` build arg, the `*-with-pi` /
|
||||
> `omos-with-pi` / `pi-only` variants, and the `base-pi-only` tag) is
|
||||
> deprecated. pi now ships as its own self-contained image:
|
||||
> **[`joakimp/pi-devbox`](https://gitea.jordbo.se/joakimp/pi-devbox)** — pull
|
||||
> that directly. The section below documents the legacy path until removal.
|
||||
>
|
||||
> *Migration note for v2.0.0:* the global npm prefix
|
||||
> (`NPM_CONFIG_PREFIX`) will move off the pi-specific `~/.pi/npm-global`
|
||||
> path to a neutral opencode path. Globally `npm install -g`'d tools may
|
||||
> need their volume/PATH refreshed; the v2.0.0 release notes will carry a
|
||||
> one-time migration shim and details.
|
||||
|
||||
[pi](https://github.com/earendil-works/pi) is a lightweight TUI coding-agent that can run alongside opencode in the same container. Both harnesses share the mempalace install and palace data — wing/diary entries created by one are visible to the other.
|
||||
|
||||
### Setup
|
||||
|
||||
@@ -59,6 +59,11 @@ services:
|
||||
# allowing both native and containerized opencode on the same machine.
|
||||
- devbox-opencode-config:/home/developer/.config/opencode
|
||||
- devbox-pi-config:/home/developer/.pi
|
||||
# Persist the generated LAN-jump keypair (~/.ssh-local) across recreates.
|
||||
# setup-lan-access.sh generates this key once and reuses it; persisting
|
||||
# it means you authorize it on the host ONCE rather than re-authorizing
|
||||
# after every `docker compose up --force-recreate`.
|
||||
- devbox-ssh-local:/home/developer/.ssh-local
|
||||
|
||||
# NOTE: Do NOT bind-mount ~/.agents/skills/ from the host. The
|
||||
# container manages its own skills directory independently — the
|
||||
@@ -95,6 +100,14 @@ services:
|
||||
# - ~/.bash_aliases:/home/developer/.bash_aliases:ro
|
||||
# - ~/.inputrc:/home/developer/.inputrc:ro
|
||||
|
||||
# Optional: host-owned shell config + LAN jump overrides (recommended
|
||||
# over the single-file ~/.bash_aliases mount above — it's a directory,
|
||||
# so it survives editors' atomic-save). The image's ~/.bash_aliases
|
||||
# sources ~/.config/devbox-shell/bash_aliases if present, and
|
||||
# setup-lan-access.sh reads ~/.config/devbox-shell/ssh-lan.conf for
|
||||
# named-peer `ProxyJump host` overrides (see ssh-lan.conf.example).
|
||||
# - ~/.config/devbox-shell:/home/developer/.config/devbox-shell:ro
|
||||
|
||||
# Optional: persist uv data (Python installs, tool installs)
|
||||
# Without this, 'uv python install' must be re-run after container removal.
|
||||
- devbox-uv:/home/developer/.local/share/uv
|
||||
@@ -126,6 +139,7 @@ services:
|
||||
volumes:
|
||||
devbox-opencode-config:
|
||||
devbox-pi-config:
|
||||
devbox-ssh-local:
|
||||
devbox-data:
|
||||
devbox-state:
|
||||
devbox-shell-history:
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
# PR-5: Retire pi from opencode-devbox
|
||||
|
||||
After pi-devbox has shipped v1.0.0 as a fully independent image (with
|
||||
its own base + variant Dockerfiles, CI, smoke tests, and docs), the
|
||||
pi-related paths in opencode-devbox become dead weight. This PR
|
||||
removes them.
|
||||
|
||||
## Pre-conditions before merging
|
||||
|
||||
This PR should land **only after** all of the following are stable:
|
||||
|
||||
1. `pi-devbox v1.0.0` published, smoke tests passing, in active use
|
||||
for at least one release cycle.
|
||||
2. Anyone consuming `joakimp/pi-devbox:base-pi-only` directly (e.g.
|
||||
forks pinned to it) has been notified and migrated.
|
||||
3. The deprecation warning (PR-1 of this work — see below) has been
|
||||
live for at least one release cycle so consumers have visible
|
||||
notice.
|
||||
|
||||
## Files / sections to remove from opencode-devbox
|
||||
|
||||
### `Dockerfile.variant`
|
||||
|
||||
Remove these blocks entirely:
|
||||
|
||||
- The `INSTALL_PI` / `PI_VERSION` / `PI_TOOLKIT_REF` /
|
||||
`PI_EXTENSIONS_REF` / `PI_FORK_REPO` / `PI_FORK_REF` /
|
||||
`PI_OBSMEM_REPO` / `PI_OBSMEM_REF` build-args.
|
||||
- The `RUN if [ "${INSTALL_PI}" = "true" ]; then ...` block (entire
|
||||
block — git_clone_retry, git_fetch_ref, npm install
|
||||
pi-coding-agent, the four /opt/pi-* clones, the npm installs in
|
||||
/opt/pi-fork and /opt/pi-observational-memory, and the four
|
||||
rev-parse echoes).
|
||||
- All comments referencing pi-only as "the single source of truth for
|
||||
the pi-devbox image" (the variant matrix table, the explanatory
|
||||
paragraph, and the "rationale" comments at the top of the file
|
||||
about pi-only existing for pi-devbox to FROM).
|
||||
|
||||
Update the variant matrix table at the top of `Dockerfile.variant`:
|
||||
|
||||
```
|
||||
variant INSTALL_OPENCODE INSTALL_OMOS
|
||||
───────────────── ──────────────── ────────────
|
||||
base true false
|
||||
omos true true
|
||||
```
|
||||
|
||||
(only two variants now; pi-only and the with-pi/omos-with-pi axis are
|
||||
gone).
|
||||
|
||||
### `entrypoint-user.sh`
|
||||
|
||||
Remove:
|
||||
|
||||
- The pi-toolkit and pi-extensions install hooks (the section that
|
||||
runs `(cd /opt/pi-toolkit && ./install.sh --yes)` etc.).
|
||||
- The `~/.pi/agent/settings.json` seeding from
|
||||
`/opt/pi-toolkit/settings.example.json`.
|
||||
- Any other pi-conditional blocks (search for `INSTALL_PI`, `pi-toolkit`,
|
||||
`pi-extensions`, `~/.pi/`).
|
||||
|
||||
Verify that the AWS Bedrock auth bootstrap (the pi-toolkit AWS env
|
||||
loader) is not relied on by opencode users. If it is, lift it out of
|
||||
the pi-toolkit dependency (it's small and self-contained).
|
||||
|
||||
### `Dockerfile.base`
|
||||
|
||||
Remove:
|
||||
|
||||
- The `mkdir -p /home/${USER_NAME}/.pi/agent/extensions` line in the
|
||||
standard-directories block. Replace with the equivalent opencode-
|
||||
specific paths if any aren't already present (`~/.config/opencode`
|
||||
is already there).
|
||||
- `NPM_CONFIG_PREFIX=/home/${USER_NAME}/.pi/npm-global` — change to
|
||||
`/home/${USER_NAME}/.config/opencode/npm-global` or a more neutral
|
||||
path. Update the corresponding `PATH` env var.
|
||||
|
||||
Also update the long base-image header comment to remove the
|
||||
"variants for pi-devbox" rationale.
|
||||
|
||||
### CI (`.gitea/workflows/docker-publish-split.yml` or equivalent)
|
||||
|
||||
Remove:
|
||||
|
||||
- The `pi-only` variant build job.
|
||||
- The `with-pi` and `omos-with-pi` variant build jobs (they're
|
||||
redundant with the standalone pi-devbox now).
|
||||
- The `base-pi-only` tag publish step (which pushes to
|
||||
`joakimp/pi-devbox:base-pi-only` from this repo).
|
||||
- The `resolve-pi-version` job step (no longer needed).
|
||||
- Smoke-test invocations with `--variant pi-only`, `--variant with-pi`,
|
||||
`--variant omos-with-pi`.
|
||||
|
||||
Remaining variants in CI: `base`, `omos`. The "with-pi" axis is
|
||||
fully retired.
|
||||
|
||||
### `scripts/smoke-test.sh`
|
||||
|
||||
Remove:
|
||||
|
||||
- The `--variant pi-only`, `--variant with-pi`, `--variant
|
||||
omos-with-pi` branches.
|
||||
- pi-related assertions: `pi --version`, the
|
||||
`~/.pi/agent/extensions/*.ts ≥ 4` check, the mempalace.ts bridge
|
||||
gate (mempalace itself stays, but its bridge into pi is no longer
|
||||
this image's concern).
|
||||
|
||||
Remaining variant axis in smoke tests: `base`, `omos`.
|
||||
|
||||
### `README.md` (and `AGENTS.md`, `DOCKER_HUB.md`)
|
||||
|
||||
- Remove the pi-only variant from the "Image variants" table.
|
||||
- Remove the with-pi / omos-with-pi variants if they were documented.
|
||||
- Remove all sections about pi-toolkit, pi-extensions, pi-fork,
|
||||
pi-observational-memory, ~/.pi paths, and pi-related env vars.
|
||||
- Remove the "this image also produces base-pi-only for pi-devbox"
|
||||
notes.
|
||||
- Add a single-paragraph **"Looking for pi?"** section pointing to
|
||||
`joakimp/pi-devbox`.
|
||||
|
||||
### `Dockerfile` references in `pi-devbox` repo (cleanup of cross-repo coupling)
|
||||
|
||||
This isn't a change to opencode-devbox, but it's part of the same
|
||||
deprecation:
|
||||
|
||||
- Once pi-devbox v1.0.0 is the single source of truth, remove
|
||||
pi-devbox/Dockerfile (the 5-line shim with the long
|
||||
`joakimp/pi-devbox:base-pi-only` rationale comment). It's replaced
|
||||
by `Dockerfile.base` + `Dockerfile.variant` produced by PR-1 of
|
||||
this work.
|
||||
|
||||
## Two-step deprecation path (recommended)
|
||||
|
||||
Rather than a single big-bang removal, use a deprecation cycle:
|
||||
|
||||
### Step 1 — pre-PR (lands at the same time as pi-devbox v1.0.0)
|
||||
|
||||
Add a deprecation warning to opencode-devbox:
|
||||
|
||||
1. **Build-time message** — when `INSTALL_PI=true`,
|
||||
`INSTALL_PI_DEPRECATED=warn` is the default; the variant build
|
||||
prints to stderr:
|
||||
```
|
||||
===========================================================
|
||||
DEPRECATION WARNING: INSTALL_PI is deprecated in opencode-devbox
|
||||
and will be removed in v2.0.0. Use joakimp/pi-devbox:latest
|
||||
instead. See https://gitea.jordbo.se/joakimp/pi-devbox
|
||||
===========================================================
|
||||
```
|
||||
2. **CHANGELOG** entry on opencode-devbox: "INSTALL_PI build-arg path
|
||||
deprecated; will be removed in v2.0.0."
|
||||
3. **README and DOCKER_HUB** updates: mark `pi-only`, `with-pi`,
|
||||
`omos-with-pi` variants as deprecated, point to pi-devbox.
|
||||
4. The `base-pi-only` tag continues to be published but with a
|
||||
notice in the description: "Internal artifact for pi-devbox.
|
||||
Deprecated; pull joakimp/pi-devbox:latest directly."
|
||||
|
||||
### Step 2 — removal PR (this document)
|
||||
|
||||
Lands one release cycle (or one calendar month, whichever is later)
|
||||
after step 1. Removes everything listed in the per-file sections
|
||||
above. Tagged as opencode-devbox v2.0.0 (the major bump signals the
|
||||
breaking change).
|
||||
|
||||
## Risk assessment
|
||||
|
||||
### What could go wrong
|
||||
|
||||
- **Someone is consuming `base-pi-only` directly** without going
|
||||
through pi-devbox. The deprecation warning + one-cycle delay should
|
||||
surface this.
|
||||
- **Mempalace bridge in pi-extensions** — this stays in pi-devbox; no
|
||||
impact on opencode-devbox.
|
||||
- **Shared base assumptions** — opencode-devbox's
|
||||
`~/.pi/npm-global` NPM_CONFIG_PREFIX was a pi-specific design. In
|
||||
the cleanup we move it to a neutral path. Existing opencode-devbox
|
||||
users get a one-time migration: their `npm install -g` packages
|
||||
installed at the old path stop being on PATH. Document this in the
|
||||
v2.0.0 changelog and add a one-liner that copies the old prefix
|
||||
contents to the new one if the old one exists.
|
||||
|
||||
### What's safe
|
||||
|
||||
- The base apt set, the Go-binary installs, MemPalace, the SSH
|
||||
ControlMaster setup, the entrypoint UID/GID dance — all of these
|
||||
stay. They're not pi-specific.
|
||||
- The `omos` variant — fully unaffected.
|
||||
- Existing opencode-only users — no change to their workflow.
|
||||
|
||||
## Verification
|
||||
|
||||
After PR-5 lands, the following should be true:
|
||||
|
||||
- `grep -ri "INSTALL_PI\|pi-toolkit\|pi-extensions\|pi-fork\|pi-observational-memory\|base-pi-only" .` in opencode-devbox returns no matches.
|
||||
- `docker history joakimp/opencode-devbox:latest` shows no pi-related layers.
|
||||
- The opencode-devbox CI matrix builds only `base` and `omos` variants.
|
||||
- pi-devbox CI is unaffected (it's a different repo).
|
||||
- Both repos build cleanly in their own CI without referencing the other.
|
||||
|
||||
## Estimated effort
|
||||
|
||||
- Step 1 (deprecation warnings): ~2 hours.
|
||||
- Step 2 (removal): ~4 hours including local testing of opencode-only
|
||||
build paths.
|
||||
- One release cycle of monitoring between them.
|
||||
|
||||
Total: ~1 working day of focused effort, spread over a calendar month.
|
||||
|
||||
## Order in the broader plan
|
||||
|
||||
1. PR-1 on pi-devbox — copy base + variant Dockerfiles, strip
|
||||
opencode/omos paths, tag v1.0.0.
|
||||
2. PR-2 on pi-devbox — add pandoc, graphviz, imagemagick, tldr, yq.
|
||||
3. PR-3 on pi-devbox — add `:latest-studio` variant.
|
||||
4. (Optional) PR-4 on pi-devbox — add `:latest-studio-tex` variant.
|
||||
5. PR-pre on opencode-devbox — deprecation warnings (step 1 above).
|
||||
6. **PR-5 on opencode-devbox — actual removal (this document, step 2).**
|
||||
|
||||
PRs 1–4 are independent and can land in any order on pi-devbox. PR-pre
|
||||
should land alongside or shortly after pi-devbox v1.0.0 (PR-1) so
|
||||
consumers know to migrate. PR-5 lands one release cycle after PR-pre.
|
||||
@@ -88,6 +88,7 @@ for dir in \
|
||||
/home/"$USER_NAME"/.config/opencode \
|
||||
/home/"$USER_NAME"/.config/nvim \
|
||||
/home/"$USER_NAME"/.pi \
|
||||
/home/"$USER_NAME"/.ssh-local \
|
||||
/home/"$USER_NAME"/.agents/skills; do
|
||||
[ -d "$dir" ] || continue
|
||||
|
||||
|
||||
@@ -97,9 +97,15 @@ mkdir -p "${SSH_LOCAL}/cm" 2>/dev/null || true
|
||||
chmod 700 "${SSH_LOCAL}" "${SSH_LOCAL}/cm" 2>/dev/null || true
|
||||
|
||||
# ── Jump key (generated once; preserved across restarts) ──────────────
|
||||
# Persisted via a named volume on ~/.ssh-local (see compose), so a fresh key
|
||||
# is generated only on the very first start (or if the volume is wiped). When
|
||||
# we DO generate one it must be (re-)authorized on the host, so we flag it and
|
||||
# print a copy-paste authorize line below.
|
||||
KEY_JUST_GENERATED=0
|
||||
if [ ! -f "$KEY" ]; then
|
||||
ssh-keygen -t ed25519 -N '' -C "devbox-jump@${HOSTNAME:-container}" -f "$KEY" >/dev/null 2>&1 || exit 0
|
||||
chmod 600 "$KEY" 2>/dev/null || true
|
||||
KEY_JUST_GENERATED=1
|
||||
fi
|
||||
|
||||
# ── Render the writable config ────────────────────────────────────────
|
||||
@@ -187,18 +193,33 @@ ${INCLUDE_BLOCK}
|
||||
EOF
|
||||
chmod 600 "$CONFIG" 2>/dev/null || true
|
||||
|
||||
# ── One-time hint when we can't authenticate yet ──────────────────────
|
||||
# ── Authorize hints ───────────────────────────────────────────────────
|
||||
# Print the copy-paste authorize line whenever we either (a) can't yet
|
||||
# authenticate (HOST_SSH_USER unset) or (b) just generated a NEW key that the
|
||||
# host won't recognize. With ~/.ssh-local persisted via a named volume, case
|
||||
# (b) fires only on first-ever start (or after the volume is reset) — so this
|
||||
# is normally a one-time, one-line step per machine, with no file to locate.
|
||||
PUBKEY_TEXT="$(cat "${KEY}.pub" 2>/dev/null)"
|
||||
if [ -z "${HOST_SSH_USER:-}" ]; then
|
||||
cat <<EOF
|
||||
[devbox] LAN-access jump config generated at ~/.ssh-local/config, but
|
||||
HOST_SSH_USER is unset so it can't authenticate to the host yet.
|
||||
To enable container -> host -> LAN-peer access:
|
||||
1. Set HOST_SSH_USER=<your host username> in the container env.
|
||||
2. Authorize this key on the host (append to ~/.ssh/authorized_keys):
|
||||
$(cat "${KEY}.pub" 2>/dev/null)
|
||||
2. Authorize this key on the host (run ON THE HOST, once):
|
||||
echo '${PUBKEY_TEXT}' >> ~/.ssh/authorized_keys
|
||||
3. Ensure the host's SSH server (Remote Login) is enabled.
|
||||
Then: dssh host (or add 'ProxyJump host' to targets in ~/.ssh/config)
|
||||
EOF
|
||||
elif [ "$KEY_JUST_GENERATED" = "1" ]; then
|
||||
cat <<EOF
|
||||
[devbox] Generated a NEW LAN-jump key. Authorize it on the host (${HOST_SSH_USER}@host),
|
||||
then 'dssh host' and your LAN peers will work. Run this ONCE, ON THE HOST:
|
||||
echo '${PUBKEY_TEXT}' >> ~/.ssh/authorized_keys
|
||||
(Ensure the host's SSH server / Remote Login is enabled.)
|
||||
This key is persisted in the ~/.ssh-local volume, so you won't need to
|
||||
repeat this on container updates — only if that volume is reset.
|
||||
EOF
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
+55
-11
@@ -30,6 +30,19 @@ fi
|
||||
FAILED=0
|
||||
pass() { echo " ✓ $1"; }
|
||||
fail() { echo " ✗ $1" >&2; FAILED=$((FAILED + 1)); }
|
||||
warn() { echo " ⚠ $1" >&2; }
|
||||
|
||||
# Registration assertions (fork/recall installed by the BASE image's
|
||||
# entrypoint-user.sh via `pi install /opt/<pkg>`) depend on the base, not the
|
||||
# variant layer built here. validate.yml builds variants FROM the published
|
||||
# base-latest, which can lag the entrypoint in the current commit (the base
|
||||
# only rebuilds on a release tag), so a stale base-latest would red the
|
||||
# push-to-main run with a false negative. These checks are therefore warn-only
|
||||
# by default; the release pipeline (docker-publish-split.yml) builds the base
|
||||
# fresh in the same run and sets STRICT_REGISTRATION=1 to enforce them hard.
|
||||
# The build-time /opt + node_modules checks below stay hard in every path —
|
||||
# those are produced by the variant layer and must always be correct.
|
||||
STRICT_REGISTRATION="${STRICT_REGISTRATION:-0}"
|
||||
|
||||
run() {
|
||||
# Run a command inside the image and capture its output.
|
||||
@@ -187,10 +200,21 @@ if docker run --rm --entrypoint="" "$IMAGE" sh -c "command -v pi" >/dev/null 2>&
|
||||
trap 'docker rm -f "$CID" >/dev/null 2>&1 || true' EXIT
|
||||
|
||||
# Wait for entrypoint-user.sh to finish deploying pi-toolkit + extensions.
|
||||
# Marker: keybindings.json symlink lands once pi-toolkit/install.sh has run.
|
||||
# Up to 30s — omos-with-pi has more setup work than base+pi.
|
||||
for _ in $(seq 1 30); do
|
||||
if docker exec "$CID" test -L /home/developer/.pi/agent/keybindings.json 2>/dev/null; then
|
||||
# The deploy order is: pi-toolkit (writes keybindings.json) -> pi-extensions
|
||||
# (symlinks its *.ts) -> settings.json -> mempalace.ts bridge (LAST). Gating
|
||||
# only on keybindings.json races: it lands when pi-toolkit finishes, before
|
||||
# pi-extensions has symlinked its *.ts, so the "*.ts >= 4" check below could
|
||||
# sample mid-deploy under parallel build load (observed v1.16.2 run 370:
|
||||
# smoke-with-pi saw <4 .ts while omos-with-pi/pi-only saw 8). Wait for the
|
||||
# LAST-deployed artifact (the mempalace.ts bridge symlink) AND a settled
|
||||
# extension count so the deploy is fully complete before any assertion runs.
|
||||
# Up to 45s — pi-bearing variants have more setup work under load.
|
||||
for _ in $(seq 1 45); do
|
||||
if docker exec "$CID" sh -c \
|
||||
'test -L $HOME/.pi/agent/keybindings.json && \
|
||||
test -L $HOME/.pi/agent/extensions/mempalace.ts && \
|
||||
[ "$(ls -1 $HOME/.pi/agent/extensions/*.ts 2>/dev/null | wc -l)" -ge 4 ]' \
|
||||
2>/dev/null; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
@@ -206,6 +230,19 @@ if docker run --rm --entrypoint="" "$IMAGE" sh -c "command -v pi" >/dev/null 2>&
|
||||
fi
|
||||
}
|
||||
|
||||
# Like exec_test but warn-only unless STRICT_REGISTRATION=1 (see note at top).
|
||||
exec_test_reg() {
|
||||
local label="$1"; shift
|
||||
local out
|
||||
if out=$(docker exec -u developer "$CID" sh -c "$*" 2>&1); then
|
||||
pass "$label ($(echo "$out" | head -1))"
|
||||
elif [ "$STRICT_REGISTRATION" = "1" ]; then
|
||||
fail "$label: $out"
|
||||
else
|
||||
warn "$label (warn-only — stale base-latest? set STRICT_REGISTRATION=1 to enforce): $out"
|
||||
fi
|
||||
}
|
||||
|
||||
exec_test "~/.pi/agent/keybindings.json (pi-toolkit)" \
|
||||
'test -L $HOME/.pi/agent/keybindings.json && echo ok'
|
||||
exec_test "~/.pi/agent/extensions/*.ts ≥ 4 (pi-extensions)" \
|
||||
@@ -225,9 +262,9 @@ if docker run --rm --entrypoint="" "$IMAGE" sh -c "command -v pi" >/dev/null 2>&
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
exec_test "pi-fork registered in settings.json (fork tool)" \
|
||||
exec_test_reg "pi-fork registered in settings.json (fork tool)" \
|
||||
'grep -q pi-fork $HOME/.pi/agent/settings.json && echo ok'
|
||||
exec_test "pi-observational-memory registered in settings.json (recall tool)" \
|
||||
exec_test_reg "pi-observational-memory registered in settings.json (recall tool)" \
|
||||
'grep -q pi-observational-memory $HOME/.pi/agent/settings.json && echo ok'
|
||||
|
||||
docker rm -f "$CID" >/dev/null 2>&1 || true
|
||||
@@ -366,13 +403,20 @@ echo " Uncompressed size: ${SIZE_MB} MB"
|
||||
# deliberately zero-headroom 2500 ceiling and skipping promote-base-latest.
|
||||
# omos variant to ~3.1 GB. Functional smoke checks all pass; this is a
|
||||
# guardrail, not a performance limit.
|
||||
THRESHOLD=2600
|
||||
[ "$VARIANT" = "omos" ] && THRESHOLD=3300
|
||||
[ "$VARIANT" = "with-pi" ] && THRESHOLD=2900
|
||||
[ "$VARIANT" = "omos-with-pi" ] && THRESHOLD=3900
|
||||
# v1.16.2: all thresholds bumped +150 MB preemptively ahead of the combined
|
||||
# opencode 1.15.13->1.16.2 (minor) + pi 0.78.1->0.79.0 (minor) bump. Both
|
||||
# base (2506/2600) and omos (3206/3300) were sitting on ~94 MB headroom and
|
||||
# a minor opencode bump has tripped them before (v1.15.0 omos, v1.15.4
|
||||
# omos-with-pi). Restoring ~250 MB headroom avoids a partial-publish +
|
||||
# letter-suffix recovery cycle. CI's smoke size print + resolved-versions
|
||||
# table records the actual landed sizes; tighten later if they come in low.
|
||||
THRESHOLD=2750
|
||||
[ "$VARIANT" = "omos" ] && THRESHOLD=3450
|
||||
[ "$VARIANT" = "with-pi" ] && THRESHOLD=3050
|
||||
[ "$VARIANT" = "omos-with-pi" ] && THRESHOLD=4050
|
||||
# pi-only = with-pi minus opencode (its platform binary is ~145 MB), so it
|
||||
# lands a bit under base. Threshold 2750 leaves the same headroom pattern.
|
||||
[ "$VARIANT" = "pi-only" ] && THRESHOLD=2750
|
||||
[ "$VARIANT" = "pi-only" ] && THRESHOLD=2850
|
||||
if [ "$SIZE_MB" -gt "$THRESHOLD" ]; then
|
||||
fail "image size ${SIZE_MB} MB exceeds threshold ${THRESHOLD} MB for variant=$VARIANT"
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user