Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
43 changes: 39 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,22 @@ 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.
with:
persist-credentials: false

- 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 +40,32 @@ jobs:

- name: Test
run: npm test

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

- 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"
75 changes: 75 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Release

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

permissions:
contents: read

jobs:
checks:
name: typecheck, test & tag/version match
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: "22"
- 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.

- name: Tag must match package.json version
run: |
tag="${GITHUB_REF_NAME#v}"
pkg="$(node -p "require('./package.json').version")"
if [ "$tag" != "$pkg" ]; then
echo "::error::tag v$tag does not match package.json version $pkg — refusing to publish a mismatched release."
exit 1
fi

pack-smoke:
name: npm pack smoke test
needs: checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: "22"
- run: npm ci

- name: Pack tarball and run it as an end user would
run: |
npm pack --pack-destination "$RUNNER_TEMP"
mkdir -p "$RUNNER_TEMP/consumer"
cd "$RUNNER_TEMP/consumer"
npm init -y >/dev/null
npm install "$RUNNER_TEMP"/liohtml-agentbox-*.tgz
npx agentbox --help
npx agentbox doctor 2>&1 | grep -q "agentbox doctor"

publish:
name: npm publish (provenance)
needs: pack-smoke
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: "22"
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.
121 changes: 121 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# 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 (review follow-up)
- The container no longer runs with `--rm`: a container that exits early
(entry-script crash) now stays around for `docker logs` to inspect instead
of vanishing immediately. `up` removes a stale stopped container before
starting fresh; `down` still removes it explicitly; `waitForSession` now
also gives up early if the container itself exits instead of polling out
the full timeout.
- Reattaching now reports the **running container's actual** `--yolo` state
(via a container label) instead of echoing back the current invocation's
flag, which is ignored on reattach and could previously claim the wrong
thing in either direction.
- A value flag given with no value (e.g. a trailing `--gpus`) is now a hard
error instead of silently being dropped.
- `validateConfig` (and the "workspace must exist" check) now only gate
`up`/`rebuild`; `shell`/`down` stay usable even with a currently-invalid
`MEM`/`CPUS`/workspace. `doctor` now reports every `validateConfig`
problem explicitly instead of a separate, narrower ad hoc port check.
- `~/.claude.json` symlinked to a directory is now a clear error (previously
it silently passed through and Docker mounted a directory).
- The generated entry script's temp directory is verified (owner uid + no
group/other permissions) after creation, closing a race where another
local user could pre-create it to intercept the mounted script.
- Launcher: dropped the shell-based global-`tsx`-on-PATH fallback (it forwarded
raw argv through a shell, letting metacharacters be interpreted on
Windows); `tsx` is a runtime dependency now, so a missing local install is
a clear, actionable error instead.
- CI/release workflows: `persist-credentials: false` on all checkouts; the
release workflow now verifies the pushed tag matches `package.json`'s
version, runs the `npm pack` tarball smoke test as a publish gate, and
disables dependency caching (a compromised cache must never influence what
gets published).
- Dockerfile: `CLAUDE_CODE_VERSION`/`TSX_VERSION` build-arg defaults are now
pinned exact versions instead of `latest`, so a plain rebuild is
reproducible; override the args for an intentional upgrade.

### 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. The box itself stays disposable
(`down` / `up`'s stale-container cleanup); 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. Credentials are never
baked into the image, but note they *are* readable at runtime by anything
running inside the container, same as the existing Claude credential mount.
- **`--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`.
42 changes: 42 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# 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 via a GitHub issue (for non-sensitive matters) or, for anything you'd
rather not post publicly, by contacting the maintainer directly:
[@Liohtml](https://github.com/Liohtml). All complaints will be reviewed and
investigated promptly and fairly.

(Security vulnerabilities have their own private channel — see
[SECURITY.md](SECURITY.md); please don't use it for conduct reports.)

For the full text including enforcement guidelines, see the
[Contributor Covenant v2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
Loading
Loading