feat: repurpose .openharness as install root
Deletes the in-repo .openharness/ subdir, moves composeOverrides config to repo-root config.json (gitignored, seeded from config.example.json), and reworks scripts/install.sh to clone into ~/.openharness/ with an in-place migration for existing ~/openharness/ checkouts. Closes #251 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
f27e3aadf1
commit
7bb3c4b3f0
31 files changed
+124
-90
No files matched your search
@@ -136,7 +136,6 @@ LABEL devcontainer.metadata='[{"remoteUser":"sandbox","workspaceFolder":"/home/s
|
||||
# ─── Copy install scripts + workspace (for CI; bind mount overrides at runtime)
|
||||
COPY --chown=sandbox:sandbox install/ /home/sandbox/install/
|
||||
RUN chmod +x /home/sandbox/install/*.sh
|
||||
COPY --chown=sandbox:sandbox .openharness/ /home/sandbox/.openharness/
|
||||
COPY --chown=sandbox:sandbox workspace/ /home/sandbox/harness/workspace/
|
||||
|
||||
# ─── Entrypoint ────────────────────────────────────────────────────
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
# rewrite host file ownership). The chown loop only matches directories,
|
||||
# so ~/.claude.json (a file) is untouched regardless.
|
||||
#
|
||||
# Usage: enable via .openharness/config.json composeOverrides[]:
|
||||
# Usage: enable via config.json composeOverrides[]:
|
||||
# ".devcontainer/docker-compose.claude-host.yml"
|
||||
|
||||
services:
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
# into a container that may execute untrusted agent code. Enable only for
|
||||
# trusted workflows. OAuth login is the expected default auth path for
|
||||
# codex in this harness, so the overlay is enabled by default in
|
||||
# .openharness/config.json.
|
||||
# config.json.
|
||||
#
|
||||
# Bleed-through: ~/.codex/sessions/, history.jsonl, and memories/ from
|
||||
# other host work will be visible inside the sandbox. Acknowledged cost
|
||||
@@ -43,7 +43,7 @@
|
||||
# skips its recursive chown on /home/sandbox/.codex (which would otherwise
|
||||
# rewrite host file ownership).
|
||||
#
|
||||
# Usage: enable via .openharness/config.json composeOverrides[]:
|
||||
# Usage: enable via config.json composeOverrides[]:
|
||||
# ".devcontainer/docker-compose.codex-host.yml"
|
||||
|
||||
services:
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
# Provider keys are NOT OAuth tokens — they typically have no expiry and
|
||||
# no per-request scope, so a leak from this overlay has a wider blast
|
||||
# radius than `claude-host` or `codex-host`. The overlay is opt-in: it is
|
||||
# NOT listed in the default `.openharness/config.json` `composeOverrides`.
|
||||
# NOT listed in the default `config.json` `composeOverrides`.
|
||||
#
|
||||
# Bleed-through: ~/.deepagents/sessions/, memory, and skills from other
|
||||
# host work will be visible inside the sandbox. Acknowledged cost of
|
||||
@@ -48,7 +48,7 @@
|
||||
# stays in the base compose — it becomes orphaned (unmounted) while this
|
||||
# overlay is active and is reinstated when the overlay is removed. To
|
||||
# return to the named volume, remove this overlay from
|
||||
# `.openharness/config.json` `composeOverrides` and rerun `make sandbox`
|
||||
# `config.json` `composeOverrides` and rerun `make sandbox`
|
||||
# (or `make destroy && make sandbox` if the container is already up).
|
||||
# Resetting the `deepagents-auth` named volume only (without touching
|
||||
# host `~/.deepagents`) is `docker volume rm <project>_deepagents-auth`.
|
||||
@@ -57,7 +57,7 @@
|
||||
# entrypoint.sh skips its recursive chown on /home/sandbox/.deepagents
|
||||
# (which would otherwise rewrite host file ownership).
|
||||
#
|
||||
# Usage: enable via .openharness/config.json composeOverrides[]:
|
||||
# Usage: enable via config.json composeOverrides[]:
|
||||
# ".devcontainer/docker-compose.deepagents-host.yml"
|
||||
|
||||
services:
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
# into a container that may execute untrusted agent code. Enable only for
|
||||
# trusted workflows. OpenAI OAuth through OpenCode's /connect flow is the
|
||||
# expected default auth path for opencode in this harness, so the overlay
|
||||
# is enabled by default in .openharness/config.json.
|
||||
# is enabled by default in config.json.
|
||||
#
|
||||
# Permissions: Read-write (sandbox writes host sessions/provider state).
|
||||
# Replaces: opencode-auth named volume from base docker-compose.yml at the
|
||||
@@ -36,7 +36,7 @@
|
||||
# entrypoint.sh skips recursive chown on /home/sandbox/.local/share/opencode
|
||||
# (which would otherwise rewrite host file ownership).
|
||||
#
|
||||
# Usage: enable via .openharness/config.json composeOverrides[]:
|
||||
# Usage: enable via config.json composeOverrides[]:
|
||||
# ".devcontainer/docker-compose.opencode-host.yml"
|
||||
|
||||
services:
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
# into a container that may execute untrusted agent code. Enable only
|
||||
# for trusted workflows. OAuth login is the expected default auth path
|
||||
# for pi in this harness, so the overlay is enabled by default in
|
||||
# .openharness/config.json.
|
||||
# config.json.
|
||||
#
|
||||
# Bleed-through: sessions and identities from other host work will be
|
||||
# visible inside the sandbox. Acknowledged cost of full-mount convenience.
|
||||
@@ -38,7 +38,7 @@
|
||||
# skips its recursive chown on /home/sandbox/.pi (which would otherwise
|
||||
# rewrite host file ownership).
|
||||
#
|
||||
# Usage: enable via .openharness/config.json composeOverrides[]:
|
||||
# Usage: enable via config.json composeOverrides[]:
|
||||
# ".devcontainer/docker-compose.pi-host.yml"
|
||||
|
||||
services:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Purpose: Run sshd as the main process, replacing the sleep-infinity default.
|
||||
# Enables: Remote SSH access to the sandbox container (host port 2222 → container port 22)
|
||||
# Auth: Entrypoint auto-configures password auth (SANDBOX_PASSWORD env var) and host keys on startup
|
||||
# Requirements: Add to .openharness/config.json composeOverrides to activate
|
||||
# Requirements: Add to config.json composeOverrides to activate
|
||||
# Usage: docker compose -f docker-compose.yml -f docker-compose.sshd.yml up
|
||||
# Provides: SSH access at ssh -p 2222 sandbox@localhost with password from SANDBOX_PASSWORD
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#
|
||||
# Core volumes:
|
||||
# - ..:/home/sandbox/harness — Project root (bind-mount)
|
||||
# - ../.openharness:/home/sandbox/.openharness — Config overlay
|
||||
# - /var/run/docker.sock:/var/run/docker.sock — Docker daemon access (nested docker)
|
||||
# - claude-auth:/home/sandbox/.claude — Persistent Claude CLI auth
|
||||
# - codex-auth:/home/sandbox/.codex — Persistent Codex CLI auth
|
||||
@@ -19,7 +18,7 @@
|
||||
# - cloudflared-auth:/home/sandbox/.cloudflared — Cloudflare credentials
|
||||
# - gh-config:/home/sandbox/.config/gh — GitHub CLI config
|
||||
#
|
||||
# Core env vars (see .openharness/config.json for overlay toggles):
|
||||
# Core env vars (see config.json for overlay toggles):
|
||||
# - TZ: Timezone (default America/Denver)
|
||||
# - GIT_USER_NAME, GIT_USER_EMAIL: Git commit author
|
||||
# - GH_TOKEN: GitHub token for `gh auth` automation
|
||||
@@ -35,7 +34,7 @@
|
||||
# -f .devcontainer/docker-compose.postgres.yml \
|
||||
# -f .devcontainer/docker-compose.slack.yml up -d
|
||||
#
|
||||
# Overlays in .openharness/config.json composeOverrides are auto-loaded by `make sandbox` and `scripts/install.sh` via jq.
|
||||
# Overlays in config.json composeOverrides are auto-loaded by `make sandbox` and `scripts/install.sh` via jq.
|
||||
|
||||
name: ${SANDBOX_NAME:-openharness}
|
||||
|
||||
@@ -47,7 +46,6 @@ services:
|
||||
dockerfile: .devcontainer/Dockerfile
|
||||
volumes:
|
||||
- ..:/home/sandbox/harness
|
||||
- ../.openharness:/home/sandbox/.openharness
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- claude-auth:/home/sandbox/.claude
|
||||
- codex-auth:/home/sandbox/.codex
|
||||
|
||||
@@ -18,7 +18,7 @@ fi
|
||||
# host file ownership (see docker-compose.{claude,codex,pi,deepagents,opencode}-host.yml).
|
||||
# Harness packs that introduce their own host-mount overlays should add
|
||||
# their own skip clauses or run a *-entrypoint-hook.sh.
|
||||
for dir in .claude .codex .pi .deepagents .cloudflared .config/gh .ssh .openharness; do
|
||||
for dir in .claude .codex .pi .deepagents .cloudflared .config/gh .ssh; do
|
||||
if [ -d "/home/sandbox/$dir" ]; then
|
||||
if [ "$dir" = ".claude" ] && [ "${CLAUDE_HOST_BIND_MOUNT:-0}" = "1" ]; then
|
||||
continue
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
**/.env*
|
||||
/config.json
|
||||
.worktrees/*
|
||||
.claude/worktrees/
|
||||
tasks/*/archive/
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# `.openharness/`
|
||||
|
||||
Host-side configuration mounted into the sandbox container at `/home/sandbox/.openharness/`. Read by the install flow and the container build; the directory is bind-mounted by `.devcontainer/docker-compose.yml`.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Tracked? | Consumer | Purpose |
|
||||
|---|---|---|---|
|
||||
| `config.json` | yes | `install.sh`, `install/banner.sh`, all `docker-compose.*-host.yml` headers | `composeOverrides[]` — list of overlay compose files to apply on top of the base `docker-compose.yml`. Modified by `install.sh` at install time when host UID ≠ 1000. |
|
||||
| `.gitignore` | yes | git | Excludes per-host runtime artifacts. |
|
||||
| `agent/auth.json` | no (gitignored via root `**/auth.json`) | mifune pi pack runtime | Per-user credential symlink. Only present if a pack that needs it is installed. |
|
||||
|
||||
## Conventions
|
||||
|
||||
- **Hand-edit `config.json`** to opt into / out of overlays. Each overlay's own header documents its requirements.
|
||||
- **Do not commit** anything in this directory beyond `config.json`, `.gitignore`, and this README.
|
||||
|
||||
## See also
|
||||
|
||||
- `.devcontainer/docker-compose.yml` (mount point + base service)
|
||||
- `.devcontainer/Dockerfile` (`COPY .openharness/`)
|
||||
- `docs/guide/overlays.md` (overlay catalog)
|
||||
@@ -1 +0,0 @@
|
||||
../workspace/.claude/rules
|
||||
@@ -21,6 +21,10 @@ Update policy and release automation live in [`.claude/rules/git.md`](.claude/ru
|
||||
- Top-level `Makefile` wrapping `docker compose -f .devcontainer/docker-compose.yml` so users type `make sandbox` / `make shell` / `make destroy` instead of the 50-character compose lines. Targets: `sandbox`, `shell`, `destroy`, `stop`, `logs`, `ps`, `restart`, `help` (default, self-documenting via awk). Uses `-include .devcontainer/.env` so `SANDBOX_NAME` flows through. No agent-CLI targets — the harness's value is sandbox isolation, agent choice (`claude` / `codex` / `pi`) happens inside `make shell`.
|
||||
|
||||
### Changed
|
||||
- **BREAKING**: Installer now clones to `~/.openharness/` (was `~/openharness/`); `scripts/install.sh` auto-migrates existing `~/openharness/` checkouts in place.
|
||||
- **BREAKING**: In-repo `.openharness/` subdirectory removed entirely.
|
||||
- **BREAKING**: `composeOverrides[]` config moved from `.openharness/config.json` to repo-root `config.json` (gitignored, seeded from tracked `config.example.json`).
|
||||
- **BREAKING**: `pi-host.yml` dropped from default `composeOverrides` — Pi users add it back manually in `config.json`.
|
||||
- `/cloudflared-tunnel` skill reconstructs a missing `<tunnel-id>.json` from `cloudflared tunnel token` instead of failing — fixes the case where a tunnel exists in the Cloudflare account (e.g. created on another host) but only `cert.pem` is on the running machine. Documents that token-mode runs (`--token <T>`) silently drop the local `--url` flag, leaving Cloudflare to return 530s while connections register. ([#249](https://github.com/ryaneggz/open-harness/issues/249))
|
||||
- Quickstart collapsed from two parallel paths (installer + manual) to a single linear flow in `README.md` and `docs/quickstart.md`. Manual setup demoted to a `<details>` block. New "Configuration" section in both docs surfaces `.devcontainer/.env` as the customization surface (`make destroy && make sandbox` to re-apply).
|
||||
- `install.sh` moved to `scripts/install.sh` to colocate with `cron-runtime.ts` and `ralph.sh` per the orchestrator-scripts convention. Repo-detection logic and help-text self-references updated for the new path. **Action required for the curl-pipe URL**: update the `https://oh.mifune.dev/install.sh` redirect to `https://raw.githubusercontent.com/ryaneggz/open-harness/refs/heads/main/scripts/install.sh`.
|
||||
|
||||
@@ -6,20 +6,25 @@
|
||||
SANDBOX_NAME ?= openharness
|
||||
COMPOSE_BASE := -f .devcontainer/docker-compose.yml
|
||||
COMPOSE_OVERRIDES := $(shell jq -r '.composeOverrides[]?' \
|
||||
.openharness/config.json 2>/dev/null | sed 's|^|-f |' | tr '\n' ' ')
|
||||
config.json 2>/dev/null | sed 's|^|-f |' | tr '\n' ' ')
|
||||
COMPOSE := docker compose $(COMPOSE_BASE) $(COMPOSE_OVERRIDES)
|
||||
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
.PHONY: sandbox shell destroy stop logs ps restart help _check-jq
|
||||
.PHONY: sandbox shell destroy stop logs ps restart help _check-jq _check-config
|
||||
|
||||
_check-jq:
|
||||
@command -v jq >/dev/null 2>&1 || { \
|
||||
echo "ERROR: jq is required to expand .openharness/config.json composeOverrides."; \
|
||||
echo "ERROR: jq is required to expand config.json composeOverrides."; \
|
||||
echo "Install jq (e.g. apt install jq, brew install jq) and re-run."; \
|
||||
exit 1; }
|
||||
|
||||
sandbox: _check-jq ## Provision and start the sandbox
|
||||
_check-config:
|
||||
@[ -f config.json ] || { \
|
||||
echo "ERROR: config.json missing. Run scripts/install.sh, OR: cp config.example.json config.json"; \
|
||||
exit 1; }
|
||||
|
||||
sandbox: _check-jq _check-config ## Provision and start the sandbox
|
||||
$(COMPOSE) up -d --build
|
||||
|
||||
shell: ## Connect to the sandbox (agent choice happens inside)
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
curl -fsSL https://oh.mifune.dev/install.sh | bash
|
||||
```
|
||||
|
||||
The installer clones into `~/openharness`, offers to share your host
|
||||
The installer clones into `~/.openharness`, offers to share your host
|
||||
`gh` token, writes `.devcontainer/.env`, and builds the image (~10 min
|
||||
cold, ~30s warm). Only host dependency: [Docker](https://docs.docker.com/get-docker/).
|
||||
|
||||
## 🚀 Use it
|
||||
|
||||
```bash
|
||||
cd ~/openharness
|
||||
cd ~/.openharness
|
||||
make shell # enter the isolated sandbox
|
||||
# inside the sandbox, launch any core agent:
|
||||
# claude # Claude Code (default)
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
"composeOverrides": [
|
||||
".devcontainer/docker-compose.claude-host.yml",
|
||||
".devcontainer/docker-compose.codex-host.yml",
|
||||
".devcontainer/docker-compose.pi-host.yml",
|
||||
".devcontainer/docker-compose.opencode-host.yml"
|
||||
]
|
||||
}
|
||||
@@ -36,7 +36,7 @@ Key configuration choices:
|
||||
- `command: sleep infinity` — keeps the container alive; actual work happens inside named tmux sessions, not in the foreground process.
|
||||
- `stdin_open: true` and `tty: true` — required for interactive shells via `docker exec`.
|
||||
|
||||
Optional overlays in `.devcontainer/` add services (Postgres, Cloudflare tunnel, SSH daemon, Slack bot) and are activated by listing them in `.openharness/config.json` under `composeOverrides`. `make sandbox` and `scripts/install.sh` read that file via `jq` and pass the corresponding `-f` flags to `docker compose up`.
|
||||
Optional overlays in `.devcontainer/` add services (Postgres, Cloudflare tunnel, SSH daemon, Slack bot) and are activated by listing them in `config.json` (at the repo root, gitignored — copy from `config.example.json` if missing) under `composeOverrides`. `make sandbox` and `scripts/install.sh` read that file via `jq` and pass the corresponding `-f` flags to `docker compose up`.
|
||||
|
||||
## Bind Mounts
|
||||
|
||||
@@ -45,7 +45,6 @@ The container mounts the project root and several named volumes:
|
||||
| Mount | Host path | Container path | Purpose |
|
||||
|-------|-----------|---------------|---------|
|
||||
| Project root | `..` (repo root) | `/home/sandbox/harness` | Source code, worktrees, workspace |
|
||||
| OH config | `../.openharness` | `/home/sandbox/.openharness` | Sandbox config, compose overlay selection |
|
||||
| Docker socket | `/var/run/docker.sock` | `/var/run/docker.sock` | Nested docker for the `oh` CLI |
|
||||
| Claude auth | named volume `claude-auth` | `/home/sandbox/.claude` | Claude Code credentials — persists across rebuilds |
|
||||
| Pi auth | named volume `pi-auth` | `/home/sandbox/.pi` | Pi Agent OAuth tokens |
|
||||
@@ -98,14 +97,13 @@ open-harness/
|
||||
├── .github/
|
||||
│ ├── workflows/ # CI: lint, test, build, release, docs deploy
|
||||
│ └── ISSUE_TEMPLATE/ # agent / audit / bug / feature / skill / task
|
||||
├── config.json # composeOverrides[] consumed by Makefile + install.sh (gitignored; copy from config.example.json)
|
||||
├── .devcontainer/
|
||||
│ ├── Dockerfile # Debian + Node 22 + agent CLIs + pnpm
|
||||
│ ├── docker-compose.yml # base compose
|
||||
│ ├── docker-compose.*.yml # overlays (postgres, cloudflared, ssh, …)
|
||||
│ ├── entrypoint.sh # boot: docker GID, cron runtime, banner
|
||||
│ └── .example.env # template copied by install.sh on first run
|
||||
├── .openharness/
|
||||
│ └── config.json # composeOverrides[] consumed by Makefile + install.sh
|
||||
├── .claude/
|
||||
│ ├── rules/ # auto-loaded coding/process rules
|
||||
│ ├── skills/ # /release, /ci-status, /cloudflared-tunnel, /agent-browser
|
||||
|
||||
@@ -10,7 +10,7 @@ Clone the pack into your sandbox workspace and follow its README:
|
||||
git clone https://github.com/<owner>/<repo> workspace/<pack-name>
|
||||
```
|
||||
|
||||
The pack's README is the source of truth for what to do next — typically: register any compose overlays in `.openharness/config.json`, run an install script to pull agent CLIs, and copy any seed files into `workspace/`. Each pack documents its own steps so the harness itself stays minimal.
|
||||
The pack's README is the source of truth for what to do next — typically: register any compose overlays in `config.json` (at the repo root, gitignored — copy from `config.example.json` if missing), run an install script to pull agent CLIs, and copy any seed files into `workspace/`. Each pack documents its own steps so the harness itself stays minimal.
|
||||
|
||||
## Pack layout
|
||||
|
||||
@@ -22,7 +22,7 @@ A pack is a self-contained directory. Conventional layout:
|
||||
harness.json # Manifest (optional, informational)
|
||||
install-hook.sh # Pulls agent CLIs / sets up the sandbox
|
||||
entrypoint-hook.sh # Sourced by openharness entrypoint at runtime
|
||||
overlays/*.yml # Compose overlays — register in .openharness/config.json
|
||||
overlays/*.yml # Compose overlays — register in config.json
|
||||
workspace-seed/ # Files to copy into the sandbox workspace
|
||||
Dockerfile # OPTIONAL: derived image FROM openharness base
|
||||
```
|
||||
@@ -53,7 +53,7 @@ A pack MAY ship a `harness.json` describing itself for discoverability. The file
|
||||
| `description` | string | One-line summary. |
|
||||
| `openharness` | string | Minimum compatible openharness version (semver range). |
|
||||
| `agents` | string[] | Agent CLIs the pack wires in. Informational. |
|
||||
| `compose_overlays` | string[] | Paths-within-pack to docker-compose YAML overlays — user adds these to `.openharness/config.json`. |
|
||||
| `compose_overlays` | string[] | Paths-within-pack to docker-compose YAML overlays — user adds these to `config.json`. |
|
||||
| `prebuilt_image` | string? | Optional GHCR image tag for a fast Docker-only on-ramp. |
|
||||
|
||||
## Distribution
|
||||
|
||||
@@ -55,7 +55,7 @@ Only applies when the `deepagents-host` overlay is enabled. See [overlays](./ove
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `HOST_DEEPAGENTS_DIR` | `~/.deepagents` | Path to your host DeepAgents directory. The overlay bind-mounts it RW into `/home/sandbox/.deepagents`. **Setting this alone does nothing** — the overlay must also be listed in `.openharness/config.json` `composeOverrides`. |
|
||||
| `HOST_DEEPAGENTS_DIR` | `~/.deepagents` | Path to your host DeepAgents directory. The overlay bind-mounts it RW into `/home/sandbox/.deepagents`. **Setting this alone does nothing** — the overlay must also be listed in `config.json` `composeOverrides`. |
|
||||
|
||||
### SSH (overlay)
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ Run multiple sandboxes on a single host, each reachable via SSH on a unique port
|
||||
|
||||
### 1. Enable the sshd overlay
|
||||
|
||||
Add to each sandbox's `.openharness/config.json`:
|
||||
Add to each sandbox's `config.json` (at the repo root, gitignored — copy from `config.example.json` if missing):
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ sidebar_position: 2
|
||||
|
||||
All sandboxes are built from `.devcontainer/Dockerfile` — a Debian Bookworm image with Node.js 22, pnpm, agent CLIs (Claude Code, Codex, OpenCode, Pi), and dev tools pre-installed.
|
||||
|
||||
Compose overlays in `.devcontainer/` add optional services or host-state mounts. Enable or disable them in `.openharness/config.json`.
|
||||
Compose overlays in `.devcontainer/` add optional services or host-state mounts. Enable or disable them in `config.json` (at the repo root, gitignored — copy from `config.example.json` if missing).
|
||||
|
||||
## Available overlays
|
||||
|
||||
@@ -27,7 +27,7 @@ Compose overlays in `.devcontainer/` add optional services or host-state mounts.
|
||||
|
||||
## Configuration
|
||||
|
||||
Edit `.openharness/config.json` to enable or disable overlays:
|
||||
Edit `config.json` (at the repo root, gitignored — copy from `config.example.json` if missing) to enable or disable overlays:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -72,7 +72,7 @@ The password is set from the `SANDBOX_PASSWORD` environment variable (default: `
|
||||
|
||||
To add PostgreSQL to your sandbox:
|
||||
|
||||
1. Add `".devcontainer/docker-compose.postgres.yml"` to `composeOverrides` in `.openharness/config.json`
|
||||
1. Add `".devcontainer/docker-compose.postgres.yml"` to `composeOverrides` in `config.json`
|
||||
2. Run `make sandbox`
|
||||
3. The database is available at `postgresql://sandbox:sandbox@postgres:5432/sandbox`
|
||||
|
||||
@@ -184,7 +184,7 @@ your host state:
|
||||
`config.toml` (CLI defaults), memory, skills, sessions
|
||||
|
||||
The overlay is **opt-in** and is NOT listed in the default
|
||||
`.openharness/config.json` `composeOverrides`. DeepAgents itself is an
|
||||
`config.json` `composeOverrides`. DeepAgents itself is an
|
||||
optional supported runtime; Claude remains the default agent path.
|
||||
|
||||
Enable it by adding the overlay path to your existing
|
||||
@@ -239,7 +239,7 @@ mkdir -p ~/.deepagents
|
||||
**Disabling the overlay (returning to the named volume):**
|
||||
|
||||
1. Remove `".devcontainer/docker-compose.deepagents-host.yml"` from
|
||||
`composeOverrides` in `.openharness/config.json`.
|
||||
`composeOverrides` in `config.json`.
|
||||
2. Run `make sandbox` (or `make destroy && make sandbox`).
|
||||
|
||||
The base `deepagents-auth` named volume reattaches at
|
||||
|
||||
@@ -49,7 +49,7 @@ The `docker` compose overlay mounts `/var/run/docker.sock` into the container, a
|
||||
- Manage sibling containers on the same host
|
||||
- Access `host.docker.internal` for host networking
|
||||
|
||||
This is enabled by default. If your agent doesn't need Docker access, remove the docker overlay from `.openharness/config.json`.
|
||||
This is enabled by default. If your agent doesn't need Docker access, remove the docker overlay from `config.json`.
|
||||
|
||||
## Isolation model
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ curl -fsSL https://oh.mifune.dev/install.sh | bash
|
||||
The installer:
|
||||
|
||||
1. Verifies Docker and git are present.
|
||||
2. Clones the repo into `~/openharness` (or pulls latest if the directory already exists).
|
||||
2. Clones the repo into `~/.openharness` (or pulls latest if the directory already exists).
|
||||
3. Prompts for `SANDBOX_NAME` and `SANDBOX_PASSWORD`, then writes `.devcontainer/.env`.
|
||||
4. Runs `docker compose -f .devcontainer/docker-compose.yml up -d --build`.
|
||||
5. Prints the next-step commands (open a shell, stop, tear down).
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ deepagents # optional runtime; configure provider keys (see below)
|
||||
|
||||
OpenCode's recommended default for ChatGPT Plus and Pro users is OpenAI OAuth through `/connect` → OpenAI. It writes auth to `~/.local/share/opencode/auth.json`.
|
||||
|
||||
Each agent writes tokens to its directory under the sandbox home (`~/.claude/`, `~/.codex/`, `~/.local/share/opencode/`, `~/.pi/`, `~/.deepagents/`). With persistent named volumes, credentials survive container rebuilds. The `*-host` overlays (Claude/Codex/OpenCode/Pi enabled by default in `.openharness/config.json`; DeepAgents opt-in) bind-mount matching host directories so your laptop's existing auth carries straight in — see [provision.md](./operations/provision.md).
|
||||
Each agent writes tokens to its directory under the sandbox home (`~/.claude/`, `~/.codex/`, `~/.local/share/opencode/`, `~/.pi/`, `~/.deepagents/`). With persistent named volumes, credentials survive container rebuilds. The `*-host` overlays (Claude/Codex/OpenCode/Pi enabled by default in `config.json`; DeepAgents opt-in) bind-mount matching host directories so your laptop's existing auth carries straight in — see [provision.md](./operations/provision.md).
|
||||
|
||||
DeepAgents is an optional supported runtime; Claude remains the default. DeepAgents reads provider keys from `~/.deepagents/.env`. Create it on first use:
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@ Stops the container(s), removes the network, and **removes named volumes** decla
|
||||
|
||||
## With overlays
|
||||
|
||||
If you provisioned with overlays from `.openharness/config.json`, pass them to `down` as well so all services from the merged compose graph are cleaned up:
|
||||
If you provisioned with overlays from `config.json`, pass them to `down` as well so all services from the merged compose graph are cleaned up:
|
||||
|
||||
```bash
|
||||
COMPOSE_FILES="-f .devcontainer/docker-compose.yml"
|
||||
for f in $(jq -r '.composeOverrides[]' .openharness/config.json); do
|
||||
for f in $(jq -r '.composeOverrides[]' config.json); do
|
||||
COMPOSE_FILES="$COMPOSE_FILES -f $f"
|
||||
done
|
||||
docker compose --env-file .devcontainer/.env $COMPOSE_FILES down -v
|
||||
|
||||
@@ -12,7 +12,7 @@ That single command builds the image (Node 22, agent CLIs, procps, tmux), starts
|
||||
|
||||
## Compose overlays
|
||||
|
||||
Overlays live in `.devcontainer/docker-compose.*.yml` and are selected through `.openharness/config.json` → `composeOverrides`. Each entry adds another `-f <file>` to the compose invocation.
|
||||
Overlays live in `.devcontainer/docker-compose.*.yml` and are selected through `config.json` (at the repo root, gitignored — copy from `config.example.json` if missing) → `composeOverrides`. Each entry adds another `-f <file>` to the compose invocation.
|
||||
|
||||
| Overlay | Purpose |
|
||||
|---|---|
|
||||
@@ -31,7 +31,7 @@ Overlays live in `.devcontainer/docker-compose.*.yml` and are selected through `
|
||||
|
||||
> The `claude-host`, `codex-host`, `opencode-host`, `pi-host`, and `deepagents-host` overlays all require host UID = 1000 and pre-existing host directories (`~/.claude` + `~/.claude.json` for claude; `~/.codex` for codex; `~/.local/share/opencode` for OpenCode; `~/.pi` for pi; `~/.deepagents` for deepagents). See the trust tradeoffs in each `.devcontainer/docker-compose.*-host.yml` file. `scripts/install.sh` pre-creates the host directories and strips `*-host.yml` overlays from `composeOverrides` automatically when host UID is not 1000.
|
||||
|
||||
To enable an overlay, add its path to `.openharness/config.json`:
|
||||
To enable an overlay, add its path to `config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -46,7 +46,7 @@ Then provision with the overlay list expanded:
|
||||
|
||||
```bash
|
||||
COMPOSE_FILES="-f .devcontainer/docker-compose.yml"
|
||||
for f in $(jq -r '.composeOverrides[]' .openharness/config.json); do
|
||||
for f in $(jq -r '.composeOverrides[]' config.json); do
|
||||
COMPOSE_FILES="$COMPOSE_FILES -f $f"
|
||||
done
|
||||
docker compose --env-file .devcontainer/.env $COMPOSE_FILES up -d --build
|
||||
|
||||
+2
-2
@@ -17,14 +17,14 @@ Install Docker with the Compose plugin: [docs.docker.com/get-docker](https://doc
|
||||
curl -fsSL https://oh.mifune.dev/install.sh | bash
|
||||
```
|
||||
|
||||
The installer clones into `~/openharness`, prompts to share your host
|
||||
The installer clones into `~/.openharness`, prompts to share your host
|
||||
`gh` token, writes `.devcontainer/.env` with safe defaults, and brings
|
||||
the sandbox up via `docker compose`.
|
||||
|
||||
## Enter the sandbox
|
||||
|
||||
```bash
|
||||
cd ~/openharness
|
||||
cd ~/.openharness
|
||||
make shell
|
||||
```
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ docker compose -f .devcontainer/docker-compose.yml up -d --build
|
||||
|
||||
What happens:
|
||||
|
||||
1. Docker reads `.devcontainer/docker-compose.yml` plus any overlays (see `.openharness/config.json` for active overlays).
|
||||
1. Docker reads `.devcontainer/docker-compose.yml` plus any overlays (see `config.json` at the repo root for active overlays).
|
||||
2. The image is built or rebuilt as needed.
|
||||
3. The container starts in detached mode.
|
||||
4. The healthcheck eventually flips status to `healthy`.
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ overlays=""
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
overlays=$(jq -r \
|
||||
'.composeOverrides[]? | sub("^\\.devcontainer/docker-compose\\."; "") | sub("\\.yml$"; "")' \
|
||||
"${HOME}/harness/.openharness/config.json" 2>/dev/null \
|
||||
"${HOME}/harness/config.json" 2>/dev/null \
|
||||
| paste -sd, -)
|
||||
fi
|
||||
[ -z "$overlays" ] && overlays="(none)"
|
||||
|
||||
+70
-16
@@ -92,7 +92,7 @@ Usage:
|
||||
curl -fsSL https://oh.mifune.dev/install.sh | bash [-s -- <flags>]
|
||||
./scripts/install.sh [<flags>]
|
||||
|
||||
Clones (or pulls) the repo into ~/openharness, prepares host auth dirs,
|
||||
Clones (or pulls) the repo into ~/.openharness, prepares host auth dirs,
|
||||
and brings up the sandbox via 'docker compose'. Per SPEC v0.7, docker
|
||||
compose is the canonical substrate; the legacy 'oh' CLI is removed.
|
||||
|
||||
@@ -175,18 +175,60 @@ if [ -n "$REPO_CANDIDATE" ] && [ -f "$REPO_CANDIDATE/.devcontainer/docker-compos
|
||||
REPO_DIR="$REPO_CANDIDATE"
|
||||
ok "Using local repo: $REPO_DIR"
|
||||
else
|
||||
REPO_DIR="$HOME/openharness"
|
||||
# One-time migration: legacy curl-pipe installs cloned to ~/.openharness,
|
||||
# which collides visually with the in-repo `.openharness/` config dir.
|
||||
# If the new path is absent and the old one is a clone, move it.
|
||||
LEGACY_REPO="$HOME/.openharness"
|
||||
if [ ! -d "$REPO_DIR" ] && [ -d "$LEGACY_REPO/.git" ]; then
|
||||
mv "$LEGACY_REPO" "$REPO_DIR"
|
||||
ok "Migrated $LEGACY_REPO → $REPO_DIR"
|
||||
# Install target is ~/.openharness (hidden dir, no collision with repo subdirs).
|
||||
# OLD_REPO is the post-#200 path that was briefly the install target.
|
||||
OLD_REPO="$HOME/openharness"
|
||||
REPO_DIR="$HOME/.openharness"
|
||||
|
||||
# ── Population classification + migration ─────────────────────────────
|
||||
__HAS_OLD=0; __HAS_NEW=0
|
||||
[ -d "$OLD_REPO/.git" ] && __HAS_OLD=1
|
||||
if [ -d "$REPO_DIR" ] && [ ! -d "$REPO_DIR/.git" ]; then
|
||||
die "~/.openharness exists but is not a git clone. Inspect and remove it, then re-run."
|
||||
fi
|
||||
unset LEGACY_REPO
|
||||
[ -d "$REPO_DIR/.git" ] && __HAS_NEW=1
|
||||
|
||||
if [ "$__HAS_OLD" = "1" ] && [ "$__HAS_NEW" = "1" ]; then
|
||||
# ── B+C collision: both paths exist ───────────────────────────────────
|
||||
__OLD_DIRTY=0; __NEW_DIRTY=0
|
||||
git -C "$OLD_REPO" diff --quiet 2>/dev/null && git -C "$OLD_REPO" diff --cached --quiet 2>/dev/null || __OLD_DIRTY=1
|
||||
git -C "$REPO_DIR" diff --quiet 2>/dev/null && git -C "$REPO_DIR" diff --cached --quiet 2>/dev/null || __NEW_DIRTY=1
|
||||
if [ "$__OLD_DIRTY" = "1" ] && [ "$__NEW_DIRTY" = "0" ]; then
|
||||
# OLD has changes; keep OLD as active (rename to new), archive NEW
|
||||
__ARCHIVE="${REPO_DIR}.legacy.$(date +%Y%m%d%H%M%S)"
|
||||
mv "$REPO_DIR" "$__ARCHIVE"
|
||||
warn "Archived $REPO_DIR → $__ARCHIVE"
|
||||
git -C "$OLD_REPO" stash push -u -m "install.sh: pre-rename autostash" 2>/dev/null || true
|
||||
mv "$OLD_REPO" "$REPO_DIR"
|
||||
ok "Migrated $OLD_REPO → $REPO_DIR (had local changes — autostashed)"
|
||||
elif [ "$__NEW_DIRTY" = "1" ] && [ "$__OLD_DIRTY" = "0" ]; then
|
||||
# NEW has changes; keep NEW, archive OLD
|
||||
__ARCHIVE="${OLD_REPO}.legacy.$(date +%Y%m%d%H%M%S)"
|
||||
mv "$OLD_REPO" "$__ARCHIVE"
|
||||
warn "Archived $OLD_REPO → $__ARCHIVE"
|
||||
ok "Keeping $REPO_DIR (had local changes)"
|
||||
else
|
||||
# Neither (or both) dirty: prefer OLD_REPO (post-#200, more recent install)
|
||||
__ARCHIVE="${REPO_DIR}.legacy.$(date +%Y%m%d%H%M%S)"
|
||||
mv "$REPO_DIR" "$__ARCHIVE"
|
||||
warn "Archived $REPO_DIR → $__ARCHIVE"
|
||||
git -C "$OLD_REPO" stash push -u -m "install.sh: pre-rename autostash" 2>/dev/null || true
|
||||
mv "$OLD_REPO" "$REPO_DIR"
|
||||
ok "Migrated $OLD_REPO → $REPO_DIR"
|
||||
fi
|
||||
unset __OLD_DIRTY __NEW_DIRTY __ARCHIVE
|
||||
elif [ "$__HAS_OLD" = "1" ] && [ "$__HAS_NEW" = "0" ]; then
|
||||
# ── Population B: ~/openharness only — rename to ~/.openharness ────────
|
||||
git -C "$OLD_REPO" stash push -u -m "install.sh: pre-rename autostash" 2>/dev/null || true
|
||||
mv "$OLD_REPO" "$REPO_DIR"
|
||||
ok "Migrated $OLD_REPO → $REPO_DIR"
|
||||
fi
|
||||
# Population C (~/.openharness only) and A (neither) fall through to the
|
||||
# pull / clone logic below.
|
||||
unset __HAS_OLD __HAS_NEW OLD_REPO
|
||||
|
||||
if [ -d "$REPO_DIR/.git" ]; then
|
||||
# Gate pull on clean working tree — don't abort on local edits.
|
||||
# ── Pull (clean tree only) ─────────────────────────────────────────────
|
||||
if git -C "$REPO_DIR" diff --quiet 2>/dev/null && git -C "$REPO_DIR" diff --cached --quiet 2>/dev/null; then
|
||||
printf " Repository exists — pulling latest changes...\n"
|
||||
git -C "$REPO_DIR" pull --ff-only
|
||||
@@ -195,6 +237,7 @@ else
|
||||
warn "Local changes detected in $REPO_DIR — skipping git pull. Stash or commit them, then re-run if you want the latest main."
|
||||
fi
|
||||
else
|
||||
# ── Population A: fresh clone ──────────────────────────────────────────
|
||||
if [ -n "${OH_INSTALL_REF:-}" ]; then
|
||||
git clone --branch "$OH_INSTALL_REF" https://github.com/ryaneggz/open-harness.git "$REPO_DIR"
|
||||
ok "Repository cloned at ref '$OH_INSTALL_REF': $REPO_DIR"
|
||||
@@ -203,6 +246,18 @@ else
|
||||
ok "Repository cloned: $REPO_DIR"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Seed config.json from example if missing ──────────────────────────
|
||||
if [ ! -f "$REPO_DIR/config.json" ] && [ -f "$REPO_DIR/config.example.json" ]; then
|
||||
cp "$REPO_DIR/config.example.json" "$REPO_DIR/config.json"
|
||||
ok "Seeded config.json from config.example.json"
|
||||
fi
|
||||
|
||||
# ── Shell CWD notice (for B migration) ────────────────────────────────
|
||||
# If the user's shell was open in ~/openharness, that path no longer exists.
|
||||
printf "\n"
|
||||
warn "If your current shell is still in ~/openharness, run: cd ~/.openharness"
|
||||
printf "\n"
|
||||
fi
|
||||
|
||||
cd "$REPO_DIR"
|
||||
@@ -323,7 +378,7 @@ fi
|
||||
|
||||
__HOST_UID="$(id -u)"
|
||||
if [ "$__HOST_UID" != "1000" ]; then
|
||||
__OH_CONFIG="$REPO_DIR/.openharness/config.json"
|
||||
__OH_CONFIG="$REPO_DIR/config.json"
|
||||
if command -v jq >/dev/null 2>&1 && [ -f "$__OH_CONFIG" ]; then
|
||||
# Filter *-host.yml entries out of composeOverrides. Mode-0600
|
||||
# credential files in host agent state dirs can't be read by the
|
||||
@@ -337,7 +392,6 @@ if [ "$__HOST_UID" != "1000" ]; then
|
||||
mv "$__OH_TMP" "$__OH_CONFIG"
|
||||
ok "Host UID $__HOST_UID ≠ 1000 — disabled host-bind overlays in $__OH_CONFIG"
|
||||
ok "Auth will live in named volumes; first run of each agent inside the sandbox will authenticate"
|
||||
warn "$__OH_CONFIG now has a local diff — \`git pull\` in $REPO_DIR will be skipped until you commit or revert it"
|
||||
else
|
||||
rm -f "$__OH_TMP"
|
||||
ok "Host UID $__HOST_UID ≠ 1000 — host-bind overlays already disabled"
|
||||
@@ -356,7 +410,7 @@ if [ "$__HOST_UID" != "1000" ]; then
|
||||
warn "Credential files in host agent state dirs may be mode 0600;"
|
||||
warn "the sandbox WILL NOT be able to read them despite the bind-mount."
|
||||
warn ""
|
||||
warn "Install jq, OR edit $REPO_DIR/.openharness/config.json"
|
||||
warn "Install jq, OR edit $REPO_DIR/config.json"
|
||||
warn "and remove these overlays from composeOverrides (any that you"
|
||||
warn "have enabled — deepagents-host.yml is opt-in by default):"
|
||||
warn " .devcontainer/docker-compose.claude-host.yml"
|
||||
@@ -376,10 +430,10 @@ unset __HOST_UID
|
||||
banner "Building and starting sandbox"
|
||||
printf "${CYAN}==> Building image — ~10 min on cold cache, ~30s on warm cache. Compose output below.${NC}\n"
|
||||
COMPOSE_FILES="-f .devcontainer/docker-compose.yml"
|
||||
if command -v jq >/dev/null 2>&1 && [ -f "$REPO_DIR/.openharness/config.json" ]; then
|
||||
if command -v jq >/dev/null 2>&1 && [ -f "$REPO_DIR/config.json" ]; then
|
||||
while IFS= read -r override; do
|
||||
[ -n "$override" ] && COMPOSE_FILES="$COMPOSE_FILES -f $override"
|
||||
done < <(jq -r '.composeOverrides[]?' "$REPO_DIR/.openharness/config.json")
|
||||
done < <(jq -r '.composeOverrides[]?' "$REPO_DIR/config.json")
|
||||
fi
|
||||
docker compose $COMPOSE_FILES up -d --build
|
||||
ok "Sandbox '$SANDBOX_NAME' started"
|
||||
|
||||
Reference in new issue
Block a user