Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Security vulnerability
url: https://github.com/Liohtml/agentbox/security/advisories/new
about: Please report security issues privately via GitHub Security Advisories.
26 changes: 26 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# What & why

<!-- What does this PR change, and why? -->

## Scope

<!-- Which module does this touch? Keep PRs scoped to one module/concern
where possible (see CONTRIBUTING.md → Module ownership). -->

- [ ] `src/cli/` — arg parsing, help, config, command router
- [ ] `src/docker/` — image build, container run/exec, mounts
- [ ] `src/session/` — tmux entry script, TTY sizing
- [ ] `assets/` — Dockerfile / Observer
- [ ] `src/types.ts` / `CONTRACT.md` — contract change (explain below!)
- [ ] Docs / CI / meta

## Checklist

- [ ] `npm run typecheck` passes
- [ ] `npm test` passes
- [ ] No build step, no new runtime dependencies (see CONTRIBUTING.md)
- [ ] Contract friction with `CONTRACT.md` / `src/types.ts` is described below (or n/a)

## Notes

<!-- What works, what's stubbed, any contract friction. -->
13 changes: 13 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
groups:
dev-dependencies:
dependency-type: development
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
39 changes: 35 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: CI

on:
push:
branches: ["**"]
branches: [main]
pull_request:

permissions:
Expand All @@ -14,16 +14,20 @@ concurrency:

jobs:
build:
name: typecheck & test (Node 22)
name: typecheck & test (Node ${{ matrix.node }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: [22, 24]
steps:
- name: Checkout
uses: actions/checkout@v4
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Setup Node 22
- name: Setup Node ${{ matrix.node }}
uses: actions/setup-node@v4
with:
node-version: "22"
node-version: ${{ matrix.node }}
cache: npm

- name: Install dependencies
Expand All @@ -34,3 +38,30 @@ jobs:

- name: Test
run: npm test

pack-smoke:
name: npm pack smoke test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node 22
uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm

- name: Pack tarball
run: npm pack --pack-destination "$RUNNER_TEMP"

- name: Install tarball into a fresh project and run the CLI
run: |
mkdir -p "$RUNNER_TEMP/consumer"
cd "$RUNNER_TEMP/consumer"
npm init -y >/dev/null
npm install "$RUNNER_TEMP"/liohtml-agentbox-*.tgz
# The installed bin must run without any repo-local dev setup.
npx agentbox --help
npx agentbox doctor || true # doctor exits non-zero without Docker; it must still *run*
npx agentbox doctor 2>&1 | grep -q "agentbox doctor"
41 changes: 41 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Release

on:
push:
tags: ["v*"]
Comment thread
coderabbitai[bot] marked this conversation as resolved.

permissions:
contents: read

jobs:
checks:
name: typecheck & test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
- run: npm ci
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- run: npm run typecheck
- run: npm test
Comment thread
coderabbitai[bot] marked this conversation as resolved.

publish:
name: npm publish (provenance)
needs: checks
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
registry-url: "https://registry.npmjs.org"
- run: npm ci
- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
84 changes: 84 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Changelog

All notable changes to this project are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project
adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

### Fixed
- **Detaching no longer destroys the sandbox.** The container now runs
detached with the entry script as PID 1 and your terminal attaches via
`docker exec`, so `Ctrl+b d` behaves as documented (box keeps running).
Previously the tmux attach client was PID 1: detach killed every agent and
`--rm` deleted the container.
- **Teammates appear as tmux panes again.** The lead is launched with
`--teammate-mode tmux`; since Claude Code v2.1.179 the upstream default is
`in-process`, which silently disabled agentbox's split-pane layout.
- Observer: project/session names are HTML-escaped before rendering
(stored-XSS guard), the server handles bind errors (`EADDRINUSE`) with a
readable message, the observer tmux window keeps crash output visible
(`remain-on-exit`), and dead code was removed.
- CLI: invalid `--port`/`--memory`/`--cpus` values, unknown flags, stray
positionals, and `:` in mount paths fail fast with clear errors instead of
surfacing as cryptic Docker failures after a minutes-long build; a
`--workspace` typo no longer silently creates and mounts an empty
directory.
- `rebuild` refuses to run while the box is up (it used to rebuild the image
and then silently reattach to the old container); reattaching with tuning
flags now warns that they are ignored; stale stopped containers are removed
before `docker run` instead of causing a name-conflict error.
- `down` no longer reports success when `docker rm -f` actually failed.
- `~/.claude.json` edge cases: a directory there is now a clear error (was: a
broken mount), and symlinked configs (dotfile managers) are followed
correctly.
- The generated entry script lives in a per-user `0700` temp subdirectory
instead of a fixed world-shared `/tmp` path (tamper/TOCTOU + collision).
- Launcher: local `tsx` is invoked via `node` directly — no shell, so paths
with spaces work and `cmd.exe` metacharacters can't inject on Windows.
- `doctor` warns when the host uid differs from the container's `agent`
(1000), which breaks writes to the bind mounts.

### Changed
- Dockerfile: `@anthropic-ai/claude-code` and `tsx` versions are overridable
via build args (`CLAUDE_CODE_VERSION`, `TSX_VERSION`) for reproducible
builds.
- Package renamed to the scoped **`@liohtml/agentbox`** for npm publishing
(the bare name `agentbox` is an npm security placeholder). The installed
command is still `agentbox`.
- `tsx` moved from devDependencies to dependencies so the published package
runs via `npx`/global install without extra setup.
- CI: Node 22 + 24 matrix, plus an `npm pack` smoke test that installs the
tarball and runs the CLI as an end user would.

### Added
- **`--yolo` flag**: the Claude lead runs with
`--dangerously-skip-permissions` (teammates inherit it). Opt-in per
invocation, never the default — see README → Security & trust.
- **Persistent tool-cache volumes**: named Docker volumes for `~/.npm`,
`~/.cargo/registry` and pip's cache. Boxes stay `--rm`-disposable; only
downloaded packages survive.
- **Git identity + `gh` auth forwarding**: `~/.gitconfig` and `~/.config/gh`
are bind-mounted read-only when present, and the `gh` CLI is installed in
the image — agents can commit and open PRs as you, without any credential
ever entering the image.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
- **`--gpus` passthrough** to `docker run --gpus` (e.g. `all`, `device=0`).
- `doctor` warns when the local image architecture doesn't match the host
(emulated images are painfully slow under qemu).
- `SECURITY.md`, `CODE_OF_CONDUCT.md`, PR template, issue-template config,
Dependabot config, `RELEASING.md`, `ROADMAP.md`, this changelog.
- Tag-triggered release workflow with npm provenance.

### Removed
- `PUBLISH.md` (one-time bootstrap doc) — replaced by `RELEASING.md`.

## [0.1.0] — 2026-07-29

### Added
- Initial release: `agentbox up | shell | rebuild | doctor | down`.
- Docker image (Ubuntu 24.04 + Node 22 + Python 3 + Rust + Claude Code + tsx),
non-root `agent` user, no baked-in credentials.
- Host bind mounts for workspace + Claude auth reuse (`~/.claude`,
`~/.claude.json`).
- tmux session with Claude lead (Agent Teams) + Observer dashboard window.
- Zero-dependency Observer web server on `127.0.0.1:4317`.
38 changes: 38 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Code of Conduct

This project follows the [Contributor Covenant v2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).

## Our pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic
status, nationality, personal appearance, race, caste, color, religion, or
sexual identity and orientation.

## Our standards

Examples of behavior that contributes to a positive environment:

- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes

Examples of unacceptable behavior:

- Sexualized language or imagery, and sexual attention or advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information without their explicit permission

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the maintainers via a GitHub issue (for non-sensitive matters) or
privately via [GitHub Security Advisories](https://github.com/Liohtml/agentbox/security/advisories/new).
All complaints will be reviewed and investigated promptly and fairly.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

For the full text including enforcement guidelines, see the
[Contributor Covenant v2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
21 changes: 19 additions & 2 deletions CONTRACT.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ Defaults (`DEFAULTS` in types):
- `claudeDir` default = `~/.claude`, `claudeJson` default = `~/.claude.json`
- `dockerfile` = `assets/Dockerfile`, `buildContext` = `assets/`
- `observerAsset` = `assets/observer.mjs`
- `entryScriptPath` = a host temp path (e.g. `os.tmpdir()/agentbox-entry.sh`)
- `entryScriptPath` = a host temp path in a per-user 0700 subdir
(`os.tmpdir()/agentbox-<uid>/entry.sh`) — never a fixed path in the shared
temp dir (tamper/TOCTOU on multi-user hosts)

---

Expand Down Expand Up @@ -99,6 +101,13 @@ export const session: SessionModule; // see src/types.ts
| `config.claudeJson` (`~/.claude.json`) | `/home/agent/.claude.json` | **touch first** so Docker mounts a file, not a dir |
| `config.entryScriptPath` | `/agentbox/entry.sh` | run via `bash` (no execute-bit reliance) |
| `config.observerAsset` | `/agentbox/observer.mjs` | the dashboard |
| `config.gitconfigPath` *(if present)* | `/home/agent/.gitconfig` | **read-only** — git identity reuse |
| `config.ghConfigDir` *(if present)* | `/home/agent/.config/gh` | **read-only** — gh CLI auth reuse |

Named cache volumes (`CACHE_VOLUMES` in types.ts): `agentbox-npm-cache`,
`agentbox-cargo-registry`, `agentbox-pip-cache` → the box stays `--rm`,
only tool downloads persist. Host identity mounts are read-only, existence-
gated, and NEVER copied into the image.

- Publish `-p <observerPort>:<observerPort>`.
- Apply `--memory <config.memory>` and `--cpus <config.cpus>`.
Expand All @@ -117,8 +126,16 @@ export const session: SessionModule; // see src/types.ts
`main`.
4. **Launch the entry script via `bash /agentbox/entry.sh`** so a missing
execute bit never blocks startup.
5. Set `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` in the `main` window.
5. Set `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` in the `main` window, and
launch the lead with `--teammate-mode tmux`: since Claude Code v2.1.179
the default teammateMode is `in-process`, which silently disables the
split-pane layout.
6. Read the real TTY size via `stty size` so tmux isn't stuck at 80×24.
7. **The tmux attach client must NEVER be container PID 1.** The container
runs detached (`docker run -d`); the entry script sets the session up and
idles while it exists, and clients attach via `docker exec … tmux attach`.
Otherwise `C-b d` (detach) exits PID 1, kills every agent, and `--rm`
deletes the container.

---

Expand Down
Loading
Loading