Skip to content
Open
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
60 changes: 60 additions & 0 deletions docker/dev/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# syntax=docker/dockerfile:1.10.0
#
# Interactive dev container for agentOS. Unlike docker/build/*.Dockerfile, this
# image contains no repo source: the working tree is bind-mounted at /build and
# every build output lands in named volumes, so host edits are visible
# immediately and rebuilds stay incremental.
#
# The deno sysroot / LTO dance from the release build is deliberately omitted —
# dev binaries only need to run in this container, not on old glibc.
FROM ubuntu:24.04

ARG RUST_TOOLCHAIN=1.91.1
ARG NODE_MAJOR=22
ARG PNPM_VERSION=10.13.1
ARG JUST_VERSION=1.36.0

ENV DEBIAN_FRONTEND=noninteractive \
CARGO_HOME=/usr/local/cargo \
RUSTUP_HOME=/usr/local/rustup \
PNPM_HOME=/usr/local/pnpm \
COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
PATH=/usr/local/cargo/bin:/usr/local/pnpm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
ca-certificates curl git make cmake ninja-build build-essential \
pkg-config libssl-dev xz-utils unzip jq python3 python3-venv \
clang lld binaryen file procps less vim-tiny; \
rm -rf /var/lib/apt/lists/*

# Node + pnpm
RUN set -eux; \
curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash -; \
apt-get install -y --no-install-recommends nodejs; \
corepack enable; \
corepack prepare "pnpm@${PNPM_VERSION}" --activate; \
rm -rf /var/lib/apt/lists/*

# Rust: host target for the sidecar, wasm32-wasip1 + rust-src for the WASM
# command toolchain (scripts/patch-std.sh rebuilds std from source).
RUN set -eux; \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
| sh -s -- -y --default-toolchain "${RUST_TOOLCHAIN}" --profile minimal --no-modify-path; \
rustup target add wasm32-wasip1; \
rustup component add rust-src clippy rustfmt

RUN set -eux; \
arch="$(uname -m)"; \
case "$arch" in \
x86_64) just_arch=x86_64-unknown-linux-musl ;; \
aarch64) just_arch=aarch64-unknown-linux-musl ;; \
*) echo "unsupported arch: $arch" >&2; exit 1 ;; \
esac; \
curl -fsSL "https://github.com/casey/just/releases/download/${JUST_VERSION}/just-${JUST_VERSION}-${just_arch}.tar.gz" \
| tar -xz -C /usr/local/bin just

WORKDIR /build

CMD ["sleep", "infinity"]
58 changes: 58 additions & 0 deletions docker/dev/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Dev container

agentOS targets native Linux execution, so on macOS the engine, sidecar, and VMs
run in this container while the working tree stays on the host. The repo is
bind-mounted at `/build`; every build output lives in a named volume, so host
edits are visible immediately and no darwin-native binary is ever dragged into
Linux.

This is a dev environment. For release artifacts see `docker/build/`.

## First run

```bash
just dev-up # build the image, start the container
just dev-bootstrap # pnpm install + WASM command set + sidecar (slow, one time)
```

`dev-bootstrap` builds the full default WASM tool set from source, which is the
long pole. It only needs re-running when the toolchain or software sources
change.

## Daily use

```bash
just dev-terminal-example # engine on :6420, Vite on :5173
just dev-shell # interactive shell in the container
just dev-exec 'cargo check --workspace'
just dev-build-tabs # rebuild the inspector custom-tab bundle
just dev-down # stop
```

Open <http://localhost:5173> for the example UI. The hosted Rivet dashboard runs
in the host browser and points at <http://localhost:6420>, which serves the actor
gateway, `/inspector/*`, and custom-tab assets.

## Layout

| Path | Backing | Why |
| --- | --- | --- |
| `/build` | bind mount | host edits visible immediately |
| `/build/node_modules` | volume | host tree holds darwin binaries |
| `/build/target` | volume | Linux cargo output, survives recreation |
| `/build/toolchain/target`, `toolchain/c/*` | volumes | generated WASM/sysroot trees |
| cargo registry/git, pnpm store | volumes | warm caches across rebuilds |

## Caveats

- **Run `pnpm install` inside the container, not on the host.** Both write
package-level `node_modules` symlinks into the bind mount, and their
platform-specific optional dependencies differ. If you need host typechecks,
re-run `pnpm install` on the host afterwards.
- DNS is pinned to 1.1.1.1/8.8.8.8. Docker Desktop's embedded resolver
intermittently stops answering, which surfaces as an `EAI_AGAIN` storm during
install.
- `AGENTOS_SIDECAR_BIN` points at `/build/target/debug/agentos-sidecar`. Rebuild
it with `just dev-exec 'cargo build -p agentos-sidecar'`.
- The container publishes 6420 and 5173. Free them on the host first if another
engine is already bound.
62 changes: 62 additions & 0 deletions docker/dev/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: agentos-dev

services:
dev:
build:
context: ../..
dockerfile: docker/dev/Dockerfile
working_dir: /build
# Keep the container resident; drive it with `just dev-shell` / `docker compose exec`.
command: ["sleep", "infinity"]
stdin_open: true
tty: true
ports:
# RivetKit engine: actor gateway + /inspector/* + custom-tab assets. The
# hosted dashboard runs in the host browser and points at this port.
- "6420:6420"
# Vite dev server for examples/browser-terminal.
- "5173:5173"
# Docker Desktop's embedded resolver (192.168.65.7) intermittently stops
# answering, which surfaces as EAI_AGAIN storms during `pnpm install`.
dns:
- 1.1.1.1
- 8.8.8.8
environment:
# Unambiguous sidecar resolution; `just dev-bootstrap` builds this path.
AGENTOS_SIDECAR_BIN: /build/target/debug/agentos-sidecar
RUST_BACKTRACE: "1"
# Vite/RivetKit must listen on all interfaces to be reachable from the host.
HOST: 0.0.0.0
# RivetKit binds the engine it spawns to loopback, which a published port
# cannot reach. This is the supported override for that bind address
# (rivetkit's `engineHost`); setting RIVET__GUARD__HOST does not work,
# because RivetKit passes its own value to the engine process.
RIVET_RUN_ENGINE_HOST: 0.0.0.0
volumes:
- ../..:/build:cached

# Host node_modules are darwin-native — mask every bind-mounted tree that
# holds platform binaries or build output with a container-owned volume.
- node_modules:/build/node_modules
- cargo_target:/build/target
- toolchain_target:/build/toolchain/target

# Only `toolchain/c/vendor` is safe to mount: it holds the large wasi-sdk
# and llvm-project downloads and nothing deletes it. `sysroot`, `libs`,
# `.cache`, and `toolchain/vendor` are `rm -rf`'d wholesale by the build,
# which fails with EBUSY on a mount point — leave them on the bind mount.
- toolchain_c_vendor:/build/toolchain/c/vendor

# Caches outside the working tree.
- cargo_registry:/usr/local/cargo/registry
- cargo_git:/usr/local/cargo/git
- pnpm_store:/root/.local/share/pnpm/store

volumes:
node_modules:
cargo_target:
cargo_registry:
cargo_git:
pnpm_store:
toolchain_target:
toolchain_c_vendor:
9 changes: 5 additions & 4 deletions examples/browser-terminal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,11 @@ Override the web→server endpoint with `VITE_AGENTOS_ENDPOINT` (default
- Software: `@agentos-software/common` (provides `sh` + coreutils) plus `git`,
`curl`, `ripgrep`, `jq`, and `sqlite3`. Agent OS has no vim/editor package, so
there is no in-VM editor.
- The shipped actor has no `listShells` action and keeps no server-side
scrollback, so reconnect re-adopts saved shell ids and resumes **live** output
only (history from before the reload is not replayed). Stale ids (VM recreated)
are dropped after a liveness probe.
- This client predates the actor's `listShells` and `shellSnapshot` actions: it
re-adopts saved shell ids on reconnect and resumes **live** output only, so
history from before the reload is not replayed. Stale ids (VM recreated) are
dropped after a liveness probe. The dashboard Terminal tab uses the
server-owned list and server-rendered repaint instead.
- The VM shell is line-buffered (it only echoes a line on Enter), so the client
does **local echo + line editing** (printable chars, Backspace, Ctrl-C) and
suppresses the shell's own echo of the submitted line to avoid double display.
62 changes: 62 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,65 @@ test-bounded cmd='pnpm test':

test-risky-probe *tests:
./.agent/scripts/run-risky-test-probe.sh "$@"

# --- Dev container (docker/dev) ---
#
# agentOS runs Linux-only, so on macOS the engine, sidecar, and VMs live in a
# container while the working tree stays on the host. The repo is bind-mounted
# at /build; node_modules, cargo target, and the toolchain build trees are
# container-owned volumes, so host edits are visible instantly without dragging
# darwin-native binaries into Linux.
#
# First run: `just dev-up && just dev-bootstrap` (the bootstrap is slow — it
# builds the WASM command set and the sidecar from source). After that,
# `just dev-terminal-example` and edit TypeScript with hot reload.

dev-compose := "docker compose -f docker/dev/compose.yaml"

# Build the image and start the container.
dev-up:
{{dev-compose}} up -d --build

dev-down:
{{dev-compose}} down

# Drop into a shell inside the dev container.
dev-shell:
{{dev-compose}} exec dev bash

# Run any command inside the dev container: `just dev-exec 'cargo check --workspace'`
dev-exec cmd:
{{dev-compose}} exec dev bash -lc "{{cmd}}"

# One-time (slow) build of everything the engine needs from source.
dev-bootstrap:
{{dev-compose}} exec dev bash -lc '\
set -euo pipefail; \
echo "==> pnpm install"; \
pnpm install --frozen-lockfile; \
echo "==> WASM command set (long)"; \
just toolchain-build; \
just toolchain-copy-commands; \
echo "==> software packages"; \
pnpm --filter "@agentos-software/*" \
--filter "!@agentos-software/codex" \
--filter "!@agentos-software/codex-cli" \
--filter "!@agentos-software/everything" build; \
echo "==> agentos-sidecar (debug)"; \
cargo build -p agentos-sidecar; \
echo "==> workspace TypeScript + inspector tab bundle"; \
pnpm --filter @rivet-dev/agentos-core --filter @rivet-dev/agentos-runtime-core build; \
pnpm --filter @rivet-dev/agentos build; \
echo "==> done"'

# Serve examples/browser-terminal: engine on :6420, Vite on :5173.
dev-terminal-example:
{{dev-compose}} exec dev bash -lc '\
cd examples/browser-terminal && \
pnpm concurrently -k -n server,web -c blue,magenta \
"tsx server.ts" \
"vite --host 0.0.0.0"'

# Rebuild the inspector custom-tab bundle (run after editing src/inspector-tabs).
dev-build-tabs:
{{dev-compose}} exec dev bash -lc 'pnpm --filter @rivet-dev/agentos build:tabs'
3 changes: 2 additions & 1 deletion packages/agentos/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"@agentos-software/common": "workspace:*",
"@rivet-dev/agentos-core": "workspace:*",
"@rivetkit/react": "catalog:rivetkit",
"ghostty-web": "^0.4.0",
"rivetkit": "catalog:rivetkit",
"zod": "^4.1.11"
},
Expand All @@ -85,9 +86,9 @@
},
"devDependencies": {
"@agentos-software/coreutils": "workspace:*",
"@rivet-dev/agentos-test-harness": "workspace:*",
"@radix-ui/react-collapsible": "^1.1.2",
"@radix-ui/react-scroll-area": "^1.2.2",
"@rivet-dev/agentos-test-harness": "workspace:*",
"@tanstack/react-query": "^5.87.1",
"@types/node": "^22.19.15",
"@types/react": "^19.0.0",
Expand Down
Loading
Loading